Response class for Conflict
responses (status code 409).
The request could not be processed because of conflict in the current state of the resource.
References:
Response class for HTTP Version Not Supported
responses (status code 505).
The server does not support the HTTP
version used in the request.
References:
Response class for Variant Also Negotiates
responses (status code 506).
Transparent content negotiation for the request results in a circular reference.
References:
Raised when trying to activate a gem, and the gem exists on the system, but not the requested version. Instead of rescuing from this class, make sure to rescue from the superclass Gem::LoadError
to catch all types of load errors.
Raised when there are conflicting gem specs loaded
Raised when a gem dependencies file specifies a ruby version that does not match the current version.
The Version
class processes string versions into comparable values. A version string should normally be a series of numbers separated by periods. Each part (digits separated by periods) is considered its own number, and these are used for sorting. So for instance, 3.10 sorts higher than 3.2 because ten is greater than two.
If any part contains letters (currently only a-z are supported) then that version is considered prerelease. Versions with a prerelease part in the Nth part sort less than versions with N-1 parts. Prerelease parts are sorted alphabetically using the normal Ruby
string sorting rules. If a prerelease part contains both letters and numbers, it will be broken into multiple parts to provide expected sort behavior (1.0.a10 becomes 1.0.a.10, and is greater than 1.0.a9).
Prereleases sort between real releases (newest to oldest):
1.0
1.0.b1
1.0.a.2
0.9
If you want to specify a version restriction that includes both prereleases and regular releases of the 1.x series this is the best way:
s.add_dependency 'example', '>= 1.0.0.a', '< 2.0.0'
Users expect to be able to specify a version constraint that gives them some reasonable expectation that new versions of a library will work with their software if the version constraint is true, and not work with their software if the version constraint is false. In other words, the perfect system will accept all compatible versions of the library and reject all incompatible versions.
Libraries change in 3 ways (well, more than 3, but stay focused here!).
The change may be an implementation detail only and have no effect on the client software.
The change may add new features, but do so in a way that client software written to an earlier version is still compatible.
The change may change the public interface of the library in such a way that old software is no longer compatible.
Some examples are appropriate at this point. Suppose I have a Stack class that supports a push
and a pop
method.
Switch from an array based implementation to a linked-list based implementation.
Provide an automatic (and transparent) backing store for large stacks.
Add a depth
method to return the current depth of the stack.
Add a top
method that returns the current top of stack (without changing the stack).
Change push
so that it returns the item pushed (previously it had no usable return value).
Changes pop
so that it no longer returns a value (you must use top
to get the top of the stack).
Rename the methods to push_item
and pop_item
.
Rational
Versioning Versions shall be represented by three non-negative integers, separated by periods (e.g. 3.1.4). The first integers is the “major” version number, the second integer is the “minor” version number, and the third integer is the “build” number.
A category 1 change (implementation detail) will increment the build number.
A category 2 change (backwards compatible) will increment the minor version number and reset the build number.
A category 3 change (incompatible) will increment the major build number and reset the minor and build numbers.
Any “public” release of a gem should have a different version. Normally that means incrementing the build number. This means a developer can generate builds all day long, but as soon as they make a public release, the version must be updated.
Let’s work through a project lifecycle using our Stack example from above.
Version
0.0.1The initial Stack class is release.
Version
0.0.2Switched to a linked=list implementation because it is cooler.
Version
0.1.0Added a depth
method.
Version
1.0.0Added top
and made pop
return nil (pop
used to return the old top item).
Version
1.1.0push
now returns the value pushed (it used it return nil).
Version
1.1.1Fixed a bug in the linked list implementation.
Version
1.1.2Fixed a bug introduced in the last fix.
Client A needs a stack with basic push/pop capability. They write to the original interface (no top
), so their version constraint looks like:
gem 'stack', '>= 0.0'
Essentially, any version is OK with Client A. An incompatible change to the library will cause them grief, but they are willing to take the chance (we call Client A optimistic).
Client B is just like Client A except for two things: (1) They use the depth
method and (2) they are worried about future incompatibilities, so they write their version constraint like this:
gem 'stack', '~> 0.1'
The depth
method was introduced in version 0.1.0, so that version or anything later is fine, as long as the version stays below version 1.0 where incompatibilities are introduced. We call Client B pessimistic because they are worried about incompatible future changes (it is OK to be pessimistic!).
Version
Catastrophe: From: www.zenspider.com/ruby/2008/10/rubygems-how-to-preventing-catastrophe.html
Let’s say you’re depending on the fnord gem version 2.y.z. If you specify your dependency as “>= 2.0.0” then, you’re good, right? What happens if fnord 3.0 comes out and it isn’t backwards compatible with 2.y.z? Your stuff will break as a result of using “>=”. The better route is to specify your dependency with an “approximate” version specifier (“~>”). They’re a tad confusing, so here is how the dependency specifiers work:
Specification From ... To (exclusive) ">= 3.0" 3.0 ... ∞ "~> 3.0" 3.0 ... 4.0 "~> 3.0.0" 3.0.0 ... 3.1 "~> 3.5" 3.5 ... 4.0 "~> 3.5.0" 3.5.0 ... 3.6 "~> 3" 3.0 ... 4.0
For the last example, single-digit versions are automatically extended with a zero to give a sensible result.
Raised by transcoding methods when a named encoding does not correspond with a known converter.
Utility methods for using the RubyGems API.
The WebauthnListener
class retrieves an OTP after a user successfully WebAuthns with the Gem
host. An instance opens a socket using the TCPServer
instance given and listens for a request from the Gem
host. The request should be a GET request to the root path and contains the OTP code in the form of a query parameter ‘code`. The listener will return the code which will be used as the OTP for API requests.
Types of responses sent by the listener after receiving a request:
- 200 OK: OTP code was successfully retrieved - 204 No Content: If the request was an OPTIONS request - 400 Bad Request: If the request did not contain a query parameter `code` - 404 Not Found: The request was not to the root path - 405 Method Not Allowed: OTP code was not retrieved because the request was not a GET/OPTIONS request
Example usage:
thread = Gem::WebauthnListener.listener_thread("https://rubygems.example", server) thread.join otp = thread[:otp] error = thread[:error]
The WebauthnListener
Response class is used by the WebauthnListener
to create responses to be sent to the Gem
host. It creates a Gem::Net::HTTPResponse instance when initialized and can be converted to the appropriate format to be sent by a socket using ‘to_s`. Gem::Net::HTTPResponse instances cannot be directly sent over a socket.
Types of response classes:
- OkResponse - NoContentResponse - BadRequestResponse - NotFoundResponse - MethodNotAllowedResponse
Example usage:
server = TCPServer.new(0) socket = server.accept response = OkResponse.for("https://rubygems.example") socket.print response.to_s socket.close
The WebauthnPoller
class retrieves an OTP after a user successfully WebAuthns. An instance polls the Gem
host for the OTP code. The polling request (api/v1/webauthn_verification/<webauthn_token>/status.json) is sent to the Gem
host every 5 seconds and will timeout after 5 minutes. If the status field in the json response is “success”, the code field will contain the OTP code.
Example usage:
thread = Gem::WebauthnPoller.poll_thread( {}, "RubyGems.org", "https://rubygems.org/api/v1/webauthn_verification/odow34b93t6aPCdY", { email: "email@example.com", password: "password" } ) thread.join otp = thread[:otp] error = thread[:error]
Object
is the default root of all Ruby
objects. Object
inherits from BasicObject
which allows creating alternate object hierarchies. Methods on Object
are available to all classes unless explicitly overridden.
Object
mixes in the Kernel
module, making the built-in kernel functions globally accessible. Although the instance methods of Object
are defined by the Kernel
module, we have chosen to document them here for clarity.
When referencing constants in classes inheriting from Object
you do not need to use the full namespace. For example, referencing File
inside YourClass
will find the top-level File
class.
In the descriptions of Object’s methods, the parameter symbol refers to a symbol, which is either a quoted string or a Symbol
(such as :name
).
First, what’s elsewhere. Class
Object:
Inherits from class BasicObject.
Includes module Kernel.
Here, class Object provides methods for:
!~
: Returns true
if self
does not match the given object, otherwise false
.
<=>
: Returns 0 if self
and the given object object
are the same object, or if self == object
; otherwise returns nil
.
===
: Implements case equality, effectively the same as calling ==
.
eql?
: Implements hash equality, effectively the same as calling ==
.
kind_of?
(aliased as is_a?
): Returns whether given argument is an ancestor of the singleton class of self
.
instance_of?
: Returns whether self
is an instance of the given class.
instance_variable_defined?
: Returns whether the given instance variable is defined in self
.
method
: Returns the Method
object for the given method in self
.
methods
: Returns an array of symbol names of public and protected methods in self
.
nil?
: Returns false
. (Only nil
responds true
to method nil?
.)
object_id
: Returns an integer corresponding to self
that is unique for the current process
private_methods
: Returns an array of the symbol names of the private methods in self
.
protected_methods
: Returns an array of the symbol names of the protected methods in self
.
public_method
: Returns the Method
object for the given public method in self
.
public_methods
: Returns an array of the symbol names of the public methods in self
.
respond_to?
: Returns whether self
responds to the given method.
singleton_class
: Returns the singleton class of self
.
singleton_method
: Returns the Method
object for the given singleton method in self
.
singleton_methods
: Returns an array of the symbol names of the singleton methods in self
.
define_singleton_method
: Defines a singleton method in self
for the given symbol method-name and block or proc.
extend
: Includes the given modules in the singleton class of self
.
public_send
: Calls the given public method in self
with the given argument.
send
: Calls the given method in self
with the given argument.
instance_variable_get
: Returns the value of the given instance variable in self
, or nil
if the instance variable is not set.
instance_variable_set
: Sets the value of the given instance variable in self
to the given object.
instance_variables
: Returns an array of the symbol names of the instance variables in self
.
remove_instance_variable
: Removes the named instance variable from self
.
clone
: Returns a shallow copy of self
, including singleton class and frozen state.
define_singleton_method
: Defines a singleton method in self
for the given symbol method-name and block or proc.
dup
: Returns a shallow unfrozen copy of self
.
enum_for
(aliased as to_enum
): Returns an Enumerator
for self
using the using the given method, arguments, and block.
extend
: Includes the given modules in the singleton class of self
.
freeze
: Prevents further modifications to self
.
hash
: Returns the integer hash value for self
.
inspect
: Returns a human-readable string representation of self
.
itself
: Returns self
.
method_missing
: Method
called when an undefined method is called on self
.
public_send
: Calls the given public method in self
with the given argument.
send
: Calls the given method in self
with the given argument.
to_s
: Returns a string representation of self
.
DateTime
A subclass of Date
that easily handles date, hour, minute, second, and offset.
DateTime
class is considered deprecated. Use Time
class.
DateTime
does not consider any leap seconds, does not track any summer time rules.
A DateTime
object is created with DateTime::new
, DateTime::jd
, DateTime::ordinal
, DateTime::commercial
, DateTime::parse
, DateTime::strptime
, DateTime::now
, Time#to_datetime
, etc.
require 'date' DateTime.new(2001,2,3,4,5,6) #=> #<DateTime: 2001-02-03T04:05:06+00:00 ...>
The last element of day, hour, minute, or second can be a fractional number. The fractional number’s precision is assumed at most nanosecond.
DateTime.new(2001,2,3.5) #=> #<DateTime: 2001-02-03T12:00:00+00:00 ...>
An optional argument, the offset, indicates the difference between the local time and UTC. For example, Rational(3,24)
represents ahead of 3 hours of UTC, Rational(-5,24)
represents behind of 5 hours of UTC. The offset should be -1 to +1, and its precision is assumed at most second. The default value is zero (equals to UTC).
DateTime.new(2001,2,3,4,5,6,Rational(3,24)) #=> #<DateTime: 2001-02-03T04:05:06+03:00 ...>
The offset also accepts string form:
DateTime.new(2001,2,3,4,5,6,'+03:00') #=> #<DateTime: 2001-02-03T04:05:06+03:00 ...>
An optional argument, the day of calendar reform (start
), denotes a Julian day number, which should be 2298874 to 2426355 or negative/positive infinity. The default value is Date::ITALY
(2299161=1582-10-15).
A DateTime
object has various methods. See each reference.
d = DateTime.parse('3rd Feb 2001 04:05:06+03:30') #=> #<DateTime: 2001-02-03T04:05:06+03:30 ...> d.hour #=> 4 d.min #=> 5 d.sec #=> 6 d.offset #=> (7/48) d.zone #=> "+03:30" d += Rational('1.5') #=> #<DateTime: 2001-02-04%16:05:06+03:30 ...> d = d.new_offset('+09:00') #=> #<DateTime: 2001-02-04%21:35:06+09:00 ...> d.strftime('%I:%M:%S %p') #=> "09:35:06 PM" d > DateTime.new(1999) #=> true
DateTime
and when should you use Time
? It’s a common misconception that William Shakespeare and Miguel de Cervantes died on the same day in history - so much so that UNESCO named April 23 as World Book Day because of this fact. However, because England hadn’t yet adopted the Gregorian Calendar Reform (and wouldn’t until 1752) their deaths are actually 10 days apart. Since Ruby’s Time
class implements a proleptic Gregorian calendar and has no concept of calendar reform there’s no way to express this with Time
objects. This is where DateTime
steps in:
shakespeare = DateTime.iso8601('1616-04-23', Date::ENGLAND) #=> Tue, 23 Apr 1616 00:00:00 +0000 cervantes = DateTime.iso8601('1616-04-23', Date::ITALY) #=> Sat, 23 Apr 1616 00:00:00 +0000
Already you can see something is weird - the days of the week are different. Taking this further:
cervantes == shakespeare #=> false (shakespeare - cervantes).to_i #=> 10
This shows that in fact they died 10 days apart (in reality 11 days since Cervantes died a day earlier but was buried on the 23rd). We can see the actual date of Shakespeare’s death by using the gregorian
method to convert it:
shakespeare.gregorian #=> Tue, 03 May 1616 00:00:00 +0000
So there’s an argument that all the celebrations that take place on the 23rd April in Stratford-upon-Avon are actually the wrong date since England is now using the Gregorian calendar. You can see why when we transition across the reform date boundary:
# start off with the anniversary of Shakespeare's birth in 1751 shakespeare = DateTime.iso8601('1751-04-23', Date::ENGLAND) #=> Tue, 23 Apr 1751 00:00:00 +0000 # add 366 days since 1752 is a leap year and April 23 is after February 29 shakespeare + 366 #=> Thu, 23 Apr 1752 00:00:00 +0000 # add another 365 days to take us to the anniversary in 1753 shakespeare + 366 + 365 #=> Fri, 04 May 1753 00:00:00 +0000
As you can see, if we’re accurately tracking the number of solar years since Shakespeare’s birthday then the correct anniversary date would be the 4th May and not the 23rd April.
So when should you use DateTime
in Ruby
and when should you use Time
? Almost certainly you’ll want to use Time
since your app is probably dealing with current dates and times. However, if you need to deal with dates and times in a historical context you’ll want to use DateTime
to avoid making the same mistakes as UNESCO. If you also have to deal with timezones then best of luck - just bear in mind that you’ll probably be dealing with local solar times, since it wasn’t until the 19th century that the introduction of the railways necessitated the need for Standard Time and eventually timezones.
A Time
object represents a date and time:
Time.new(2000, 1, 1, 0, 0, 0) # => 2000-01-01 00:00:00 -0600
Although its value can be expressed as a single numeric (see Epoch Seconds below), it can be convenient to deal with the value by parts:
t = Time.new(-2000, 1, 1, 0, 0, 0.0) # => -2000-01-01 00:00:00 -0600 t.year # => -2000 t.month # => 1 t.mday # => 1 t.hour # => 0 t.min # => 0 t.sec # => 0 t.subsec # => 0 t = Time.new(2000, 12, 31, 23, 59, 59.5) # => 2000-12-31 23:59:59.5 -0600 t.year # => 2000 t.month # => 12 t.mday # => 31 t.hour # => 23 t.min # => 59 t.sec # => 59 t.subsec # => (1/2)
Epoch seconds is the exact number of seconds (including fractional subseconds) since the Unix Epoch, January 1, 1970.
You can retrieve that value exactly using method Time.to_r
:
Time.at(0).to_r # => (0/1) Time.at(0.999999).to_r # => (9007190247541737/9007199254740992)
Other retrieval methods such as Time#to_i
and Time#to_f
may return a value that rounds or truncates subseconds.
A Time
object derived from the system clock (for example, by method Time.now
) has the resolution supported by the system.
Conceptually, Time
class uses a rational value to represent the number of seconds from Epoch, 1970-01-01 00:00:00 UTC. There are no boundary or resolution limitations. The value can be obtained using Time#to_r
.
The Time
class always uses the Gregorian calendar. I.e. the proleptic Gregorian calendar is used. Other calendars, such as Julian calendar, are not supported.
The implementation uses a signed 63 bit integer, Integer
(Bignum) object or Ratoinal object to represent a rational value. (The signed 63 bit integer is used regardless of 32 and 64 bit environments.) The value represents the number of nanoseconds from Epoch. The signed 63 bit integer can represent 1823-11-12 to 2116-02-20. When Integer
or Rational
object is used (before 1823, after 2116, under nanosecond), Time
works slower than when the signed 63 bit integer is used.
Ruby
uses the C function localtime
and gmtime
to map between the number and 6-tuple (year,month,day,hour,minute,second). localtime
is used for local time and gmtime
is used for UTC.
Integer
and Rational
has no range limit, but the localtime and gmtime has range limits due to the C types time_t
and struct tm
. If that limit is exceeded, Ruby
extrapolates the localtime function.
time_t
can represent 1901-12-14 to 2038-01-19 if it is 32 bit signed integer, -292277022657-01-27 to 292277026596-12-05 if it is 64 bit signed integer. However localtime
on some platforms doesn’t supports negative time_t
(before 1970).
struct tm
has tm_year member to represent years. (tm_year = 0
means the year 1900.) It is defined as int
in the C standard. tm_year can represent years between -2147481748 to 2147485547 if int
is 32 bit.
Ruby
supports leap seconds as far as if the C function localtime
and gmtime
supports it. They use the tz database in most Unix systems. The tz database has timezones which supports leap seconds. For example, “Asia/Tokyo” doesn’t support leap seconds but “right/Asia/Tokyo” supports leap seconds. So, Ruby
supports leap seconds if the TZ environment variable is set to “right/Asia/Tokyo” in most Unix systems.
All of these examples were done using the EST timezone which is GMT-5.
Time
Instance You can create a new instance of Time
with Time.new
. This will use the current system time. Time.now
is an alias for this. You can also pass parts of the time to Time.new
such as year, month, minute, etc. When you want to construct a time this way you must pass at least a year. If you pass the year with nothing else time will default to January 1 of that year at 00:00:00 with the current system timezone. Here are some examples:
Time.new(2002) #=> 2002-01-01 00:00:00 -0500 Time.new(2002, 10) #=> 2002-10-01 00:00:00 -0500 Time.new(2002, 10, 31) #=> 2002-10-31 00:00:00 -0500
You can pass a UTC offset:
Time.new(2002, 10, 31, 2, 2, 2, "+02:00") #=> 2002-10-31 02:02:02 +0200
zone = timezone("Europe/Athens") # Eastern European Time, UTC+2 Time.new(2002, 10, 31, 2, 2, 2, zone) #=> 2002-10-31 02:02:02 +0200
You can also use Time.local
and Time.utc
to infer local and UTC timezones instead of using the current system setting.
You can also create a new time using Time.at
which takes the number of seconds (with subsecond) since the Unix Epoch.
Time.at(628232400) #=> 1989-11-28 00:00:00 -0500
Time
Once you have an instance of Time
there is a multitude of things you can do with it. Below are some examples. For all of the following examples, we will work on the assumption that you have done the following:
t = Time.new(1993, 02, 24, 12, 0, 0, "+09:00")
Was that a monday?
t.monday? #=> false
What year was that again?
t.year #=> 1993
Was it daylight savings at the time?
t.dst? #=> false
What’s the day a year later?
t + (60*60*24*365) #=> 1994-02-24 12:00:00 +0900
How many seconds was that since the Unix Epoch?
t.to_i #=> 730522800
You can also do standard functions like compare two times.
t1 = Time.new(2010) t2 = Time.new(2011) t1 == t2 #=> false t1 == t1 #=> true t1 < t2 #=> true t1 > t2 #=> false Time.new(2010,10,31).between?(t1, t2) #=> true
First, what’s elsewhere. Class
Time
:
Inherits from class Object.
Includes module Comparable.
Here, class Time
provides methods that are useful for:
::new
: Returns a new time from specified arguments (year, month, etc.), including an optional timezone value.
::local
(aliased as ::mktime
): Same as ::new
, except the timezone is the local timezone.
::utc
(aliased as ::gm
): Same as ::new
, except the timezone is UTC.
::at
: Returns a new time based on seconds since epoch.
::now
: Returns a new time based on the current system time.
+
(plus): Returns a new time increased by the given number of seconds.
-
(minus): Returns a new time decreased by the given number of seconds.
year
: Returns the year of the time.
hour
: Returns the hours value for the time.
min
: Returns the minutes value for the time.
sec
: Returns the seconds value for the time.
usec
(aliased as tv_usec
): Returns the number of microseconds in the subseconds value of the time.
nsec
(aliased as tv_nsec
: Returns the number of nanoseconds in the subsecond part of the time.
subsec
: Returns the subseconds value for the time.
wday
: Returns the integer weekday value of the time (0 == Sunday).
yday
: Returns the integer yearday value of the time (1 == January 1).
hash
: Returns the integer hash value for the time.
utc_offset
(aliased as gmt_offset
and gmtoff
): Returns the offset in seconds between time and UTC.
to_f
: Returns the float number of seconds since epoch for the time.
to_i
(aliased as tv_sec
): Returns the integer number of seconds since epoch for the time.
to_r
: Returns the Rational
number of seconds since epoch for the time.
zone
: Returns a string representation of the timezone of the time.
dst?
(aliased as isdst
): Returns whether the time is DST (daylight saving time).
sunday?
: Returns whether the time is a Sunday.
monday?
: Returns whether the time is a Monday.
tuesday?
: Returns whether the time is a Tuesday.
wednesday?
: Returns whether the time is a Wednesday.
thursday?
: Returns whether the time is a Thursday.
friday?
: Returns whether time is a Friday.
saturday?
: Returns whether the time is a Saturday.
inspect
: Returns the time in detail as a string.
strftime
: Returns the time as a string, according to a given format.
to_a
: Returns a 10-element array of values from the time.
to_s
: Returns a string representation of the time.
getutc
(aliased as getgm
): Returns a new time converted to UTC.
getlocal
: Returns a new time converted to local time.
localtime
: Converts time to local time in place.
deconstruct_keys
: Returns a hash of time components used in pattern-matching.
round
:Returns a new time with subseconds rounded.
ceil
: Returns a new time with subseconds raised to a ceiling.
floor
: Returns a new time with subseconds lowered to a floor.
For the forms of argument zone
, see Timezone Specifiers.
Certain Time
methods accept arguments that specify timezones:
Time.at
: keyword argument in:
.
Time.new
: positional argument zone
or keyword argument in:
.
Time.now
: keyword argument in:
.
Time#getlocal
: positional argument zone
.
Time#localtime
: positional argument zone
.
The value given with any of these must be one of the following (each detailed below):
The zone value may be a string offset from UTC in the form '+HH:MM'
or '-HH:MM'
, where:
HH
is the 2-digit hour in the range 0..23
.
MM
is the 2-digit minute in the range 0..59
.
Examples:
t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC Time.at(t, in: '-23:59') # => 1999-12-31 20:16:01 -2359 Time.at(t, in: '+23:59') # => 2000-01-02 20:14:01 +2359
The zone value may be a letter in the range 'A'..'I'
or 'K'..'Z'
; see List of military time zones:
t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC Time.at(t, in: 'A') # => 2000-01-01 21:15:01 +0100 Time.at(t, in: 'I') # => 2000-01-02 05:15:01 +0900 Time.at(t, in: 'K') # => 2000-01-02 06:15:01 +1000 Time.at(t, in: 'Y') # => 2000-01-01 08:15:01 -1200 Time.at(t, in: 'Z') # => 2000-01-01 20:15:01 UTC
The zone value may be an integer number of seconds in the range -86399..86399
:
t = Time.utc(2000, 1, 1, 20, 15, 1) # => 2000-01-01 20:15:01 UTC Time.at(t, in: -86399) # => 1999-12-31 20:15:02 -235959 Time.at(t, in: 86399) # => 2000-01-02 20:15:00 +235959
The zone value may be an object responding to certain timezone methods, an instance of Timezone and TZInfo for example.
The timezone methods are:
local_to_utc
:
Called when Time.new
is invoked with tz
as the value of positional argument zone
or keyword argument in:
.
a Time-like object in the UTC timezone.
utc_to_local
:
Called when Time.at
or Time.now
is invoked with tz
as the value for keyword argument in:
, and when Time#getlocal
or Time#localtime
is called with tz
as the value for positional argument zone
.
The UTC offset will be calculated as the difference between the original time and the returned object as an Integer
. If the object is in fixed offset, its utc_offset
is also counted.
a Time-like object in the local timezone.
A custom timezone class may have these instance methods, which will be called if defined:
abbr
:
Called when Time#strftime
is invoked with a format involving %Z
.
a string abbreviation for the timezone name.
dst?
:
Called when Time.at
or Time.now
is invoked with tz
as the value for keyword argument in:
, and when Time#getlocal
or Time#localtime
is called with tz
as the value for positional argument zone
.
whether the time is daylight saving time.
name
:
Called when Marshal.dump(t)
is invoked
none.
the string name of the timezone.
Time
-Like Objects A Time
-like object is a container object capable of interfacing with timezone libraries for timezone conversion.
The argument to the timezone conversion methods above will have attributes similar to Time
, except that timezone related attributes are meaningless.
The objects returned by local_to_utc
and utc_to_local
methods of the timezone object may be of the same class as their arguments, of arbitrary object classes, or of class Integer
.
For a returned class other than Integer
, the class must have the following methods:
year
mon
mday
hour
min
sec
isdst
For a returned Integer
, its components, decomposed in UTC, are interpreted as times in the specified timezone.
If the class (the receiver of class methods, or the class of the receiver of instance methods) has find_timezone
singleton method, this method is called to achieve the corresponding timezone object from a timezone name.
For example, using Timezone:
class TimeWithTimezone < Time require 'timezone' def self.find_timezone(z) = Timezone[z] end TimeWithTimezone.now(in: "America/New_York") #=> 2023-12-25 00:00:00 -0500 TimeWithTimezone.new("2023-12-25 America/New_York") #=> 2023-12-25 00:00:00 -0500
Or, using TZInfo:
class TimeWithTZInfo < Time require 'tzinfo' def self.find_timezone(z) = TZInfo::Timezone.get(z) end TimeWithTZInfo.now(in: "America/New_York") #=> 2023-12-25 00:00:00 -0500 TimeWithTZInfo.new("2023-12-25 America/New_York") #=> 2023-12-25 00:00:00 -0500
You can define this method per subclasses, or on the toplevel Time
class.
An instance of class IO (commonly called a stream) represents an input/output stream in the underlying operating system. Class
IO is the basis for input and output in Ruby
.
Class
File
is the only class in the Ruby
core that is a subclass of IO. Some classes in the Ruby
standard library are also subclasses of IO; these include TCPSocket
and UDPSocket
.
The global constant ARGF
(also accessible as $<
) provides an IO-like stream that allows access to all file paths found in ARGV (or found in STDIN if ARGV is empty). ARGF
is not itself a subclass of IO.
Class
StringIO
provides an IO-like stream that handles a String
. StringIO
is not itself a subclass of IO.
Important objects based on IO include:
$stdin.
$stdout.
$stderr.
Instances of class File
.
An instance of IO may be created using:
IO.new
: returns a new IO object for the given integer file descriptor.
IO.open
: passes a new IO object to the given block.
IO.popen
: returns a new IO object that is connected to the $stdin and $stdout of a newly-launched subprocess.
Kernel#open
: Returns a new IO object connected to a given source: stream, file, or subprocess.
Like a File
stream, an IO stream has:
A read/write mode, which may be read-only, write-only, or read/write; see Read/Write Mode.
A data mode, which may be text-only or binary; see Data Mode.
Internal and external encodings; see Encodings.
And like other IO streams, it has:
A position, which determines where in the stream the next read or write is to occur; see Position.
A line number, which is a special, line-oriented, “position” (different from the position mentioned above); see Line Number.
io/console
Extension io/console
provides numerous methods for interacting with the console; requiring it adds numerous methods to class IO.
Many examples here use these variables:
# English text with newlines. text = <<~EOT First line Second line Fourth line Fifth line EOT # Russian text. russian = "\u{442 435 441 442}" # => "тест" # Binary data. data = "\u9990\u9991\u9992\u9993\u9994" # Text file. File.write('t.txt', text) # File with Russian text. File.write('t.rus', russian) # File with binary data. f = File.new('t.dat', 'wb:UTF-16') f.write(data) f.close
A number of IO methods accept optional keyword arguments that determine how a new stream is to be opened:
:mode
: Stream mode.
:flags
: Integer
file open flags; If mode
is also given, the two are bitwise-ORed.
:external_encoding
: External encoding for the stream.
:internal_encoding
: Internal encoding for the stream. '-'
is a synonym for the default internal encoding. If the value is nil
no conversion occurs.
:encoding
: Specifies external and internal encodings as 'extern:intern'
.
:textmode
: If a truthy value, specifies the mode as text-only, binary otherwise.
:binmode
: If a truthy value, specifies the mode as binary, text-only otherwise.
:autoclose
: If a truthy value, specifies that the fd
will close when the stream closes; otherwise it remains open.
:path:
If a string value is provided, it is used in inspect
and is available as path
method.
Also available are the options offered in String#encode
, which may control conversion between external and internal encoding.
You can perform basic stream IO with these methods, which typically operate on multi-byte strings:
IO#read
: Reads and returns some or all of the remaining bytes from the stream.
IO#write
: Writes zero or more strings to the stream; each given object that is not already a string is converted via to_s
.
An IO stream has a nonnegative integer position, which is the byte offset at which the next read or write is to occur. A new stream has position zero (and line number zero); method rewind
resets the position (and line number) to zero.
These methods discard buffers and the Encoding::Converter
instances used for that IO.
The relevant methods:
IO#tell
(aliased as pos
): Returns the current position (in bytes) in the stream.
IO#pos=
: Sets the position of the stream to a given integer new_position
(in bytes).
IO#seek
: Sets the position of the stream to a given integer offset
(in bytes), relative to a given position whence
(indicating the beginning, end, or current position).
IO#rewind
: Positions the stream at the beginning (also resetting the line number).
A new IO stream may be open for reading, open for writing, or both.
A stream is automatically closed when claimed by the garbage collector.
Attempted reading or writing on a closed stream raises an exception.
The relevant methods:
IO#close
: Closes the stream for both reading and writing.
IO#close_read
: Closes the stream for reading.
IO#close_write
: Closes the stream for writing.
IO#closed?
: Returns whether the stream is closed.
You can query whether a stream is positioned at its end:
You can reposition to end-of-stream by using method IO#seek
:
f = File.new('t.txt') f.eof? # => false f.seek(0, :END) f.eof? # => true f.close
Or by reading all stream content (which is slower than using IO#seek
):
f.rewind f.eof? # => false f.read # => "First line\nSecond line\n\nFourth line\nFifth line\n" f.eof? # => true
Class
IO supports line-oriented input and output
Class
IO supports line-oriented input for files and IO streams
You can read lines from a file using these methods:
IO.foreach
: Reads each line and passes it to the given block.
IO.readlines
: Reads and returns all lines in an array.
For each of these methods:
You can specify open options.
Line parsing depends on the effective line separator; see Line Separator.
The length of each returned line depends on the effective line limit; see Line Limit.
You can read lines from an IO stream using these methods:
IO#each_line
: Reads each remaining line, passing it to the given block.
IO#gets
: Returns the next line.
IO#readline
: Like gets
, but raises an exception at end-of-stream.
IO#readlines
: Returns all remaining lines in an array.
For each of these methods:
Reading may begin mid-line, depending on the stream’s position; see Position.
Line parsing depends on the effective line separator; see Line Separator.
The length of each returned line depends on the effective line limit; see Line Limit.
Each of the line input methods uses a line separator: the string that determines what is considered a line; it is sometimes called the input record separator.
The default line separator is taken from global variable $/
, whose initial value is "\n"
.
Generally, the line to be read next is all data from the current position to the next line separator (but see Special Line Separator Values):
f = File.new('t.txt') # Method gets with no sep argument returns the next line, according to $/. f.gets # => "First line\n" f.gets # => "Second line\n" f.gets # => "\n" f.gets # => "Fourth line\n" f.gets # => "Fifth line\n" f.close
You can use a different line separator by passing argument sep
:
f = File.new('t.txt') f.gets('l') # => "First l" f.gets('li') # => "ine\nSecond li" f.gets('lin') # => "ne\n\nFourth lin" f.gets # => "e\n" f.close
Or by setting global variable $/
:
f = File.new('t.txt') $/ = 'l' f.gets # => "First l" f.gets # => "ine\nSecond l" f.gets # => "ine\n\nFourth l" f.close
Each of the line input methods accepts two special values for parameter sep
:
nil
: The entire stream is to be read (“slurped”) into a single string:
f = File.new('t.txt') f.gets(nil) # => "First line\nSecond line\n\nFourth line\nFifth line\n" f.close
''
(the empty string): The next “paragraph” is to be read (paragraphs being separated by two consecutive line separators):
f = File.new('t.txt') f.gets('') # => "First line\nSecond line\n\n" f.gets('') # => "Fourth line\nFifth line\n" f.close
Each of the line input methods uses an integer line limit, which restricts the number of bytes that may be returned. (A multi-byte character will not be split, and so a returned line may be slightly longer than the limit).
The default limit value is -1
; any negative limit value means that there is no limit.
If there is no limit, the line is determined only by sep
.
# Text with 1-byte characters. File.open('t.txt') {|f| f.gets(1) } # => "F" File.open('t.txt') {|f| f.gets(2) } # => "Fi" File.open('t.txt') {|f| f.gets(3) } # => "Fir" File.open('t.txt') {|f| f.gets(4) } # => "Firs" # No more than one line. File.open('t.txt') {|f| f.gets(10) } # => "First line" File.open('t.txt') {|f| f.gets(11) } # => "First line\n" File.open('t.txt') {|f| f.gets(12) } # => "First line\n" # Text with 2-byte characters, which will not be split. File.open('t.rus') {|f| f.gets(1).size } # => 1 File.open('t.rus') {|f| f.gets(2).size } # => 1 File.open('t.rus') {|f| f.gets(3).size } # => 2 File.open('t.rus') {|f| f.gets(4).size } # => 2
With arguments sep
and limit
given, combines the two behaviors:
Returns the next line as determined by line separator sep
.
But returns no more bytes than are allowed by the limit limit
.
Example:
File.open('t.txt') {|f| f.gets('li', 20) } # => "First li" File.open('t.txt') {|f| f.gets('li', 2) } # => "Fi"
A readable IO stream has a non-negative integer line number:
IO#lineno
: Returns the line number.
IO#lineno=
: Resets and returns the line number.
Unless modified by a call to method IO#lineno=
, the line number is the number of lines read by certain line-oriented methods, according to the effective line separator:
IO.foreach
: Increments the line number on each call to the block.
IO#each_line
: Increments the line number on each call to the block.
IO#gets
: Increments the line number.
IO#readline
: Increments the line number.
IO#readlines
: Increments the line number for each line read.
A new stream is initially has line number zero (and position zero); method rewind
resets the line number (and position) to zero:
f = File.new('t.txt') f.lineno # => 0 f.gets # => "First line\n" f.lineno # => 1 f.rewind f.lineno # => 0 f.close
Reading lines from a stream usually changes its line number:
f = File.new('t.txt', 'r') f.lineno # => 0 f.readline # => "This is line one.\n" f.lineno # => 1 f.readline # => "This is the second line.\n" f.lineno # => 2 f.readline # => "Here's the third line.\n" f.lineno # => 3 f.eof? # => true f.close
Iterating over lines in a stream usually changes its line number:
File.open('t.txt') do |f| f.each_line do |line| p "position=#{f.pos} eof?=#{f.eof?} lineno=#{f.lineno}" end end
Output:
"position=11 eof?=false lineno=1" "position=23 eof?=false lineno=2" "position=24 eof?=false lineno=3" "position=36 eof?=false lineno=4" "position=47 eof?=true lineno=5"
Unlike the stream’s position, the line number does not affect where the next read or write will occur:
f = File.new('t.txt') f.lineno = 1000 f.lineno # => 1000 f.gets # => "First line\n" f.lineno # => 1001 f.close
Associated with the line number is the global variable $.
:
When a stream is opened, $.
is not set; its value is left over from previous activity in the process:
$. = 41 f = File.new('t.txt') $. = 41 # => 41 f.close
When a stream is read, $.
is set to the line number for that stream:
f0 = File.new('t.txt') f1 = File.new('t.dat') f0.readlines # => ["First line\n", "Second line\n", "\n", "Fourth line\n", "Fifth line\n"] $. # => 5 f1.readlines # => ["\xFE\xFF\x99\x90\x99\x91\x99\x92\x99\x93\x99\x94"] $. # => 1 f0.close f1.close
Methods IO#rewind
and IO#seek
do not affect $.
:
f = File.new('t.txt') f.readlines # => ["First line\n", "Second line\n", "\n", "Fourth line\n", "Fifth line\n"] $. # => 5 f.rewind f.seek(0, :SET) $. # => 5 f.close
You can write to an IO stream line-by-line using this method:
IO#puts
: Writes objects to the stream.
You can process an IO stream character-by-character using these methods:
IO#getc
: Reads and returns the next character from the stream.
IO#readchar
: Like getc
, but raises an exception at end-of-stream.
IO#ungetc
: Pushes back (“unshifts”) a character or integer onto the stream.
IO#putc
: Writes a character to the stream.
IO#each_char
: Reads each remaining character in the stream, passing the character to the given block.
You can process an IO stream byte-by-byte using these methods:
IO#getbyte
: Returns the next 8-bit byte as an integer in range 0..255.
IO#readbyte
: Like getbyte
, but raises an exception if at end-of-stream.
IO#ungetbyte
: Pushes back (“unshifts”) a byte back onto the stream.
IO#each_byte
: Reads each remaining byte in the stream, passing the byte to the given block.
You can process an IO stream codepoint-by-codepoint:
IO#each_codepoint
: Reads each remaining codepoint, passing it to the given block.
First, what’s elsewhere. Class
IO:
Inherits from class Object.
Includes module Enumerable, which provides dozens of additional methods.
Here, class IO provides methods that are useful for:
::new
(aliased as ::for_fd
): Creates and returns a new IO object for the given integer file descriptor.
::open
: Creates a new IO object.
::pipe
: Creates a connected pair of reader and writer IO objects.
::popen
: Creates an IO object to interact with a subprocess.
::select
: Selects which given IO instances are ready for reading, writing, or have pending exceptions.
::binread
: Returns a binary string with all or a subset of bytes from the given file.
::read
: Returns a string with all or a subset of bytes from the given file.
::readlines
: Returns an array of strings, which are the lines from the given file.
getbyte
: Returns the next 8-bit byte read from self
as an integer.
getc
: Returns the next character read from self
as a string.
gets
: Returns the line read from self
.
pread
: Returns all or the next n bytes read from self
, not updating the receiver’s offset.
read
: Returns all remaining or the next n bytes read from self
for a given n.
read_nonblock
: the next n bytes read from self
for a given n, in non-block mode.
readbyte
: Returns the next byte read from self
; same as getbyte
, but raises an exception on end-of-stream.
readchar
: Returns the next character read from self
; same as getc
, but raises an exception on end-of-stream.
readline
: Returns the next line read from self
; same as getline, but raises an exception of end-of-stream.
readlines
: Returns an array of all lines read read from self
.
readpartial
: Returns up to the given number of bytes from self
.
::binwrite
: Writes the given string to the file at the given filepath, in binary mode.
::write
: Writes the given string to self
.
<<
: Appends the given string to self
.
print
: Prints last read line or given objects to self
.
printf
: Writes to self
based on the given format string and objects.
putc
: Writes a character to self
.
puts
: Writes lines to self
, making sure line ends with a newline.
pwrite
: Writes the given string at the given offset, not updating the receiver’s offset.
write
: Writes one or more given strings to self
.
write_nonblock
: Writes one or more given strings to self
in non-blocking mode.
lineno
: Returns the current line number in self
.
lineno=
: Sets the line number is self
.
pos
(aliased as tell
): Returns the current byte offset in self
.
pos=
: Sets the byte offset in self
.
reopen
: Reassociates self
with a new or existing IO stream.
rewind
: Positions self
to the beginning of input.
seek
: Sets the offset for self
relative to given position.
::foreach
: Yields each line of given file to the block.
each
(aliased as each_line
): Calls the given block with each successive line in self
.
each_byte
: Calls the given block with each successive byte in self
as an integer.
each_char
: Calls the given block with each successive character in self
as a string.
each_codepoint
: Calls the given block with each successive codepoint in self
as an integer.
autoclose=
: Sets whether self
auto-closes.
binmode
: Sets self
to binary mode.
close
: Closes self
.
close_on_exec=
: Sets the close-on-exec flag.
close_read
: Closes self
for reading.
close_write
: Closes self
for writing.
set_encoding
: Sets the encoding for self
.
set_encoding_by_bom
: Sets the encoding for self
, based on its Unicode byte-order-mark.
sync=
: Sets the sync-mode to the given value.
autoclose?
: Returns whether self
auto-closes.
binmode?
: Returns whether self
is in binary mode.
close_on_exec?
: Returns the close-on-exec flag for self
.
closed?
: Returns whether self
is closed.
eof?
(aliased as eof
): Returns whether self
is at end-of-stream.
external_encoding
: Returns the external encoding object for self
.
fileno
(aliased as to_i
): Returns the integer file descriptor for self
internal_encoding
: Returns the internal encoding object for self
.
pid
: Returns the process ID of a child process associated with self
, if self
was created by ::popen
.
stat
: Returns the File::Stat
object containing status information for self
.
sync
: Returns whether self
is in sync-mode.
tty?
(aliased as isatty
): Returns whether self
is a terminal.
fdatasync
: Immediately writes all buffered data in self
to disk.
flush
: Flushes any buffered data within self
to the underlying operating system.
fsync
: Immediately writes all buffered data and attributes in self
to disk.
ungetbyte
: Prepends buffer for self
with given integer byte or string.
ungetc
: Prepends buffer for self
with given string.
::sysopen
: Opens the file given by its path, returning the integer file descriptor.
advise
: Announces the intention to access data from self
in a specific way.
fcntl
: Passes a low-level command to the file specified by the given file descriptor.
ioctl
: Passes a low-level command to the device specified by the given file descriptor.
sysread
: Returns up to the next n bytes read from self using a low-level read.
sysseek
: Sets the offset for self
.
syswrite
: Writes the given string to self
using a low-level write.
::copy_stream
: Copies data from a source to a destination, each of which is a filepath or an IO-like object.
::try_convert
: Returns a new IO object resulting from converting the given object.
inspect
: Returns the string representation of self
.
Class
Struct provides a convenient way to create a simple class that can store and fetch values.
This example creates a subclass of Struct
, Struct::Customer
; the first argument, a string, is the name of the subclass; the other arguments, symbols, determine the members of the new subclass.
Customer = Struct.new('Customer', :name, :address, :zip) Customer.name # => "Struct::Customer" Customer.class # => Class Customer.superclass # => Struct
Corresponding to each member are two methods, a writer and a reader, that store and fetch values:
methods = Customer.instance_methods false methods # => [:zip, :address=, :zip=, :address, :name, :name=]
An instance of the subclass may be created, and its members assigned values, via method ::new
:
joe = Customer.new("Joe Smith", "123 Maple, Anytown NC", 12345) joe # => #<struct Struct::Customer name="Joe Smith", address="123 Maple, Anytown NC", zip=12345>
The member values may be managed thus:
joe.name # => "Joe Smith" joe.name = 'Joseph Smith' joe.name # => "Joseph Smith"
And thus; note that member name may be expressed as either a string or a symbol:
joe[:name] # => "Joseph Smith" joe[:name] = 'Joseph Smith, Jr.' joe['name'] # => "Joseph Smith, Jr."
See Struct::new
.
First, what’s elsewhere. Class
Struct:
Inherits from class Object.
Includes module Enumerable, which provides dozens of additional methods.
See also Data
, which is a somewhat similar, but stricter concept for defining immutable value objects.
Here, class Struct provides methods that are useful for:
Struct
Subclass ::new
: Returns a new subclass of Struct.
==
: Returns whether a given object is equal to self
, using ==
to compare member values.
eql?
: Returns whether a given object is equal to self
, using eql?
to compare member values.
[]
: Returns the value associated with a given member name.
to_a
(aliased as values
, deconstruct
): Returns the member values in self
as an array.
deconstruct_keys
: Returns a hash of the name/value pairs for given member names.
dig
: Returns the object in nested objects that is specified by a given member name and additional arguments.
members
: Returns an array of the member names.
select
(aliased as filter
): Returns an array of member values from self
, as selected by the given block.
values_at
: Returns an array containing values for given member names.
[]=
: Assigns a given value to a given member name.
each
: Calls a given block with each member name.
each_pair
: Calls a given block with each member name/value pair.
UNIXServer
represents a UNIX domain stream server socket.
UNIXSocket
represents a UNIX domain stream client socket.
IO streams for strings, with access similar to IO
; see IO
.
Examples on this page assume that StringIO has been required:
require 'stringio'
BasicObject
is the parent class of all classes in Ruby
. In particular, BasicObject
is the parent class of class Object
, which is itself the default parent class of every Ruby
class:
class Foo; end Foo.superclass # => Object Object.superclass # => BasicObject
BasicObject
is the only class that has no parent:
BasicObject.superclass # => nil
Class
BasicObject
can be used to create an object hierarchy (e.g., class Delegator
) that is independent of Ruby’s object hierarchy. Such objects:
Do not have namespace “pollution” from the many methods provided in class Object
and its included module Kernel
.
Do not have definitions of common classes, and so references to such common classes must be fully qualified (::String
, not String
).
A variety of strategies can be used to provide useful portions of the Standard Library in subclasses of BasicObject
:
The immediate subclass could include Kernel
, which would define methods such as puts
, exit
, etc.
A custom Kernel-like module could be created and included.
Delegation can be used via method_missing
:
class MyObjectSystem < BasicObject DELEGATE = [:puts, :p] def method_missing(name, *args, &block) return super unless DELEGATE.include? name ::Kernel.send(name, *args, &block) end def respond_to_missing?(name, include_private = false) DELEGATE.include?(name) end end
These are the methods defined for BasicObject:
::new
: Returns a new BasicObject instance.
!
: Returns the boolean negation of self
: true
or false
.
!=
: Returns whether self
and the given object are not equal.
==
: Returns whether self
and the given object are equivalent.
__id__
: Returns the integer object identifier for self
.
__send__
: Calls the method identified by the given symbol.
equal?
: Returns whether self
and the given object are the same object.
instance_eval
: Evaluates the given string or block in the context of self
.
instance_exec
: Executes the given block in the context of self
, passing the given arguments.
method_missing
: Called when self
is called with a method it does not define.
singleton_method_added
: Called when a singleton method is added to self
.
singleton_method_removed
: Called when a singleton method is removed from self
.
singleton_method_undefined
: Called when a singleton method is undefined in self
.
Raised when an IO
operation fails.
File.open("/etc/hosts") {|f| f << "example"} #=> IOError: not opened for writing File.open("/etc/hosts") {|f| f.close; f.read } #=> IOError: closed stream
Note that some IO
failures raise SystemCallError
s and these are not subclasses of IOError:
File.open("does/not/exist") #=> Errno::ENOENT: No such file or directory - does/not/exist
Ractor is an Actor-model abstraction for Ruby
that provides thread-safe parallel execution.
Ractor.new
makes a new Ractor, which can run in parallel.
# The simplest ractor r = Ractor.new {puts "I am in Ractor!"} r.join # wait for it to finish # Here, "I am in Ractor!" is printed
Ractors do not share all objects with each other. There are two main benefits to this: across ractors, thread-safety concerns such as data-races and race-conditions are not possible. The other benefit is parallelism.
To achieve this, object sharing is limited across ractors. For example, unlike in threads, ractors can’t access all the objects available in other ractors. Even objects normally available through variables in the outer scope are prohibited from being used across ractors.
a = 1 r = Ractor.new {puts "I am in Ractor! a=#{a}"} # fails immediately with # ArgumentError (can not isolate a Proc because it accesses outer variables (a).)
The object must be explicitly shared:
a = 1 r = Ractor.new(a) { |a1| puts "I am in Ractor! a=#{a1}"}
On CRuby (the default implementation), Global Virtual Machine Lock (GVL) is held per ractor, so ractors can perform in parallel without locking each other. This is unlike the situation with threads on CRuby.
Instead of accessing shared state, objects should be passed to and from ractors by sending and receiving them as messages.
a = 1 r = Ractor.new do a_in_ractor = receive # receive blocks until somebody passes a message puts "I am in Ractor! a=#{a_in_ractor}" end r.send(a) # pass it r.join # Here, "I am in Ractor! a=1" is printed
In addition to that, any arguments passed to Ractor.new
are passed to the block and available there as if received by Ractor.receive
, and the last block value can be received with Ractor#value
.
When an object is sent to and from a ractor, it’s important to understand whether the object is shareable or unshareable. Most Ruby
objects are unshareable objects. Even frozen objects can be unshareable if they contain (through their instance variables) unfrozen objects.
Shareable objects are those which can be used by several threads without compromising thread-safety, for example numbers, true
and false
. Ractor.shareable?
allows you to check this, and Ractor.make_shareable
tries to make the object shareable if it’s not already, and gives an error if it can’t do it.
Ractor.shareable?(1) #=> true -- numbers and other immutable basic values are shareable Ractor.shareable?('foo') #=> false, unless the string is frozen due to # frozen_string_literal: true Ractor.shareable?('foo'.freeze) #=> true Ractor.shareable?([Object.new].freeze) #=> false, inner object is unfrozen ary = ['hello', 'world'] ary.frozen? #=> false ary[0].frozen? #=> false Ractor.make_shareable(ary) ary.frozen? #=> true ary[0].frozen? #=> true ary[1].frozen? #=> true
When a shareable object is sent (via send
or Ractor.yield), no additional processing occurs on it. It just becomes usable by both ractors. When an unshareable object is sent, it can be either copied or moved. The first is the default, and it copies the object fully by deep cloning (Object#clone
) the non-shareable parts of its structure.
data = ['foo', 'bar'.freeze] r = Ractor.new do data2 = Ractor.receive puts "In ractor: #{data2.object_id}, #{data2[0].object_id}, #{data2[1].object_id}" end r.send(data) r.take puts "Outside : #{data.object_id}, #{data[0].object_id}, #{data[1].object_id}"
This will output something like:
In ractor: 340, 360, 320 Outside : 380, 400, 320
Note that the object ids of the array and the non-frozen string inside the array have changed in the ractor because they are different objects. The second array’s element, which is a shareable frozen string, is the same object.
Deep cloning of objects may be slow, and sometimes impossible. Alternatively, move: true
may be used during sending. This will move the unshareable object to the receiving ractor, making it inaccessible to the sending ractor.
data = ['foo', 'bar'] r = Ractor.new do data_in_ractor = Ractor.receive puts "In ractor: #{data_in_ractor.object_id}, #{data_in_ractor[0].object_id}" end r.send(data, move: true) r.take puts "Outside: moved? #{Ractor::MovedObject === data}" puts "Outside: #{data.inspect}"
This will output:
In ractor: 100, 120 Outside: moved? true test.rb:9:in `method_missing': can not send any methods to a moved object (Ractor::MovedError)
Notice that even inspect
(and more basic methods like __id__
) is inaccessible on a moved object.
Class
and Module
objects are shareable so the class/module definitions are shared between ractors. Ractor objects are also shareable. All operations on shareable objects are thread-safe, so the thread-safety property will be kept. We can not define mutable shareable objects in Ruby
, but C extensions can introduce them.
It is prohibited to access (get) instance variables of shareable objects in other ractors if the values of the variables aren’t shareable. This can occur because modules/classes are shareable, but they can have instance variables whose values are not. In non-main ractors, it’s also prohibited to set instance variables on classes/modules (even if the value is shareable).
class C class << self attr_accessor :tricky end end C.tricky = "unshareable".dup r = Ractor.new(C) do |cls| puts "I see #{cls}" puts "I can't see #{cls.tricky}" cls.tricky = true # doesn't get here, but this would also raise an error end r.take # I see C # can not access instance variables of classes/modules from non-main Ractors (RuntimeError)
Ractors can access constants if they are shareable. The main Ractor is the only one that can access non-shareable constants.
GOOD = 'good'.freeze BAD = 'bad'.dup r = Ractor.new do puts "GOOD=#{GOOD}" puts "BAD=#{BAD}" end r.take # GOOD=good # can not access non-shareable objects in constant Object::BAD by non-main Ractor. (NameError) # Consider the same C class from above r = Ractor.new do puts "I see #{C}" puts "I can't see #{C.tricky}" end r.take # I see C # can not access instance variables of classes/modules from non-main Ractors (RuntimeError)
See also the description of shareable_constant_value pragma in Comments syntax explanation.
Each ractor has its own main Thread
. New threads can be created from inside ractors (and, on CRuby, they share the GVL with other threads of this ractor).
r = Ractor.new do a = 1 Thread.new {puts "Thread in ractor: a=#{a}"}.join end r.take # Here "Thread in ractor: a=1" will be printed
In the examples below, sometimes we use the following method to wait for ractors that are not currently blocked to finish (or to make progress).
def wait sleep(0.1) end
It is **only for demonstration purposes** and shouldn’t be used in a real code. Most of the time, take
is used to wait for ractors to finish.
See Ractor design doc for more details.
JSON is a lightweight data-interchange format.
A JSON value is one of the following:
Double-quoted text: "foo"
.
Number: 1
, 1.0
, 2.0e2
.
Boolean: true
, false
.
Null: null
.
Array: an ordered list of values, enclosed by square brackets:
["foo", 1, 1.0, 2.0e2, true, false, null]
Object: a collection of name/value pairs, enclosed by curly braces; each name is double-quoted text; the values may be any JSON values:
{"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}
A JSON array or object may contain nested arrays, objects, and scalars to any depth:
{"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]} [{"foo": 0, "bar": 1}, ["baz", 2]]
To make module JSON available in your code, begin with:
require 'json'
All examples here assume that this has been done.
You can parse a String containing JSON data using either of two methods:
where
source
is a Ruby
object.
opts
is a Hash object containing options that control both input allowed and output formatting.
The difference between the two methods is that JSON.parse!
omits some checks and may not be safe for some source
data; use it only for data from trusted sources. Use the safer method JSON.parse
for less trusted sources.
When source
is a JSON array, JSON.parse
by default returns a Ruby
Array:
json = '["foo", 1, 1.0, 2.0e2, true, false, null]' ruby = JSON.parse(json) ruby # => ["foo", 1, 1.0, 200.0, true, false, nil] ruby.class # => Array
The JSON array may contain nested arrays, objects, and scalars to any depth:
json = '[{"foo": 0, "bar": 1}, ["baz", 2]]' JSON.parse(json) # => [{"foo"=>0, "bar"=>1}, ["baz", 2]]
When the source is a JSON object, JSON.parse
by default returns a Ruby
Hash:
json = '{"a": "foo", "b": 1, "c": 1.0, "d": 2.0e2, "e": true, "f": false, "g": null}' ruby = JSON.parse(json) ruby # => {"a"=>"foo", "b"=>1, "c"=>1.0, "d"=>200.0, "e"=>true, "f"=>false, "g"=>nil} ruby.class # => Hash
The JSON object may contain nested arrays, objects, and scalars to any depth:
json = '{"foo": {"bar": 1, "baz": 2}, "bat": [0, 1, 2]}' JSON.parse(json) # => {"foo"=>{"bar"=>1, "baz"=>2}, "bat"=>[0, 1, 2]}
When the source is a JSON scalar (not an array or object), JSON.parse
returns a Ruby
scalar.
String:
ruby = JSON.parse('"foo"') ruby # => 'foo' ruby.class # => String
Integer:
ruby = JSON.parse('1') ruby # => 1 ruby.class # => Integer
Float:
ruby = JSON.parse('1.0') ruby # => 1.0 ruby.class # => Float ruby = JSON.parse('2.0e2') ruby # => 200 ruby.class # => Float
Boolean:
ruby = JSON.parse('true') ruby # => true ruby.class # => TrueClass ruby = JSON.parse('false') ruby # => false ruby.class # => FalseClass
Null:
ruby = JSON.parse('null') ruby # => nil ruby.class # => NilClass
Option max_nesting
(Integer) specifies the maximum nesting depth allowed; defaults to 100
; specify false
to disable depth checking.
With the default, false
:
source = '[0, [1, [2, [3]]]]' ruby = JSON.parse(source) ruby # => [0, [1, [2, [3]]]]
Too deep:
# Raises JSON::NestingError (nesting of 2 is too deep): JSON.parse(source, {max_nesting: 1})
Bad value:
# Raises TypeError (wrong argument type Symbol (expected Fixnum)): JSON.parse(source, {max_nesting: :foo})
Option allow_nan
(boolean) specifies whether to allow NaN
, Infinity
, and MinusInfinity
in source
; defaults to false
.
With the default, false
:
# Raises JSON::ParserError (225: unexpected token at '[NaN]'): JSON.parse('[NaN]') # Raises JSON::ParserError (232: unexpected token at '[Infinity]'): JSON.parse('[Infinity]') # Raises JSON::ParserError (248: unexpected token at '[-Infinity]'): JSON.parse('[-Infinity]')
Allow:
source = '[NaN, Infinity, -Infinity]' ruby = JSON.parse(source, {allow_nan: true}) ruby # => [NaN, Infinity, -Infinity]
Option symbolize_names
(boolean) specifies whether returned Hash keys should be Symbols; defaults to false
(use Strings).
With the default, false
:
source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}' ruby = JSON.parse(source) ruby # => {"a"=>"foo", "b"=>1.0, "c"=>true, "d"=>false, "e"=>nil}
Use Symbols:
ruby = JSON.parse(source, {symbolize_names: true}) ruby # => {:a=>"foo", :b=>1.0, :c=>true, :d=>false, :e=>nil}
Option object_class
(Class) specifies the Ruby
class to be used for each JSON object; defaults to Hash.
With the default, Hash:
source = '{"a": "foo", "b": 1.0, "c": true, "d": false, "e": null}' ruby = JSON.parse(source) ruby.class # => Hash
Use class OpenStruct:
ruby = JSON.parse(source, {object_class: OpenStruct}) ruby # => #<OpenStruct a="foo", b=1.0, c=true, d=false, e=nil>
Option array_class
(Class) specifies the Ruby
class to be used for each JSON array; defaults to Array.
With the default, Array:
source = '["foo", 1.0, true, false, null]' ruby = JSON.parse(source) ruby.class # => Array
Use class Set:
ruby = JSON.parse(source, {array_class: Set}) ruby # => #<Set: {"foo", 1.0, true, false, nil}>
Option create_additions
(boolean) specifies whether to use JSON additions in parsing. See JSON Additions.
To generate a Ruby
String containing JSON data, use method JSON.generate(source, opts)
, where
source
is a Ruby
object.
opts
is a Hash object containing options that control both input allowed and output formatting.
When the source is a Ruby
Array, JSON.generate
returns a String containing a JSON array:
ruby = [0, 's', :foo] json = JSON.generate(ruby) json # => '[0,"s","foo"]'
The Ruby
Array array may contain nested arrays, hashes, and scalars to any depth:
ruby = [0, [1, 2], {foo: 3, bar: 4}] json = JSON.generate(ruby) json # => '[0,[1,2],{"foo":3,"bar":4}]'
When the source is a Ruby
Hash, JSON.generate
returns a String containing a JSON object:
ruby = {foo: 0, bar: 's', baz: :bat} json = JSON.generate(ruby) json # => '{"foo":0,"bar":"s","baz":"bat"}'
The Ruby
Hash array may contain nested arrays, hashes, and scalars to any depth:
ruby = {foo: [0, 1], bar: {baz: 2, bat: 3}, bam: :bad} json = JSON.generate(ruby) json # => '{"foo":[0,1],"bar":{"baz":2,"bat":3},"bam":"bad"}'
When the source is neither an Array nor a Hash, the generated JSON data depends on the class of the source.
When the source is a Ruby
Integer or Float, JSON.generate
returns a String containing a JSON number:
JSON.generate(42) # => '42' JSON.generate(0.42) # => '0.42'
When the source is a Ruby
String, JSON.generate
returns a String containing a JSON string (with double-quotes):
JSON.generate('A string') # => '"A string"'
When the source is true
, false
or nil
, JSON.generate
returns a String containing the corresponding JSON token:
JSON.generate(true) # => 'true' JSON.generate(false) # => 'false' JSON.generate(nil) # => 'null'
When the source is none of the above, JSON.generate
returns a String containing a JSON string representation of the source:
JSON.generate(:foo) # => '"foo"' JSON.generate(Complex(0, 0)) # => '"0+0i"' JSON.generate(Dir.new('.')) # => '"#<Dir>"'
Option allow_nan
(boolean) specifies whether NaN
, Infinity
, and -Infinity
may be generated; defaults to false
.
With the default, false
:
# Raises JSON::GeneratorError (920: NaN not allowed in JSON): JSON.generate(JSON::NaN) # Raises JSON::GeneratorError (917: Infinity not allowed in JSON): JSON.generate(JSON::Infinity) # Raises JSON::GeneratorError (917: -Infinity not allowed in JSON): JSON.generate(JSON::MinusInfinity)
Allow:
ruby = [Float::NaN, Float::Infinity, Float::MinusInfinity] JSON.generate(ruby, allow_nan: true) # => '[NaN,Infinity,-Infinity]'
Option max_nesting
(Integer) specifies the maximum nesting depth in obj
; defaults to 100
.
With the default, 100
:
obj = [[[[[[0]]]]]] JSON.generate(obj) # => '[[[[[[0]]]]]]'
Too deep:
# Raises JSON::NestingError (nesting of 2 is too deep): JSON.generate(obj, max_nesting: 2)
Options script_safe
(boolean) specifies wether '\u2028'
, '\u2029'
and '/'
should be escaped as to make the JSON
object safe to interpolate in script tags.
Options ascii_only
(boolean) specifies wether all characters outside the ASCII range should be escaped.
The default formatting options generate the most compact JSON data, all on one line and with no whitespace.
You can use these formatting options to generate JSON data in a more open format, using whitespace. See also JSON.pretty_generate
.
Option array_nl
(String) specifies a string (usually a newline) to be inserted after each JSON array; defaults to the empty String, ''
.
Option object_nl
(String) specifies a string (usually a newline) to be inserted after each JSON object; defaults to the empty String, ''
.
Option indent
(String) specifies the string (usually spaces) to be used for indentation; defaults to the empty String, ''
; defaults to the empty String, ''
; has no effect unless options array_nl
or object_nl
specify newlines.
Option space
(String) specifies a string (usually a space) to be inserted after the colon in each JSON object’s pair; defaults to the empty String, ''
.
Option space_before
(String) specifies a string (usually a space) to be inserted before the colon in each JSON object’s pair; defaults to the empty String, ''
.
In this example, obj
is used first to generate the shortest JSON data (no whitespace), then again with all formatting options specified:
obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}} json = JSON.generate(obj) puts 'Compact:', json opts = { array_nl: "\n", object_nl: "\n", indent: ' ', space_before: ' ', space: ' ' } puts 'Open:', JSON.generate(obj, opts)
Output:
Compact: {"foo":["bar","baz"],"bat":{"bam":0,"bad":1}} Open: { "foo" : [ "bar", "baz" ], "bat" : { "bam" : 0, "bad" : 1 } }
When you “round trip” a non-String object from Ruby
to JSON and back, you have a new String, instead of the object you began with:
ruby0 = Range.new(0, 2) json = JSON.generate(ruby0) json # => '0..2"' ruby1 = JSON.parse(json) ruby1 # => '0..2' ruby1.class # => String
You can use JSON additions to preserve the original object. The addition is an extension of a ruby class, so that:
JSON.generate stores more information in the JSON string.
JSON.parse, called with option create_additions
, uses that information to create a proper Ruby
object.
This example shows a Range being generated into JSON and parsed back into Ruby
, both without and with the addition for Range:
ruby = Range.new(0, 2) # This passage does not use the addition for Range. json0 = JSON.generate(ruby) ruby0 = JSON.parse(json0) # This passage uses the addition for Range. require 'json/add/range' json1 = JSON.generate(ruby) ruby1 = JSON.parse(json1, create_additions: true) # Make a nice display. display = <<~EOT Generated JSON: Without addition: #{json0} (#{json0.class}) With addition: #{json1} (#{json1.class}) Parsed JSON: Without addition: #{ruby0.inspect} (#{ruby0.class}) With addition: #{ruby1.inspect} (#{ruby1.class}) EOT puts display
This output shows the different results:
Generated JSON: Without addition: "0..2" (String) With addition: {"json_class":"Range","a":[0,2,false]} (String) Parsed JSON: Without addition: "0..2" (String) With addition: 0..2 (Range)
The JSON module includes additions for certain classes. You can also craft custom additions. See Custom JSON Additions.
The JSON module includes additions for certain classes. To use an addition, require
its source:
BigDecimal: require 'json/add/bigdecimal'
Complex: require 'json/add/complex'
Date: require 'json/add/date'
DateTime: require 'json/add/date_time'
Exception: require 'json/add/exception'
OpenStruct: require 'json/add/ostruct'
Range: require 'json/add/range'
Rational: require 'json/add/rational'
Regexp: require 'json/add/regexp'
Set: require 'json/add/set'
Struct: require 'json/add/struct'
Symbol: require 'json/add/symbol'
Time: require 'json/add/time'
To reduce punctuation clutter, the examples below show the generated JSON via puts
, rather than the usual inspect
,
BigDecimal:
require 'json/add/bigdecimal' ruby0 = BigDecimal(0) # 0.0 json = JSON.generate(ruby0) # {"json_class":"BigDecimal","b":"27:0.0"} ruby1 = JSON.parse(json, create_additions: true) # 0.0 ruby1.class # => BigDecimal
Complex:
require 'json/add/complex' ruby0 = Complex(1+0i) # 1+0i json = JSON.generate(ruby0) # {"json_class":"Complex","r":1,"i":0} ruby1 = JSON.parse(json, create_additions: true) # 1+0i ruby1.class # Complex
Date:
require 'json/add/date' ruby0 = Date.today # 2020-05-02 json = JSON.generate(ruby0) # {"json_class":"Date","y":2020,"m":5,"d":2,"sg":2299161.0} ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02 ruby1.class # Date
DateTime:
require 'json/add/date_time' ruby0 = DateTime.now # 2020-05-02T10:38:13-05:00 json = JSON.generate(ruby0) # {"json_class":"DateTime","y":2020,"m":5,"d":2,"H":10,"M":38,"S":13,"of":"-5/24","sg":2299161.0} ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02T10:38:13-05:00 ruby1.class # DateTime
Exception (and its subclasses including RuntimeError):
require 'json/add/exception' ruby0 = Exception.new('A message') # A message json = JSON.generate(ruby0) # {"json_class":"Exception","m":"A message","b":null} ruby1 = JSON.parse(json, create_additions: true) # A message ruby1.class # Exception ruby0 = RuntimeError.new('Another message') # Another message json = JSON.generate(ruby0) # {"json_class":"RuntimeError","m":"Another message","b":null} ruby1 = JSON.parse(json, create_additions: true) # Another message ruby1.class # RuntimeError
OpenStruct:
require 'json/add/ostruct' ruby0 = OpenStruct.new(name: 'Matz', language: 'Ruby') # #<OpenStruct name="Matz", language="Ruby"> json = JSON.generate(ruby0) # {"json_class":"OpenStruct","t":{"name":"Matz","language":"Ruby"}} ruby1 = JSON.parse(json, create_additions: true) # #<OpenStruct name="Matz", language="Ruby"> ruby1.class # OpenStruct
Range:
require 'json/add/range' ruby0 = Range.new(0, 2) # 0..2 json = JSON.generate(ruby0) # {"json_class":"Range","a":[0,2,false]} ruby1 = JSON.parse(json, create_additions: true) # 0..2 ruby1.class # Range
Rational:
require 'json/add/rational' ruby0 = Rational(1, 3) # 1/3 json = JSON.generate(ruby0) # {"json_class":"Rational","n":1,"d":3} ruby1 = JSON.parse(json, create_additions: true) # 1/3 ruby1.class # Rational
Regexp:
require 'json/add/regexp' ruby0 = Regexp.new('foo') # (?-mix:foo) json = JSON.generate(ruby0) # {"json_class":"Regexp","o":0,"s":"foo"} ruby1 = JSON.parse(json, create_additions: true) # (?-mix:foo) ruby1.class # Regexp
Set:
require 'json/add/set' ruby0 = Set.new([0, 1, 2]) # #<Set: {0, 1, 2}> json = JSON.generate(ruby0) # {"json_class":"Set","a":[0,1,2]} ruby1 = JSON.parse(json, create_additions: true) # #<Set: {0, 1, 2}> ruby1.class # Set
Struct:
require 'json/add/struct' Customer = Struct.new(:name, :address) # Customer ruby0 = Customer.new("Dave", "123 Main") # #<struct Customer name="Dave", address="123 Main"> json = JSON.generate(ruby0) # {"json_class":"Customer","v":["Dave","123 Main"]} ruby1 = JSON.parse(json, create_additions: true) # #<struct Customer name="Dave", address="123 Main"> ruby1.class # Customer
Symbol:
require 'json/add/symbol' ruby0 = :foo # foo json = JSON.generate(ruby0) # {"json_class":"Symbol","s":"foo"} ruby1 = JSON.parse(json, create_additions: true) # foo ruby1.class # Symbol
Time:
require 'json/add/time' ruby0 = Time.now # 2020-05-02 11:28:26 -0500 json = JSON.generate(ruby0) # {"json_class":"Time","s":1588436906,"n":840560000} ruby1 = JSON.parse(json, create_additions: true) # 2020-05-02 11:28:26 -0500 ruby1.class # Time
In addition to the JSON additions provided, you can craft JSON additions of your own, either for Ruby
built-in classes or for user-defined classes.
Here’s a user-defined class Foo
:
class Foo attr_accessor :bar, :baz def initialize(bar, baz) self.bar = bar self.baz = baz end end
Here’s the JSON addition for it:
# Extend class Foo with JSON addition. class Foo # Serialize Foo object with its class name and arguments def to_json(*args) { JSON.create_id => self.class.name, 'a' => [ bar, baz ] }.to_json(*args) end # Deserialize JSON string by constructing new Foo object with arguments. def self.json_create(object) new(*object['a']) end end
Demonstration:
require 'json' # This Foo object has no custom addition. foo0 = Foo.new(0, 1) json0 = JSON.generate(foo0) obj0 = JSON.parse(json0) # Lood the custom addition. require_relative 'foo_addition' # This foo has the custom addition. foo1 = Foo.new(0, 1) json1 = JSON.generate(foo1) obj1 = JSON.parse(json1, create_additions: true) # Make a nice display. display = <<~EOT Generated JSON: Without custom addition: #{json0} (#{json0.class}) With custom addition: #{json1} (#{json1.class}) Parsed JSON: Without custom addition: #{obj0.inspect} (#{obj0.class}) With custom addition: #{obj1.inspect} (#{obj1.class}) EOT puts display
Output:
Generated JSON: Without custom addition: "#<Foo:0x0000000006534e80>" (String) With custom addition: {"json_class":"Foo","a":[0,1]} (String) Parsed JSON: Without custom addition: "#<Foo:0x0000000006534e80>" (String) With custom addition: #<Foo:0x0000000006473bb8 @bar=0, @baz=1> (Foo)