Intercepting Ruby exceptions

The way some Ruby applications catch exceptions makes it more difficult to debug the underlying issue. That is because a common pattern for handling an exception is this:

begin
  do_something_that_fails
rescue SomeError
  raise MyAppError.new "Something went wrong"
end

So where is the problem? The problem is that by capturing the error in this way, the originals exception information is lost. That is, the original exception usually contains a message and back-trace that make debugging the error easier. With the pattern above, the output is only the MyAppError message, and the back trace to this code.

Recently, this issue has led to me not trapping errors as much as I used to, because letting the underlying exception bubble up through my apps has often resulted in more meaningful and useful errors. However, I’ve just found nesty, and I think it is the solution to this issue. That is, it allows exceptions to be caught, flagged and handled within an app, whilst preserving the original error message and back-trace.

I’ve been playing with nesty and the result is three simple ruby programs that I believe demonstrate the value of the nesty approach to handling exceptions from underlying code.

I’ve created three files that contain the code shown below. Each one has File.new(‘no_such_file’) called on line 8 so that there is some consistency between the errors raised by each example. (Unfortunately the empty lines used to achieve this don’t display output in this blog – so please assume that File.new(‘no_such_file’) is always on line 8 )

Version one: No Error handling

  File.new('no_such_file')

The error output when this run is:

wrong_file.rb:8:in `initialize': No such file or directory - no_such_file (Errno::ENOENT)
    from wrong_file.rb:8:in `new'
    from wrong_file.rb:8:in `<main>'

So a nice meaningful error that tells me what the underlying issue was. However, it would be nice if I could add some information about what I was trying to do when this error occurred. To do that I need to trap the error and handle it.

Version two: Trap the error and raise an app specific exception

class MyError < StandardError

end

begin
  File.new('no_such_file')
rescue
  raise MyError.new "Unable to access file"
end

This outputs this error message:

wrong_file_rescued.rb:10:in `rescue in <main>': Unable to access file (MyError)
    from wrong_file_rescued.rb:7:in `
<main>'

This contains error information that is specific to my app, but a lot of information is lost. Even the line number where the error occurred is lost. Line 7 is the begin statement.

Version three: Add nesty

require 'nesty'

class MyError < StandardError
  include Nesty::NestedError
end

begin
  File.new('no_such_file')
rescue
  raise MyError.new "Unable to access file"
end

And this outputs:

wrong_file_nesty.rb:10:in `rescue in <main>': Unable to access file (MyError)
    from wrong_file_nesty.rb:7:in `<main>'
    from wrong_file_nesty.rb:8:in `initialize': No such file or directory - no_such_file (Errno::ENOENT)
    from wrong_file_nesty.rb:8:in `new'
    from wrong_file_nesty.rb:8:in `<main>'

Now I have the best of both worlds: both my app specific error details and those of the underlying exception.

I found nesty via the authors blog: Skorks, and I’d thoroughly recommend you have a look at that (and the rest of that excellent blog).

This entry was posted in Ruby and tagged . Bookmark the permalink.