Exception Handling
Exception Class
When an exception occurs in Ruby code an instance of one of descendants of class Exception
is used to carry data about the exception. Such an object has its class, may have a description and may include backtrace. Such an object may be a built-in Ruby object or a custom made class furnished with additional functionalities.
The most notable built-in subclasses of Exception are: SyntaxError
, StandardError
(default for rescue
block), ArgumentError
, NameError
, NoMethodError
, and RuntimeError
(default for raise
block).
It is possible to define a custom-cut exception class through subclassing of Exception class or one of its subclasses.
class MyCustomError < Exception
end
Raising
An exception in Ruby can be raised implicitly due to some underlying code defect or explicitly by a programmer in a manner decided by the programmer.
1/0 # ZeroDivisionError: divided by 0
raise "Nobody expects the Spanish inquisition!" # RuntimeError: Nobody expects the Spanish inquisition!
raise ZeroDivisionError, "Please, don't divide by zero!" # ZeroDivisionError: Please, don't divide by zero!
Rescuing
Once an exception occurs code execution stops unless it is rescued. The reserved keyword rescue
can be used within a method or within a block.
def divide_by_zero
1/0
rescue
puts "Rescued!"
end
divide_by_zero # Rescued!
begin
1/0
rescue
puts "Rescued!"
end
# Rescued!
It is also possible to rescue from a specific subclass of Exception only.
def divide_by_zero
1/0
rescue SyntaxError
puts "Rescued!"
end
divide_by_zero # ZeroDivisionError: divided by 0
It is also possible to catch the exception object.
def divide_by_zero
1/0
rescue => e
p e
puts "Rescued!"
end
divide_by_zero
# #<ZeroDivisionError: divided by 0>
# Rescued!
e
(or other name set by the programmer) denotes an object that is an instance of one of the subclasses of the Exception class. In this particular case e
is an istance of ZeroDivisionError
class.
e.backtrace
and e.backtrace_locations
methods can be used to track traceback data.
Combining Rescue & Raise
We can use raise
and rescue
keywords within the same block.
def divide_by_zero
raise ZeroDivisionError, "Please, don't divide by zero!"
rescue ZeroDivisionError => e
puts e
puts e.backtrace
puts "Rescued!"
end
divide_by_zero
# #<ZeroDivisionError: divided by 0>
# ["(pry):296:in `divide_by_zero'", "(pry):302:in ...]
# Rescued!
Else
Within a method or a block in addition to the rescue
keyword the keyword else
can also be used. The code following else
is only executed when no exception is raised.
Ensure
Within a method or a block in addition to the rescue
and else
keywords the keyword ensure
can also be used. The code following ensure
is always executed irrespective of whether any exception occured or not.
Retry
retry
can be placed within rescue
block to tell the program to retry the rescued block. This should be used with caution as not to create an infinite loop.