Exceptions

Ruby code can raise exceptions.

Most often, a raised exception is meant to alert the running program that an unusual (i.e.,exceptional) situation has arisen, and may need to be handled.

Code throughout the Ruby core, Ruby standard library, and Ruby gems generates exceptions in certain circumstances:

File.open('nope.txt')# Raises Errno::ENOENT: "No such file or directory"

Raised Exceptions

A raised exception transfers program execution, one way or another.

Unrescued Exceptions

If an exception notrescued (seeRescued Exceptions below), execution transfers to code in the Ruby interpreter that prints a message and exits the program (or thread):

$ ruby -e "raise"-e:1:in '<main>': unhandled exception

Rescued Exceptions

Anexception handler may determine what is to happen when an exception is raised; the handler mayrescue an exception, and may prevent the program from exiting.

A simple example:

beginraise'Boom!'# Raises an exception, transfers control.puts'Will not get here.'rescueputs'Rescued an exception.'# Control transferred to here; program does not exit.endputs'Got here.'

Output:

Rescued an exception.Got here.

An exception handler has several elements:

ElementUse
Begin clause.Begins the handler and contains the code whose raised exception, if any, may be rescued.
One or more rescue clauses.Each contains “rescuing” code, which is to be executed for certain exceptions.
Else clause (optional).Contains code to be executed if no exception is raised.
Ensure clause (optional).Contains code to be executed whether or not an exception is raised, or is rescued.
end statement.Ends the handler. ‘

Begin Clause

The begin clause begins the exception handler:

Rescue Clauses

A rescue clause:

Rescued Exceptions

Arescue statement may include one or more classes that are to be rescued; if none is given,StandardError is assumed.

The rescue clause rescues both the specified class (orStandardError if none given) or any of its subclasses; seeBuilt-In Exception Class Hierarchy.

begin1/0# Raises ZeroDivisionError, a subclass of StandardError.rescueputs"Rescued #{$!.class}"end

Output:

RescuedZeroDivisionError

If therescue statement specifies an exception class, only that class (or one of its subclasses) is rescued; this example exits with aZeroDivisionError, which was not rescued because it is notArgumentError or one of its subclasses:

begin1/0rescueArgumentErrorputs"Rescued #{$!.class}"end

Arescue statement may specify multiple classes, which means that its code rescues an exception of any of the given classes (or their subclasses):

begin1/0rescueFloatDomainError,ZeroDivisionErrorputs"Rescued #{$!.class}"end
Multiple Rescue Clauses

An exception handler may contain multiple rescue clauses; in that case, the first clause that rescues the exception does so, and those before and after are ignored:

beginDir.open('nosuch')rescueErrno::ENOTDIRputs"Rescued #{$!.class}"rescueErrno::ENOENTputs"Rescued #{$!.class}"end

Output:

RescuedErrno::ENOENT
Capturing the Rescued Exception

Arescue statement may specify a variable whose value becomes the rescued exception (an instance ofException or one of its subclasses:

begin1/0rescue=>xputsx.classputsx.messageend

Output:

ZeroDivisionErrordividedby0
Global Variables

Two read-only global variables always havenil value except in a rescue clause; they’re:

Example:

begin1/0rescuep$!p$@end

Output:

#<ZeroDivisionError: divided by 0>["t.rb:2:in 'Integer#/'","t.rb:2:in '<main>'"]
Cause

In a rescue clause, the methodException#cause returns the previous value of$!, which may benil; elsewhere, the method returnsnil.

Example:

beginraise('Boom 0')rescue=>x0puts"Exception: #{x0.inspect};  $!: #{$!.inspect};  cause: #{x0.cause.inspect}."beginraise('Boom 1')rescue=>x1puts"Exception: #{x1.inspect};  $!: #{$!.inspect};  cause: #{x1.cause.inspect}."beginraise('Boom 2')rescue=>x2puts"Exception: #{x2.inspect};  $!: #{$!.inspect};  cause: #{x2.cause.inspect}."endendend

Output:

Exception: #<RuntimeError: Boom 0>;  $!: #<RuntimeError: Boom 0>;  cause: nil.Exception: #<RuntimeError: Boom 1>;  $!: #<RuntimeError: Boom 1>;  cause: #<RuntimeError: Boom 0>.Exception: #<RuntimeError: Boom 2>;  $!: #<RuntimeError: Boom 2>;  cause: #<RuntimeError: Boom 1>.

Else Clause

Theelse clause:

beginputs'Begin.'rescueputs'Rescued an exception!'elseputs'No exception raised.'end

Output:

Begin.No exception raised.

Ensure Clause

The ensure clause:

deffoo(boom:false)puts'Begin.'raise'Boom!'ifboomrescueputs'Rescued an exception!'elseputs'No exception raised.'ensureputs'Always do this.'endfoo(boom:true)foo(boom:false)

Output:

Begin.Rescued an exception!Always do this.Begin.No exception raised.Always do this.

End Statement

Theend statement ends the handler.

Code following it is reached only if any raised exception is rescued.

Begin-Less Exception Handlers

As seen above, an exception handler may be implemented withbegin andend.

An exception handler may also be implemented as:

Re-Raising an Exception

It can be useful to rescue an exception, but allow its eventual effect; for example, a program can rescue an exception, log data about it, and then “reinstate” the exception.

This may be done via theraise method, but in a special way; a rescuing clause:

begin1/0rescueZeroDivisionError# Do needful things (like logging).raise# Raised exception will be ZeroDivisionError, not RuntimeError.end

Output:

ruby t.rbt.rb:2:in 'Integer#/': divided by 0 (ZeroDivisionError)    from t.rb:2:in '<main>'

Retrying

It can be useful to retry a begin clause; for example, if it must access a possibly-volatile resource (such as a web page), it can be useful to try the access more than once (in the hope that it may become available):

retries =0beginputs"Try ##{retries}."raise'Boom'rescueputs"Rescued retry ##{retries}."if (retries+=1)<3puts'Retrying'retryelseputs'Giving up.'raiseendend
Try #0.Rescued retry #0.RetryingTry #1.Rescued retry #1.RetryingTry #2.Rescued retry #2.Giving up.# RuntimeError ('Boom') raised.

Note that the retry re-executes the entire begin clause, not just the part after the point of failure.

Raising an Exception

MethodKernel#raise raises an exception.

Custom Exceptions

To provide additional or alternate information, you may create custom exception classes. Each should be a subclass of one of the built-in exception classes (commonlyStandardError orRuntimeError); seeBuilt-In Exception Class Hierarchy.

classMyException<StandardError;end

Messages

EveryException object has a message, which is a string that is set at the time the object is created; seeException.new.

The message cannot be changed, but you can create a similar object with a different message; seeException#exception.

This method returns the message as defined:

Two other methods return enhanced versions of the message:

Each of the two methods above accepts keyword argumenthighlight; if the value of keywordhighlight istrue, the returned string includes bolding and underlining ANSI codes (see below) to enhance the appearance of the message.

Any exception class (Ruby or custom) may choose to override either of these methods, and may choose to interpret keyword argumenthighlight: true to mean that the returned message should containANSI codes that specify color, bolding, and underlining.

Because the enhanced message may be written to a non-terminal device (e.g., into an HTML page), it is best to limit the ANSI codes to these widely-supported codes:



It’s also best to craft a message that is conveniently human-readable, even if the ANSI codes are included “as-is” (rather than interpreted as font directives).

Backtraces

Abacktrace is a record of the methods currently in thecall stack; each such method has been called, but has not yet returned.

These methods return backtrace information:

By default, Ruby sets the backtrace of the exception to the location where it was raised.

The developer might adjust this by either providingbacktrace argument toKernel#raise, or usingException#set_backtrace.

Note that: