Handling Exceptions in Ruby is Easy!
Exceptions are Ruby’s way of allowing us to attempt to recover from errors. The basic syntax for handling exceptions in Ruby goes something like this:
1 2 3 4 5 |
begin do_something_risky rescue recover_from_exception end |
We can gather more information from an exception like so:
1 2 3 4 5 |
begin do_something_risky rescue StandardError => e puts e.message end |
Notice we rescued StandardError and not Exception. There are many types of exception you might not necessarily want to rescue. For example, pressing CTRL+C or sending SIGTERM to the process would not work as expected and you might have to resort to SIGKILL.
Implicit Contexts
It’s not always necessary to explicitly type begin. Class, module, and method definitions and ruby blocks serve as implicit rescue contexts.
1 2 3 4 5 6 |
def safe_method do_something_risky rescue StandardError => e logger.error e.message logger.debug e.backtrace.join("\n") end |
Rescuing exceptions is a good use case for a logger.
Multiple Exception Handling
You can handle multiple types of exception differently. Just be sure to put specific exception types before StandardError if rescue it.
1 2 3 4 5 6 7 |
begin do_something_risky rescue WhizBangError => e handle_specific e rescue StandardError => e handle_generic e end |
Re-raising Exceptions
You can re-raise an exception after doing something with it. raise knows to re-raise the exception in the rescue context.
1 2 3 4 5 6 |
begin do_something_risky rescue StandardError => e logger.error e.message raise end |
Retrying
You can retry the error-prone task after rescuing.
1 2 3 4 5 6 7 8 9 10 11 12 |
def safe_method retried = false begin do_something_risky rescue NotLoggedInError log_in unless retried retried = true retry end end end |
Just be sure to check whether you’ve already retried first to avoid an endless loop if the fix doesn’t work.
Cleaning Up
In some situations there’s code that needs to run whether there’s an exception or not. The most common example is closing a file handle
1 2 3 4 5 6 7 8 |
begin file = File.open 'file', 'w' write_to_file file rescue handle_file_error ensure file.close unless file.nil? end |
Defining Your Own Exceptions
1 2 |
class BarlessFooError < StandardError end |
Failing
1 |
fail BarlessFooError, 'Foo requires a bar.' if foo.bar.nil? |
Use fail rather than raise to surface an exception unless you are reraising as above.