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:
begin do_something_risky rescue recover_from_exception end
We can gather more information from an exception like so:
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.
It’s not always necessary to explicitly type begin. Class, module, and method definitions and ruby blocks serve as implicit rescue contexts.
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.
begin do_something_risky rescue WhizBangError => e handle_specific e rescue StandardError => e handle_generic e end
You can re-raise an exception after doing something with it. raise knows to re-raise the exception in the rescue context.
begin do_something_risky rescue StandardError => e logger.error e.message raise end
You can retry the error-prone task after rescuing.
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.
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
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
class BarlessFooError < StandardError end
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.