Net::HTTP
exception class. You cannot use Net::HTTPExceptions
directly; instead, you must use its subclasses.
This module is responsible for converting the prism syntax tree into other syntax trees.
Mixin methods for install and update options for Gem::Commands
Mixin methods for security option for Gem::Commands
Mixin methods for Gem::Command
to promote available RubyGems update
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.
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:
{Creating Time
objects}[rdoc-ref:Time@Methods+for+Creating].
{Fetching Time
values}[rdoc-ref:Time@Methods+for+Fetching].
{Querying a Time
object}[rdoc-ref:Time@Methods+for+Querying].
{Comparing Time
objects}[rdoc-ref:Time@Methods+for+Comparing].
{Converting a Time
object}[rdoc-ref:Time@Methods+for+Converting].
{Rounding a Time
}.
::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:
.
Argument: a Time-like object.
Returns: 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
.
Argument: a Time-like object.
Returns: 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
.
Argument: a Time-like object.
Returns: 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
.
Argument: a Time-like object.
Returns: whether the time is daylight saving time.
name
:
Called when Marshal.dump(t)
is invoked
Argument: none.
Returns: 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
to_i
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.
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:
IO#eof?
(also aliased as #eof
): Returns whether the stream is at end-of-stream.
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
.
An OpenStruct
is a data structure, similar to a Hash
, that allows the definition of arbitrary attributes with their accompanying values. This is accomplished by using Ruby’s metaprogramming to define methods on the class itself.
require "ostruct" person = OpenStruct.new person.name = "John Smith" person.age = 70 person.name # => "John Smith" person.age # => 70 person.address # => nil
An OpenStruct
employs a Hash
internally to store the attributes and values and can even be initialized with one:
australia = OpenStruct.new(:country => "Australia", :capital => "Canberra") # => #<OpenStruct country="Australia", capital="Canberra">
Hash
keys with spaces or characters that could normally not be used for method calls (e.g. ()[]*
) will not be immediately available on the OpenStruct
object as a method for retrieval or assignment, but can still be reached through the Object#send
method or using [].
measurements = OpenStruct.new("length (in inches)" => 24) measurements[:"length (in inches)"] # => 24 measurements.send("length (in inches)") # => 24 message = OpenStruct.new(:queued? => true) message.queued? # => true message.send("queued?=", false) message.queued? # => false
Removing the presence of an attribute requires the execution of the delete_field
method as setting the property value to nil
will not remove the attribute.
first_pet = OpenStruct.new(:name => "Rowdy", :owner => "John Smith") second_pet = OpenStruct.new(:name => "Rowdy") first_pet.owner = nil first_pet # => #<OpenStruct name="Rowdy", owner=nil> first_pet == second_pet # => false first_pet.delete_field(:owner) first_pet # => #<OpenStruct name="Rowdy"> first_pet == second_pet # => true
Ractor
compatibility: A frozen OpenStruct
with shareable values is itself shareable.
An OpenStruct
utilizes Ruby’s method lookup structure to find and define the necessary methods for properties. This is accomplished through the methods method_missing and define_singleton_method.
This should be a consideration if there is a concern about the performance of the objects that are created, as there is much more overhead in the setting of these properties compared to using a Hash
or a Struct
. Creating an open struct from a small Hash
and accessing a few of the entries can be 200 times slower than accessing the hash directly.
This is a potential security issue; building OpenStruct
from untrusted user data (e.g. JSON
web request) may be susceptible to a “symbol denial of service” attack since the keys create methods and names of methods are never garbage collected.
This may also be the source of incompatibilities between Ruby versions:
o = OpenStruct.new o.then # => nil in Ruby < 2.6, enumerator for Ruby >= 2.6
Builtin methods may be overwritten this way, which may be a source of bugs or security issues:
o = OpenStruct.new o.methods # => [:to_h, :marshal_load, :marshal_dump, :each_pair, ... o.methods = [:foo, :bar] o.methods # => [:foo, :bar]
To help remedy clashes, OpenStruct
uses only protected/private methods ending with !
and defines aliases for builtin public methods by adding a !
:
o = OpenStruct.new(make: 'Bentley', class: :luxury) o.class # => :luxury o.class! # => OpenStruct
It is recommended (but not enforced) to not use fields ending in !
; Note that a subclass’ methods may not be overwritten, nor can OpenStruct’s own methods ending with !
.
For all these reasons, consider not using OpenStruct
at all.
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
, 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
, 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. It’s an explicit blank class.
BasicObject
can be used for creating object hierarchies independent of Ruby’s object hierarchy, proxy objects like the Delegator
class, or other uses where namespace pollution from Ruby’s methods and classes must be avoided.
To avoid polluting BasicObject
for other users an appropriately named subclass of BasicObject
should be created instead of directly modifying BasicObject:
class MyObjectSystem < BasicObject end
BasicObject
does not include Kernel
(for methods like puts
) and BasicObject
is outside of the namespace of the standard library so common classes will not be found without using a full class path.
A variety of strategies can be used to provide useful portions of the standard library to subclasses of BasicObject
. A subclass could include Kernel
to obtain puts
, exit
, etc. A custom Kernel-like module could be created and included or 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
Access to classes and modules from the Ruby standard library can be obtained in a BasicObject
subclass by referencing the desired constant from the root like ::File
or ::Enumerator
. Like method_missing
, const_missing can be used to delegate constant lookup to Object
:
class MyObjectSystem < BasicObject def self.const_missing(name) ::Object.const_get(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.
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.take # 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.take # Here, "I am in Ractor! a=1" is printed
There are two pairs of methods for sending/receiving messages:
Ractor#send
and Ractor.receive
for when the sender knows the receiver (push);
Ractor.yield
and Ractor#take
for when the receiver knows the sender (pull);
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 is sent outside of the ractor as if sent by Ractor.yield
.
A little demonstration of a classic ping-pong:
server = Ractor.new(name: "server") do puts "Server starts: #{self.inspect}" puts "Server sends: ping" Ractor.yield 'ping' # The server doesn't know the receiver and sends to whoever interested received = Ractor.receive # The server doesn't know the sender and receives from whoever sent puts "Server received: #{received}" end client = Ractor.new(server) do |srv| # The server is sent to the client, and available as srv puts "Client starts: #{self.inspect}" received = srv.take # The client takes a message from the server puts "Client received from " \ "#{srv.inspect}: #{received}" puts "Client sends to " \ "#{srv.inspect}: pong" srv.send 'pong' # The client sends a message to the server end [client, server].each(&:take) # Wait until they both finish
This will output something like:
Server starts: #<Ractor:#2 server test.rb:1 running> Server sends: ping Client starts: #<Ractor:#3 test.rb:8 running> Client received from #<Ractor:#2 server test.rb:1 blocking>: ping Client sends to #<Ractor:#2 server test.rb:1 blocking>: pong Server received: pong
Ractors receive their messages via the incoming port, and send them to the outgoing port. Either one can be disabled with Ractor#close_incoming
and Ractor#close_outgoing
, respectively. When a ractor terminates, its ports are closed automatically.
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:
JSON.parse(source, opts)
JSON.parse!(source, opts)
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)
The objspace library extends the ObjectSpace
module and adds several methods to get internal statistic information about object/memory management.
You need to require 'objspace'
to use this extension module.
Generally, you SHOULD NOT use this library if you do not know about the MRI implementation. Mainly, this library is for (memory) profiler developers and MRI developers who need to know about MRI memory usage.
The ObjectSpace
module contains a number of routines that interact with the garbage collection facility and allow you to traverse all living objects with an iterator.
ObjectSpace
also provides support for object finalizers, procs that will be called after a specific object was destroyed by garbage collection. See the documentation for ObjectSpace.define_finalizer
for important information on how to use this method correctly.
a = "A" b = "B" ObjectSpace.define_finalizer(a, proc {|id| puts "Finalizer one on #{id}" }) ObjectSpace.define_finalizer(b, proc {|id| puts "Finalizer two on #{id}" }) a = nil b = nil
produces:
Finalizer two on 537763470 Finalizer one on 537763480
The Benchmark
module provides methods to measure and report the time used to execute Ruby code.
Measure the time to construct the string given by the expression "a"*1_000_000_000
:
require 'benchmark' puts Benchmark.measure { "a"*1_000_000_000 }
On my machine (OSX 10.8.3 on i5 1.7 GHz) this generates:
0.350000 0.400000 0.750000 ( 0.835234)
This report shows the user CPU time, system CPU time, the sum of the user and system CPU times, and the elapsed real time. The unit of time is seconds.
Do some experiments sequentially using the bm
method:
require 'benchmark' n = 5000000 Benchmark.bm do |x| x.report { for i in 1..n; a = "1"; end } x.report { n.times do ; a = "1"; end } x.report { 1.upto(n) do ; a = "1"; end } end
The result:
user system total real 1.010000 0.000000 1.010000 ( 1.014479) 1.000000 0.000000 1.000000 ( 0.998261) 0.980000 0.000000 0.980000 ( 0.981335)
Continuing the previous example, put a label in each report:
require 'benchmark' n = 5000000 Benchmark.bm(7) do |x| x.report("for:") { for i in 1..n; a = "1"; end } x.report("times:") { n.times do ; a = "1"; end } x.report("upto:") { 1.upto(n) do ; a = "1"; end } end
The result:
user system total real for: 1.010000 0.000000 1.010000 ( 1.015688) times: 1.000000 0.000000 1.000000 ( 1.003611) upto: 1.030000 0.000000 1.030000 ( 1.028098)
The times for some benchmarks depend on the order in which items are run. These differences are due to the cost of memory allocation and garbage collection. To avoid these discrepancies, the bmbm
method is provided. For example, to compare ways to sort an array of floats:
require 'benchmark' array = (1..1000000).map { rand } Benchmark.bmbm do |x| x.report("sort!") { array.dup.sort! } x.report("sort") { array.dup.sort } end
The result:
Rehearsal ----------------------------------------- sort! 1.490000 0.010000 1.500000 ( 1.490520) sort 1.460000 0.000000 1.460000 ( 1.463025) -------------------------------- total: 2.960000sec user system total real sort! 1.460000 0.000000 1.460000 ( 1.460465) sort 1.450000 0.010000 1.460000 ( 1.448327)
Report statistics of sequential experiments with unique labels, using the benchmark
method:
require 'benchmark' include Benchmark # we need the CAPTION and FORMAT constants n = 5000000 Benchmark.benchmark(CAPTION, 7, FORMAT, ">total:", ">avg:") do |x| tf = x.report("for:") { for i in 1..n; a = "1"; end } tt = x.report("times:") { n.times do ; a = "1"; end } tu = x.report("upto:") { 1.upto(n) do ; a = "1"; end } [tf+tt+tu, (tf+tt+tu)/3] end
The result:
user system total real for: 0.950000 0.000000 0.950000 ( 0.952039) times: 0.980000 0.000000 0.980000 ( 0.984938) upto: 0.950000 0.000000 0.950000 ( 0.946787) >total: 2.880000 0.000000 2.880000 ( 2.883764) >avg: 0.960000 0.000000 0.960000 ( 0.961255)
Timeout
long-running blocks
require 'timeout' status = Timeout::timeout(5) { # Something that should be interrupted if it takes more than 5 seconds... }
Timeout
provides a way to auto-terminate a potentially long-running operation if it hasn’t finished in a fixed amount of time.
Previous versions didn’t use a module for namespacing, however timeout
is provided for backwards compatibility. You should prefer Timeout.timeout
instead.
© 2000 Network Applied Communication Laboratory, Inc.
© 2000 Information-technology Promotion Agency, Japan
Enumerator::ArithmeticSequence
is a subclass of Enumerator
, that is a representation of sequences of numbers with common difference. Instances of this class can be generated by the Range#step
and Numeric#step
methods.
The class can be used for slicing Array
(see Array#slice
) or custom collections.
A pointer to a C structure
Raised when OLE processing failed.
EX:
obj = WIN32OLE.new("NonExistProgID")
raises the exception:
WIN32OLE::RuntimeError: unknown OLE server: `NonExistProgID' HRESULT error code:0x800401f3 Invalid class string
Subclass of Zlib::Error
When zlib returns a Z_VERSION_ERROR, usually if the zlib library version is incompatible with the version assumed by the caller.