Class Exception
and its subclasses are used to communicate between Kernel#raise
and rescue
statements in begin ... end
blocks.
An Exception
object carries information about an exception:
Its type (the exception’s class).
An optional descriptive message.
Optional backtrace information.
Some built-in subclasses of Exception
have additional methods: e.g., NameError#name
.
Two Ruby statements have default exception classes:
raise
: defaults to RuntimeError
.
rescue
: defaults to StandardError
.
When an exception has been raised but not yet handled (in rescue
, ensure
, at_exit
and END
blocks), two global variables are set:
$!
contains the current exception.
$@
contains its backtrace.
To provide additional or alternate information, a program may create custom exception classes that derive from the built-in exception classes.
A good practice is for a library to create a single “generic” exception class (typically a subclass of StandardError
or RuntimeError
) and have its other exception classes derive from that class. This allows the user to rescue the generic exception, thus catching all exceptions the library may raise even if future versions of the library add new exception subclasses.
For example:
class MyLibrary class Error < ::StandardError end class WidgetError < Error end class FrobError < Error end end
To handle both MyLibrary::WidgetError and MyLibrary::FrobError the library user can rescue MyLibrary::Error.
Exception
Classes The built-in subclasses of Exception
are:
fatal
Raised when a signal is received.
begin Process.kill('HUP',Process.pid) sleep # wait for receiver to handle signal sent by Process.kill rescue SignalException => e puts "received Exception #{e}" end
produces:
received Exception SIGHUP
Raised when attempting to divide an integer by 0.
42 / 0 #=> ZeroDivisionError: divided by 0
Note that only division by an exact 0 will raise the exception:
42 / 0.0 #=> Float::INFINITY 42 / -0.0 #=> -Float::INFINITY 0 / 0.0 #=> NaN
A rational number can be represented as a pair of integer numbers: a/b (b>0), where a is the numerator and b is the denominator. Integer
a equals rational a/1 mathematically.
You can create a Rational object explicitly with:
A rational literal.
You can convert certain objects to Rationals with:
Method Rational
.
Examples
Rational(1) #=> (1/1) Rational(2, 3) #=> (2/3) Rational(4, -6) #=> (-2/3) # Reduced. 3.to_r #=> (3/1) 2/3r #=> (2/3)
You can also create rational objects from floating-point numbers or strings.
Rational(0.3) #=> (5404319552844595/18014398509481984) Rational('0.3') #=> (3/10) Rational('2/3') #=> (2/3) 0.3.to_r #=> (5404319552844595/18014398509481984) '0.3'.to_r #=> (3/10) '2/3'.to_r #=> (2/3) 0.3.rationalize #=> (3/10)
A rational object is an exact number, which helps you to write programs without any rounding errors.
10.times.inject(0) {|t| t + 0.1 } #=> 0.9999999999999999 10.times.inject(0) {|t| t + Rational('0.1') } #=> (1/1)
However, when an expression includes an inexact component (numerical value or operation), it will produce an inexact result.
Rational(10) / 3 #=> (10/3) Rational(10) / 3.0 #=> 3.3333333333333335 Rational(-8) ** Rational(1, 3) #=> (1.0000000000000002+1.7320508075688772i)
TCPServer
represents a TCP/IP server socket.
A simple TCP server may look like:
require 'socket' server = TCPServer.new 2000 # Server bind to port 2000 loop do client = server.accept # Wait for a client to connect client.puts "Hello !" client.puts "Time is #{Time.now}" client.close end
A more usable server (serving multiple clients):
require 'socket' server = TCPServer.new 2000 loop do Thread.start(server.accept) do |client| client.puts "Hello !" client.puts "Time is #{Time.now}" client.close end end
UNIXServer
represents a UNIX domain stream server socket.
Raised when OLE processing failed.
EX:
obj = WIN32OLE.new("NonExistProgID")
raises the exception:
WIN32OLERuntimeError: unknown OLE server: `NonExistProgID' HRESULT error code:0x800401f3 Invalid class string
Class GetoptLong provides parsing both for options and for regular arguments.
Using GetoptLong, you can define options for your program. The program can then capture and respond to whatever options are included in the command that executes the program.
A simple example: file simple.rb
:
require 'getoptlong' options = GetoptLong.new( ['--number', '-n', GetoptLong::REQUIRED_ARGUMENT], ['--verbose', '-v', GetoptLong::OPTIONAL_ARGUMENT], ['--help', '-h', GetoptLong::NO_ARGUMENT] )
If you are somewhat familiar with options, you may want to skip to this full example.
A GetoptLong option has:
A string option name.
Zero or more string aliases for the name.
An option type.
Options may be defined by calling singleton method GetoptLong.new
, which returns a new GetoptLong object. Options may then be processed by calling other methods such as GetoptLong#each
.
In the array that defines an option, the first element is the string option name. Often the name takes the ‘long’ form, beginning with two hyphens.
The option name may have any number of aliases, which are defined by additional string elements.
The name and each alias must be of one of two forms:
Two hyphens, followed by one or more letters.
One hyphen, followed by a single letter.
File
aliases.rb
:
require 'getoptlong' options = GetoptLong.new( ['--xxx', '-x', '--aaa', '-a', '-p', GetoptLong::NO_ARGUMENT] ) options.each do |option, argument| p [option, argument] end
An option may be cited by its name, or by any of its aliases; the parsed option always reports the name, not an alias:
$ ruby aliases.rb -a -p --xxx --aaa -x
Output:
["--xxx", ""] ["--xxx", ""] ["--xxx", ""] ["--xxx", ""] ["--xxx", ""]
An option may also be cited by an abbreviation of its name or any alias, as long as that abbreviation is unique among the options.
File
abbrev.rb
:
require 'getoptlong' options = GetoptLong.new( ['--xxx', GetoptLong::NO_ARGUMENT], ['--xyz', GetoptLong::NO_ARGUMENT] ) options.each do |option, argument| p [option, argument] end
Command line:
$ ruby abbrev.rb --xxx --xx --xyz --xy
Output:
["--xxx", ""] ["--xxx", ""] ["--xyz", ""] ["--xyz", ""]
This command line raises GetoptLong::AmbiguousOption
:
$ ruby abbrev.rb --x
An option may be cited more than once:
$ ruby abbrev.rb --xxx --xyz --xxx --xyz
Output:
["--xxx", ""] ["--xyz", ""] ["--xxx", ""] ["--xyz", ""]
A option-like token that appears anywhere after the token --
is treated as an ordinary argument, and is not processed as an option:
$ ruby abbrev.rb --xxx --xyz -- --xxx --xyz
Output:
["--xxx", ""] ["--xyz", ""]
Each option definition includes an option type, which controls whether the option takes an argument.
File
types.rb
:
require 'getoptlong' options = GetoptLong.new( ['--xxx', GetoptLong::REQUIRED_ARGUMENT], ['--yyy', GetoptLong::OPTIONAL_ARGUMENT], ['--zzz', GetoptLong::NO_ARGUMENT] ) options.each do |option, argument| p [option, argument] end
Note that an option type has to do with the option argument (whether it is required, optional, or forbidden), not with whether the option itself is required.
An option of type GetoptLong::REQUIRED_ARGUMENT
must be followed by an argument, which is associated with that option:
$ ruby types.rb --xxx foo
Output:
["--xxx", "foo"]
If the option is not last, its argument is whatever follows it (even if the argument looks like another option):
$ ruby types.rb --xxx --yyy
Output:
["--xxx", "--yyy"]
If the option is last, an exception is raised:
$ ruby types.rb # Raises GetoptLong::MissingArgument
An option of type GetoptLong::OPTIONAL_ARGUMENT
may be followed by an argument, which if given is associated with that option.
If the option is last, it does not have an argument:
$ ruby types.rb --yyy
Output:
["--yyy", ""]
If the option is followed by another option, it does not have an argument:
$ ruby types.rb --yyy --zzz
Output:
["--yyy", ""] ["--zzz", ""]
Otherwise the option is followed by its argument, which is associated with that option:
$ ruby types.rb --yyy foo
Output:
["--yyy", "foo"]
An option of type GetoptLong::NO_ARGUMENT
takes no argument:
ruby types.rb --zzz foo
Output:
["--zzz", ""]
You can process options either with method each
and a block, or with method get
.
During processing, each found option is removed, along with its argument if there is one. After processing, each remaining element was neither an option nor the argument for an option.
File
argv.rb
:
require 'getoptlong' options = GetoptLong.new( ['--xxx', GetoptLong::REQUIRED_ARGUMENT], ['--yyy', GetoptLong::OPTIONAL_ARGUMENT], ['--zzz', GetoptLong::NO_ARGUMENT] ) puts "Original ARGV: #{ARGV}" options.each do |option, argument| p [option, argument] end puts "Remaining ARGV: #{ARGV}"
Command line:
$ ruby argv.rb --xxx Foo --yyy Bar Baz --zzz Bat Bam
Output:
Original ARGV: ["--xxx", "Foo", "--yyy", "Bar", "Baz", "--zzz", "Bat", "Bam"] ["--xxx", "Foo"] ["--yyy", "Bar"] ["--zzz", ""] Remaining ARGV: ["Baz", "Bat", "Bam"]
There are three settings that control the way the options are interpreted:
PERMUTE
.
REQUIRE_ORDER
.
RETURN_IN_ORDER
.
The initial setting for a new GetoptLong object is REQUIRE_ORDER
if environment variable POSIXLY_CORRECT
is defined, PERMUTE
otherwise.
In the PERMUTE
ordering, options and other, non-option, arguments may appear in any order and any mixture.
File
permute.rb
:
require 'getoptlong' options = GetoptLong.new( ['--xxx', GetoptLong::REQUIRED_ARGUMENT], ['--yyy', GetoptLong::OPTIONAL_ARGUMENT], ['--zzz', GetoptLong::NO_ARGUMENT] ) puts "Original ARGV: #{ARGV}" options.each do |option, argument| p [option, argument] end puts "Remaining ARGV: #{ARGV}"
Command line:
$ ruby permute.rb Foo --zzz Bar --xxx Baz --yyy Bat Bam --xxx Bag Bah
Output:
Original ARGV: ["Foo", "--zzz", "Bar", "--xxx", "Baz", "--yyy", "Bat", "Bam", "--xxx", "Bag", "Bah"] ["--zzz", ""] ["--xxx", "Baz"] ["--yyy", "Bat"] ["--xxx", "Bag"] Remaining ARGV: ["Foo", "Bar", "Bam", "Bah"]
In the REQUIRE_ORDER
ordering, all options precede all non-options; that is, each word after the first non-option word is treated as a non-option word (even if it begins with a hyphen).
File
require_order.rb
:
require 'getoptlong' options = GetoptLong.new( ['--xxx', GetoptLong::REQUIRED_ARGUMENT], ['--yyy', GetoptLong::OPTIONAL_ARGUMENT], ['--zzz', GetoptLong::NO_ARGUMENT] ) options.ordering = GetoptLong::REQUIRE_ORDER puts "Original ARGV: #{ARGV}" options.each do |option, argument| p [option, argument] end puts "Remaining ARGV: #{ARGV}"
Command line:
$ ruby require_order.rb --xxx Foo Bar --xxx Baz --yyy Bat -zzz
Output:
Original ARGV: ["--xxx", "Foo", "Bar", "--xxx", "Baz", "--yyy", "Bat", "-zzz"] ["--xxx", "Foo"] Remaining ARGV: ["Bar", "--xxx", "Baz", "--yyy", "Bat", "-zzz"]
In the RETURN_IN_ORDER
ordering, every word is treated as an option. A word that begins with a hyphen (or two) is treated in the usual way; a word word
that does not so begin is treated as an option whose name is an empty string, and whose value is word
.
File
return_in_order.rb
:
require 'getoptlong' options = GetoptLong.new( ['--xxx', GetoptLong::REQUIRED_ARGUMENT], ['--yyy', GetoptLong::OPTIONAL_ARGUMENT], ['--zzz', GetoptLong::NO_ARGUMENT] ) options.ordering = GetoptLong::RETURN_IN_ORDER puts "Original ARGV: #{ARGV}" options.each do |option, argument| p [option, argument] end puts "Remaining ARGV: #{ARGV}"
Command line:
$ ruby return_in_order.rb Foo --xxx Bar Baz --zzz Bat Bam
Output:
Original ARGV: ["Foo", "--xxx", "Bar", "Baz", "--zzz", "Bat", "Bam"] ["", "Foo"] ["--xxx", "Bar"] ["", "Baz"] ["--zzz", ""] ["", "Bat"] ["", "Bam"] Remaining ARGV: []
File
fibonacci.rb
:
require 'getoptlong' options = GetoptLong.new( ['--number', '-n', GetoptLong::REQUIRED_ARGUMENT], ['--verbose', '-v', GetoptLong::OPTIONAL_ARGUMENT], ['--help', '-h', GetoptLong::NO_ARGUMENT] ) def help(status = 0) puts <<~HELP Usage: -n n, --number n: Compute Fibonacci number for n. -v [boolean], --verbose [boolean]: Show intermediate results; default is 'false'. -h, --help: Show this help. HELP exit(status) end def print_fibonacci (number) return 0 if number == 0 return 1 if number == 1 or number == 2 i = 0 j = 1 (2..number).each do k = i + j i = j j = k puts j if @verbose end puts j unless @verbose end options.each do |option, argument| case option when '--number' @number = argument.to_i when '--verbose' @verbose = if argument.empty? true elsif argument.match(/true/i) true elsif argument.match(/false/i) false else puts '--verbose argument must be true or false' help(255) end when '--help' help end end unless @number puts 'Option --number is required.' help(255) end print_fibonacci(@number)
Command line:
$ ruby fibonacci.rb
Output:
Option --number is required. Usage: -n n, --number n: Compute Fibonacci number for n. -v [boolean], --verbose [boolean]: Show intermediate results; default is 'false'. -h, --help: Show this help.
Command line:
$ ruby fibonacci.rb --number
Raises GetoptLong::MissingArgument
:
fibonacci.rb: option `--number' requires an argument
Command line:
$ ruby fibonacci.rb --number 6
Output:
8
Command line:
$ ruby fibonacci.rb --number 6 --verbose
Output:
1 2 3 5 8
Command line:
$ ruby fibonacci.rb –number 6 –verbose yes
Output:
--verbose argument must be true or false Usage: -n n, --number n: Compute Fibonacci number for n. -v [boolean], --verbose [boolean]: Show intermediate results; default is 'false'. -h, --help: Show this help.
Raised when an invalid operation is attempted on a Fiber
, in particular when attempting to call/resume a dead fiber, attempting to yield from the root fiber, or calling a fiber across threads.
fiber = Fiber.new{} fiber.resume #=> nil fiber.resume #=> FiberError: dead fiber called
Raised when the interrupt signal is received, typically because the user has pressed Control-C (on most posix platforms). As such, it is a subclass of SignalException
.
begin puts "Press ctrl-C when you get bored" loop {} rescue Interrupt => e puts "Note: You will typically use Signal.trap instead." end
produces:
Press ctrl-C when you get bored
then waits until it is interrupted with Control-C and then prints:
Note: You will typically use Signal.trap instead.
The most standard error types are subclasses of StandardError
. A rescue clause without an explicit Exception
class will rescue all StandardErrors (and only those).
def foo raise "Oups" end foo rescue "Hello" #=> "Hello"
On the other hand:
require 'does/not/exist' rescue "Hi"
raises the exception:
LoadError: no such file to load -- does/not/exist
Raised when the arguments are wrong and there isn’t a more specific Exception
class.
Ex: passing the wrong number of arguments
[1, 2, 3].first(4, 5)
raises the exception:
ArgumentError: wrong number of arguments (given 2, expected 1)
Ex: passing an argument that is not acceptable:
[1, 2, 3].first(-4)
raises the exception:
ArgumentError: negative array size
ScriptError
is the superclass for errors raised when a script can not be executed because of a LoadError
, NotImplementedError
or a SyntaxError
. Note these type of ScriptErrors
are not StandardError
and will not be rescued unless it is specified explicitly (or its ancestor Exception
).
Raised when a feature is not implemented on the current platform. For example, methods depending on the fsync
or fork
system calls may raise this exception if the underlying operating system or Ruby runtime does not support them.
Note that if fork
raises a NotImplementedError
, then respond_to?(:fork)
returns false
.
A generic error class raised when an invalid operation is attempted. Kernel#raise
will raise a RuntimeError
if no Exception
class is specified.
raise "ouch"
raises the exception:
RuntimeError: ouch
No longer used by internal code.
OLEProperty
helper class of Property with 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
The exception class which will be raised when pushing into a closed Queue. See Thread::Queue#close
and Thread::SizedQueue#close
.
The Comparable
mixin is used by classes whose objects may be ordered. The class must define the <=>
operator, which compares the receiver against another object, returning a value less than 0, returning 0, or returning a value greater than 0, depending on whether the receiver is less than, equal to, or greater than the other object. If the other object is not comparable then the <=>
operator should return nil
. Comparable
uses <=>
to implement the conventional comparison operators (<
, <=
, ==
, >=
, and >
) and the method between?
.
class SizeMatters include Comparable attr :str def <=>(other) str.size <=> other.str.size end def initialize(str) @str = str end def inspect @str end end s1 = SizeMatters.new("Z") s2 = SizeMatters.new("YY") s3 = SizeMatters.new("XXX") s4 = SizeMatters.new("WWWW") s5 = SizeMatters.new("VVVVV") s1 < s2 #=> true s4.between?(s1, s3) #=> false s4.between?(s3, s5) #=> true [ s3, s2, s5, s4, s1 ].sort #=> [Z, YY, XXX, WWWW, VVVVV]
Module Comparable provides these methods, all of which use method <=>
:
<
: Returns whether self
is less than the given object.
<=
: Returns whether self
is less than or equal to the given object.
==
: Returns whether self
is equal to the given object.
>
: Returns whether self
is greater than or equal to the given object.
>=
: Returns whether self
is greater than the given object.
between?
: Returns true
if self
is between two given objects.
clamp
: For given objects min
and max
, or range (min..max)
, returns:
min
if (self <=> min) < 0
.
max
if (self <=> max) > 0
.
self
otherwise.
The Observer pattern (also known as publish/subscribe) provides a simple mechanism for one object to inform a set of interested third-party objects when its state changes.
The notifying class mixes in the Observable
module, which provides the methods for managing the associated observer objects.
The observable object must:
assert that it has #changed
call #notify_observers
An observer subscribes to updates using Observable#add_observer
, which also specifies the method called via notify_observers
. The default method for notify_observers
is update.
The following example demonstrates this nicely. A Ticker
, when run, continually receives the stock Price
for its @symbol
. A Warner
is a general observer of the price, and two warners are demonstrated, a WarnLow
and a WarnHigh
, which print a warning if the price is below or above their set limits, respectively.
The update
callback allows the warners to run without being explicitly called. The system is set up with the Ticker
and several observers, and the observers do their duty without the top-level code having to interfere.
Note that the contract between publisher and subscriber (observable and observer) is not declared or enforced. The Ticker
publishes a time and a price, and the warners receive that. But if you don’t ensure that your contracts are correct, nothing else can warn you.
require "observer" class Ticker ### Periodically fetch a stock price. include Observable def initialize(symbol) @symbol = symbol end def run last_price = nil loop do price = Price.fetch(@symbol) print "Current price: #{price}\n" if price != last_price changed # notify observers last_price = price notify_observers(Time.now, price) end sleep 1 end end end class Price ### A mock class to fetch a stock price (60 - 140). def self.fetch(symbol) 60 + rand(80) end end class Warner ### An abstract observer of Ticker objects. def initialize(ticker, limit) @limit = limit ticker.add_observer(self) end end class WarnLow < Warner def update(time, price) # callback for observer if price < @limit print "--- #{time.to_s}: Price below #@limit: #{price}\n" end end end class WarnHigh < Warner def update(time, price) # callback for observer if price > @limit print "+++ #{time.to_s}: Price above #@limit: #{price}\n" end end end ticker = Ticker.new("MSFT") WarnLow.new(ticker, 80) WarnHigh.new(ticker, 120) ticker.run
Produces:
Current price: 83 Current price: 75 --- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 75 Current price: 90 Current price: 134 +++ Sun Jun 09 00:10:25 CDT 2002: Price above 120: 134 Current price: 134 Current price: 112 Current price: 79 --- Sun Jun 09 00:10:25 CDT 2002: Price below 80: 79
The #notify_observers
method can also be used with +proc+s by using the :call
as func
parameter.
The following example illustrates the use of a lambda:
require 'observer' class Ticker include Observable def run # logic to retrieve the price (here 77.0) changed notify_observers(77.0) end end ticker = Ticker.new warner = ->(price) { puts "New price received: #{price}" } ticker.add_observer(warner, :call) ticker.run