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:
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
The error output when this run is:
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
end
begin
File.new('no_such_file')
rescue
raise MyError.new "Unable to access file"
end
This outputs this error message:
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
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:
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).