Ruby
Similar concepts, different syntax…
- Exceptions are raised using the
raisemethod of theKernelclass - The
rescueclause is used the handle exceptions - Exceptions are instances of the
Exceptionclass (or subclasses thereof)- Subclasses do not add methods or behavior. They do allow exceptions to be categorized.
- Most exceptions extend
StandardError- Other exceptions are low-level, typically not handled by programs
Exception Methods
Methods that can be used to get information about an particular exception:
message- Returns a
String - More suitable for programmers than end users
- Returns a
backtrace- Returns the call stack as an array of
Strings - Lines of the form: `
: :in
- Returns the call stack as an array of
Raising an Exception
fail is a synonym of raise – it’s meant to be used if you expected the
program to end as a result of the exception.
There are several ways to invoke raise:
- No arguments
- If inside
rescue, re-raises same exception - Otherwise, raises
RuntimeError
- If inside
- With single
Exceptionobject argument- Uncommon
- With single
Stringargument- Creates
RuntimeErrorwith the string arg as the message - Very common
- Creates
- With
Exceptionobject,String(formessage()), and an array of strings (forbacktrace)
Example + Exercise
def factorial(n)
raise "Bad argument" if n < 1
# raise ArgumentError if n < 1
# raise ArgumentError, "Exception argument >= 1, got #{n}" if n < 1
return 1 if n == 1
n * factorial(n-1)
end
You can also provide a custom stack trace:
def factorial4(n)
if n < 1
raise ArgumentError, "Exception argument >= 1, got #{n}", caller
end
return 1 if n == 1
n * factorial(n-1)
end
The above code blocks correspond to the code in exceptions-1.rb in the
provided source files. Uncomment the various factorial methods to see different
functionality.
Catching an Exception
rescue is a language keyword (not a Kernel method). You can attach a
rescue clause to any statement in Ruby – typically, it’s attached to begin:
begin
# statements, possible exceptions
rescue
# code to handle exceptions
end
# if no exception, code continues here -- code in rescue is not executed
Handling Exceptions
rescue handles any StandardError. The global variable $! refers to the
current raised exception, though it is better to specify a variable for the
exception in the rescue statement:
begin
x = factorial(0)
rescue => err
puts "#{err.class}: #{err.message}"
end
Handling Multiple Types of Exceptions
def factorial5(n)
raise TypeError, "Need integer" if not n.is_a? Integer
raise ArgumentError, "Need argument >= 1, got #{n}" if n < 1
return 1 if n == 1
n * factorial(n-1)
end
begin
x = factorial5("a")
rescue ArgumentError => ex
puts "Try again with argument > 1"
rescue TypeError => ex
puts "Try again with an integer"
end
Question: Does the order of the rescue statements matter?
Exercise: In exceptions-1.rb, uncomment and run the various begin/end
blocks that handle exceptions.
Propagating Exceptions
The following code gives a relatively complex system of exception handling – as a quick exercise, try tracing this code through each of the paths it could take.
def explode
raise "bam!" if rand(10) == 0
end
def risky
begin
10.times do
# raises error ~10% of time
explode
end
rescue TypeError # won't catch RuntimeError
puts $!
end # RuntimeError not handled, will propagate
"hello" # if no exception
end
def defuse
begin
puts risky
rescue RuntimeError => e # handle propagated error
puts e.message
end
end
defuse
Exercise: After you try to trace the code, try running exceptions-2.rb and
check your understanding.
Other Options
retry- When used inside a
rescue,retryreruns the block of code that raised the exception. - Useful for transient failures, e.g. overloaded server
- Number of retries should be limited
- When used inside a
ensure- Same idea as Java’s
finally
- Same idea as Java’s
Note on throw/catch
Ruby has the keywords throw/catch, but they’re not used for handling
exceptions. If you’re curious, check out this blog
post,
which is written by this guy:

Tell me he doesn’t look like a guy you can trust, I dare you.