What is a good way to suppress exceptions inside the rescue clause in Ruby?

3.1k views Asked by At
begin
  do_something
rescue
  Logger.write ...

  ...error handling...
end

The problem is that it is possible that the code inside the rescue raises an exception. For this use case, I want to suppress it.

So, how to wrap the rescue in a rescue to suppress the Exception?

1

There are 1 answers

1
DiegoSalazar On

You said it yourself

wrap the rescue in a rescue

Exception Inception

There's no special way other than wrapping blocks of code in a begin rescue end block:

begin
  do_something
rescue
  begin
    Logger.write ...
  rescue
    ...error handling...
  end
end

...unless you're using ActiveSupport: http://api.rubyonrails.org/classes/Kernel.html#method-i-suppress

suppress(Exception) do
  # all exceptions will be suppressed
end

..but it isn't a plain Ruby thing and I noticed you didn't add a rails tag to your question. Though, you can implement suppress yourself. Or, just, here take this:

def suppress(*exception_classes)
  yield
rescue *exception_classes
end

Use that in your rescue:

begin
  do_something
rescue
  Logger.write ...

  suppress Exception do
    ...error handling...
  end
end

In plain old Ruby it's got to be rescues all the way down (and indeed, suppress is just a nested rescue) but let me just state that it's a bad idea to rescue all exceptions, in essence, by not being explicit about what exception you're rescuing, in other words, by not passing an exception class argument to rescue you're implicitly rescuing StandardError which is the superclass to most exceptions (more info here). This can lead to hard to find bugs.

begin    
rescue
end

..is the same as:

begin
rescue StandardError
end

It's much better to know what exception you're rescuing and be explicit about it:

begin
rescue SomeApiError => e
  # do something when this specific exception occurs
end

With that figured out you can use a cascade strategy to rescue your exceptions:

begin
  # do the thing
rescue SomeApiError => e 
  # specific rescue
rescue SomeOtherError => e
  # more broad error handling
rescue
  # catch all rescue
end

With the above only the rescue clause that matches the exception being raised will run however, code in a rescue block won't be rescued by subsequent rescues, only the code in the begin block is rescuable.

If you want to have a block of code that always runs even in the event of an exception, use the ensure keyword:

begin
  # code
rescue => e
  # rescue code
ensure
  # this block will always run exception or not
end