Exceptions
Signal exceptions using the
failmethod. Useraiseonly when catching an exception and re-raising it (because here you're not failing, but explicitly and purposefully raising an exception). [link]begin fail "Oops" rescue => error raise if error.message != "Oops" endDon't specify
RuntimeErrorexplicitly in the two argument version offail/raise. [link]# bad fail RuntimeError, "message" # good: signals a RuntimeError by default fail "message"Prefer supplying an exception class and a message as two separate arguments to
fail/raise, instead of an exception instance. [link]# bad fail SomeException.new("message") # Note that there is no way to do `fail SomeException.new("message"), backtrace`. # good fail SomeException, "message" # Consistent with `fail SomeException, "message", backtrace`.Do not return from an
ensureblock. If you explicitly return from a method inside anensureblock, the return will take precedence over any exception being raised, and the method will return as if no exception had been raised at all. In effect, the exception will be silently thrown away. [link]def foo fail ensure return "very bad idea" endUse implicit begin blocks where possible. [link]
# bad def foo begin # main logic goes here rescue # failure handling goes here end end # good def foo # main logic goes here rescue # failure handling goes here endMitigate the proliferation of
beginblocks by using contingency methods (a term coined by Avdi Grimm). [link]# bad begin something_that_might_fail rescue IOError # handle IOError end begin something_else_that_might_fail rescue IOError # handle IOError end # good def with_io_error_handling yield rescue IOError # handle IOError end with_io_error_handling { something_that_might_fail } with_io_error_handling { something_else_that_might_fail }Don't suppress exceptions. [link]
# bad begin # an exception occurs here rescue SomeError # the rescue clause does absolutely nothing end # bad do_something rescue nilAvoid using
rescuein its modifier form. An exception can be made for assignment of the error. [link]# bad: this catches exceptions of StandardError class and its descendant classes read_file rescue handle_error($!) # good: this catches only the exceptions of Errno::ENOENT class and its descendant classes def foo read_file rescue Errno::ENOENT => ex handle_error(ex) end # passable: assigning the error content_or_error = read_file rescue $!Don't use exceptions for flow of control. [link]
# bad begin n / d rescue ZeroDivisionError puts "Cannot divide by 0!" end # good if d.zero? puts "Cannot divide by 0!" else n / d endAvoid rescuing the
Exceptionclass. This will trap signals and calls toexit, requiring you tokill -9the process. [link]# bad begin # calls to exit and kill signals will be caught (except kill -9) exit rescue Exception puts "you didn't really want to exit, right?" # exception handling end # good begin # a blind rescue rescues from StandardError, not Exception rescue => e # exception handling end # also good begin # an exception occurs here rescue StandardError => e # exception handling endPut more specific exceptions higher up the rescue chain, otherwise they'll never be rescued from. [link]
# bad begin # some code rescue StandardError => e # some handling rescue IOError => e # some handling that will never be executed end # good begin # some code rescue IOError => e # some handling rescue StandardError => e # some handling endRelease external resources obtained by your program in an
ensureblock. [link]f = File.open("testfile") begin # .. process rescue # .. handle error ensure f.close if f endUse versions of resource obtaining methods that do automatic resource cleanup when possible. [link]
# bad: you need to close the file descriptor explicitly f = File.open("testfile") # ... f.close # good: the file descriptor is closed automatically File.open("testfile") do |f| # ... end