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.1
The initial Stack class is release.
Version
0.0.2
Switched to a linked=list implementation because it is cooler.
Version
0.1.0
Added a depth
method.
Version
1.0.0
Added top
and made pop
return nil (pop
used to return the old top item).
Version
1.1.0
push
now returns the value pushed (it used it return nil).
Version
1.1.1
Fixed a bug in the linked list implementation.
Version
1.1.2
Fixed 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: blog.zenspider.com/2008/10/rubygems-howto-preventing-cata.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.
Mixin module that provides the following:
Access to the CGI
environment variables as methods. See documentation to the CGI
class for a list of these variables. The methods are exposed by removing the leading HTTP_
(if it exists) and downcasing the name. For example, auth_type
will return the environment variable AUTH_TYPE
, and accept
will return the value for HTTP_ACCEPT
.
Access to cookies, including the cookies attribute.
Access to parameters, including the params attribute, and overloading []
to perform parameter value lookup by key.
The initialize_query
method, for initializing the above mechanisms, handling multipart forms, and allowing the class to be used in “offline” mode.
Utility methods for using the RubyGems API.
! –
\private Initializes the world of objects and classes. At first, the function bootstraps the class hierarchy. It initializes the most fundamental classes and their metaclasses. - \c BasicObject - \c Object - \c Module - \c Class After the bootstrap step, the class hierarchy becomes as the following diagram. \image html boottime-classes.png Then, the function defines classes, modules and methods as usual. \ingroup class
++
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.
Time
is an abstraction of dates and times. Time
is stored internally as the number of seconds with subsecond since the Epoch, 1970-01-01 00:00:00 UTC.
The Time
class treats GMT (Greenwich Mean Time
) and UTC (Coordinated Universal Time
) as equivalent. GMT is the older way of referring to these baseline times but persists in the names of calls on POSIX systems.
Note: A Time object uses the resolution available on your system clock.
All times may have subsecond. Be aware of this fact when comparing times with each other – times that are apparently equal when displayed may be different when compared. (Since Ruby 2.7.0, Time#inspect
shows subsecond but Time#to_s
still doesn’t show subsecond.)
All of these examples were done using the EST timezone which is GMT-5.
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
Or a timezone object:
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
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.
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.
A timezone argument must have local_to_utc
and utc_to_local
methods, and may have name
, abbr
, and dst?
methods.
The local_to_utc
method should convert a Time-like object from the timezone to UTC, and utc_to_local
is the opposite. The result also should be a Time
or Time-like object (not necessary to be the same class). The zone
of the result is just ignored. Time-like argument to these methods is similar to a Time
object in UTC without subsecond; it has attribute readers for the parts, e.g. year
, month
, and so on, and epoch time readers, to_i
. The subsecond attributes are fixed as 0, and utc_offset
, zone
, isdst
, and their aliases are same as a Time
object in UTC. Also to_time
, +
, and -
methods are defined.
The name
method is used for marshaling. If this method is not defined on a timezone object, Time
objects using that timezone object can not be dumped by Marshal
.
The abbr
method is used by ‘%Z’ in strftime
.
The dst?
method is called with a Time
value and should return whether the Time
value is in daylight savings time in the zone.
At loading marshaled data, a timezone name will be converted to a timezone object by find_timezone
class method, if the method is defined.
Similarly, that class method will be called when a timezone argument does not have the necessary methods mentioned above.
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.
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.
Expect library adds the IO
instance method expect
, which does similar act to tcl’s expect extension.
In order to use this method, you must require expect:
require 'expect'
Please see expect
for usage.
The IO
class is the basis for all input and output in Ruby. An I/O stream may be duplexed (that is, bidirectional), and so may use more than one native operating system stream.
Many of the examples in this section use the File
class, the only standard subclass of IO
. The two classes are closely associated. Like the File
class, the Socket
library subclasses from IO
(such as TCPSocket
or UDPSocket
).
The Kernel#open
method can create an IO
(or File
) object for these types of arguments:
A plain string represents a filename suitable for the underlying operating system.
A string starting with "|"
indicates a subprocess. The remainder of the string following the "|"
is invoked as a process with appropriate input/output channels connected to it.
A string equal to "|-"
will create another Ruby instance as a subprocess.
The IO
may be opened with different file modes (read-only, write-only) and encodings for proper conversion. See IO.new
for these options. See Kernel#open
for details of the various command formats described above.
IO.popen
, the Open3
library, or Process#spawn may also be used to communicate with subprocesses through an IO
.
Ruby will convert pathnames between different operating system conventions if possible. For instance, on a Windows system the filename "/gumby/ruby/test.rb"
will be opened as "\gumby\ruby\test.rb"
. When specifying a Windows-style filename in a Ruby string, remember to escape the backslashes:
"C:\\gumby\\ruby\\test.rb"
Our examples here will use the Unix-style forward slashes; File::ALT_SEPARATOR can be used to get the platform-specific separator character.
The global constant ARGF
(also accessible as $<
) provides an IO-like stream which allows access to all files mentioned on the command line (or STDIN if no files are mentioned). ARGF#path
and its alias ARGF#filename
are provided to access the name of the file currently being read.
The io/console extension provides methods for interacting with the console. The console can be accessed from IO.console
or the standard input/output/error IO
objects.
Requiring io/console adds the following methods:
Example:
require 'io/console' rows, columns = $stdout.winsize puts "Your screen is #{columns} wide and #{rows} tall"
Many examples here use these filenames and their corresponding files:
t.txt
: A text-only file that is assumed to exist via:
text = <<~EOT This is line one. This is the second line. This is the third line. EOT File.write('t.txt', text)
t.dat
: A data file that is assumed to exist via:
data = "\u9990\u9991\u9992\u9993\u9994" f = File.open('t.dat', 'wb:UTF-16') f.write(data) f.close
t.rus
: A Russian-language text file that is assumed to exist via:
File.write('t.rus', "\u{442 435 441 442}")
t.tmp
: A file that is assumed not to exist.
A number of IO method calls must or may specify a mode for the stream; the mode determines how stream is to be accessible, including:
Whether the stream is to be read-only, write-only, or read-write.
Whether the stream is positioned at its beginning or its end.
Whether the stream treats data as text-only or binary.
The external and internal encodings.
When mode
is an integer it must be one or more (combined by bitwise OR (|
) of the modes defined in File::Constants
:
File::RDONLY
: Open for reading only.
File::WRONLY
: Open for writing only.
File::RDWR
: Open for reading and writing.
File::APPEND
: Open for appending only.
File::CREAT
: Create file if it does not exist.
File::EXCL
: Raise an exception if File::CREAT
is given and the file exists.
Examples:
File.new('t.txt', File::RDONLY) File.new('t.tmp', File::RDWR | File::CREAT | File::EXCL)
Note: Method
IO#set_encoding
does not allow the mode to be specified as an integer.
When mode
is a string it must begin with one of the following:
'r'
: Read-only stream, positioned at the beginning; the stream cannot be changed to writable.
'w'
: Write-only stream, positioned at the beginning; the stream cannot be changed to readable.
'a'
: Write-only stream, positioned at the end; every write appends to the end; the stream cannot be changed to readable.
'r+'
: Read-write stream, positioned at the beginning.
'w+'
: Read-write stream, positioned at the end.
'a+'
: Read-write stream, positioned at the end.
For a writable file stream (that is, any except read-only), the file is truncated to zero if it exists, and is created if it does not exist.
Examples:
File.open('t.txt', 'r') File.open('t.tmp', 'w')
Either of the following may be suffixed to any of the above:
't'
: Text data; sets the default external encoding to Encoding::UTF_8
; on Windows, enables conversion between EOL and CRLF.
'b'
: Binary data; sets the default external encoding to Encoding::ASCII_8BIT
; on Windows, suppresses conversion between EOL and CRLF.
If neither is given, the stream defaults to text data.
Examples:
File.open('t.txt', 'rt') File.open('t.dat', 'rb')
The following may be suffixed to any writable mode above:
'x'
: Creates the file if it does not exist; raises an exception if the file exists.
Example:
File.open('t.tmp', 'wx')
Finally, the mode string may specify encodings – either external encoding only or both external and internal encodings – by appending one or both encoding names, separated by colons:
f = File.new('t.dat', 'rb') f.external_encoding # => #<Encoding:ASCII-8BIT> f.internal_encoding # => nil f = File.new('t.dat', 'rb:UTF-16') f.external_encoding # => #<Encoding:UTF-16 (dummy)> f.internal_encoding # => nil f = File.new('t.dat', 'rb:UTF-16:UTF-16') f.external_encoding # => #<Encoding:UTF-16 (dummy)> f.internal_encoding # => #<Encoding:UTF-16>
The numerous encoding names are available in array Encoding.name_list
:
Encoding.name_list.size # => 175 Encoding.name_list.take(3) # => ["ASCII-8BIT", "UTF-8", "US-ASCII"]
When the external encoding is set, strings read are tagged by that encoding when reading, and strings written are converted to that encoding when writing.
When both external and internal encodings are set, strings read are converted from external to internal encoding, and strings written are converted from internal to external encoding. For further details about transcoding input and output, see Encoding
.
If the external encoding is 'BOM|UTF-8'
, 'BOM|UTF-16LE'
or 'BOM|UTF16-BE'
, Ruby checks for a Unicode BOM in the input document to help determine the encoding. For UTF-16 encodings the file open mode must be binary. If the BOM is found, it is stripped and the external encoding from the BOM is used.
Note that the BOM-style encoding option is case insensitive, so ‘bom|utf-8’ is also valid.)
A number of IO methods accept an optional parameter opts
, which determines 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.
Also available are the options offered in String#encode
, which may control conversion between external internal encoding.
A number of IO methods accept optional keyword arguments that determine how a stream is to be treated:
:chomp
: If true
, line separators are omitted; default is false
.
An IO stream has a position, which is the non-negative integer offset (in bytes) in the stream where the next read or write will occur.
Note that a text stream may have multi-byte characters, so a text stream whose position is n
(bytes) may not have n
characters preceding the current position – there may be fewer.
A new stream is initially positioned:
At the beginning (position 0
) if its mode is 'r'
, 'w'
, or 'r+'
.
At the end (position self.size
) if its mode is 'a'
, 'w+'
, or 'a+'
.
Methods to query the position:
IO#tell
and its alias IO#pos
return the position for an open stream.
IO#eof?
and its alias IO#eof
return whether the position is at the end of a readable stream.
Reading from a stream usually changes its position:
f = File.open('t.txt') f.tell # => 0 f.readline # => "This is line one.\n" f.tell # => 19 f.readline # => "This is the second line.\n" f.tell # => 45 f.eof? # => false f.readline # => "Here's the third line.\n" f.eof? # => true
Writing to a stream usually changes its position:
f = File.open('t.tmp', 'w') f.tell # => 0 f.write('foo') # => 3 f.tell # => 3 f.write('bar') # => 3 f.tell # => 6
Iterating over a stream usually changes its position:
f = File.open('t.txt') f.each do |line| p "position=#{f.pos} eof?=#{f.eof?} line=#{line}" end
Output:
"position=19 eof?=false line=This is line one.\n" "position=45 eof?=false line=This is the second line.\n" "position=70 eof?=true line=This is the third line.\n"
The position may also be changed by certain other methods:
IO#pos=
and IO#seek
change the position to a specified offset.
IO#rewind
changes the position to the beginning.
A readable IO stream has a line number, which is the non-negative integer line number in the stream where the next read will occur.
A new stream is initially has line number 0
.
Method IO#lineno
returns the line number.
Reading lines from a stream usually changes its line number:
f = File.open('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
Iterating over lines in a stream usually changes its line number:
f = File.open('t.txt') f.each_line do |line| p "position=#{f.pos} eof?=#{f.eof?} line=#{line}" end
Output:
"position=19 eof?=false line=This is line one.\n" "position=45 eof?=false line=This is the second line.\n" "position=70 eof?=true line=This is the third line.\n"
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:
::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.
readline
Returns the next line read from self
; same as getline, but raises an exception of end-of-file.
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=
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_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.
external_encoding
Returns the external encoding object for self
.
internal_encoding
Returns the internal encoding object for self
.
stat
Returns the File::Stat
object containing status information for self
.
sync
Returns whether self
is in sync-mode.
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.
UNIXServer
represents a UNIX domain stream server socket.
UNIXSocket
represents a UNIX domain stream client socket.
Pseudo I/O on String
object, with interface corresponding to IO
.
Commonly used to simulate $stdio
or $stderr
require 'stringio' # Writing stream emulation io = StringIO.new io.puts "Hello World" io.string #=> "Hello World\n" # Reading stream emulation io = StringIO.new "first\nsecond\nlast\n" io.getc #=> "f" io.gets #=> "irst\n" io.read #=> "second\nlast\n"
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) or super 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.
Returns the integer object identifier for self
.
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
Method
called when an undefined method is called on self
.
singleton_method_added
Method
called when a singleton method is added to self
.
singleton_method_removed
Method
called when a singleton method is added removed from self
.
singleton_method_undefined
Method
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
The GetoptLong
class allows you to parse command line options similarly to the GNU getopt_long() C library call. Note, however, that GetoptLong
is a pure Ruby implementation.
GetoptLong
allows for POSIX-style options like --file
as well as single letter options like -f
The empty option --
(two minus symbols) is used to end option processing. This can be particularly important if options have optional arguments.
Here is a simple example of usage:
require 'getoptlong' opts = GetoptLong.new( [ '--help', '-h', GetoptLong::NO_ARGUMENT ], [ '--repeat', '-n', GetoptLong::REQUIRED_ARGUMENT ], [ '--name', GetoptLong::OPTIONAL_ARGUMENT ] ) dir = nil name = nil repetitions = 1 opts.each do |opt, arg| case opt when '--help' puts <<-EOF hello [OPTION] ... DIR -h, --help: show help --repeat x, -n x: repeat x times --name [name]: greet user by name, if name not supplied default is John DIR: The directory in which to issue the greeting. EOF when '--repeat' repetitions = arg.to_i when '--name' if arg == '' name = 'John' else name = arg end end end if ARGV.length != 1 puts "Missing dir argument (try --help)" exit 0 end dir = ARGV.shift Dir.chdir(dir) for i in (1..repetitions) print "Hello" if name print ", #{name}" end puts end
Example command line:
hello -n 6 --name -- /tmp