Begin, rescue, and ensure provide flexible exception handling. Supposed we have the following method:
def divide(a, b) begin a / b rescue TypeError => e puts "I am rescuing from a TypeError" puts e puts e.class puts e.backtrace rescue ZeroDivisionError => e puts "I am rescuing from a ZeroDivisionError" puts e puts e.class puts e.backtrace else puts "No exception was raised" ensure puts "BLAH BLAH BLAH" end end
Let’s examine what this method outputs with various inputs.
>> divide(4, 2) No exception was raised BLAH BLAH BLAH
If the function is passed valid output, the code in else is executed and so is the code in ensure. The code in ensure is executed regardless, but else is only executed if there are no exceptions.
>> divide(1, "cat") I am rescuing from a TypeError String can't be coerced into Fixnum TypeError lib/begin_rescue.rb:3:in `/' lib/begin_rescue.rb:3:in `divide' lib/begin_rescue.rb:22:in `<main>' BLAH BLAH BLAH
Where there is a TypeError, the code in the TypeError rescue block and the ensure block are executed. By passing => e to the rescue TypeError => e line, we have access to the error message, error class, and error backtrace – useful stuff for debugging purposes.
>> divide(1, 0) I am rescuing from a ZeroDivisionError divided by 0 ZeroDivisionError lib/begin_rescue.rb:3:in `/' lib/begin_rescue.rb:3:in `divide' lib/begin_rescue.rb:23:in `<main>' BLAH BLAH BLAH
Curiously, the following code does not execute the code in the ensure block (didn’t we previously establish that the code in the ensure block is always executed under all conditions).
lib/begin_rescue.rb:1:in `divide': wrong number of arguments (0 for 2) (ArgumentError) from lib/begin_rescue.rb:21:in `<main>'
In this case the ArgumentError is raised before the function is executed, so the ensure never gets the chance to run. We can rescue from an ArgumentError (rescuing from ArgumentErrors is not a good idea, BTW), by putting the method call in another begin/rescue block:
begin divide rescue ArgumentError puts "Ahhhhh, that's better" end