Recently at EverTrue, a colleague and I were working on bringing up a new service in Rails. As we were working, I pulled in BetterErrors gem as a way to make development easier. If you’re not familiar with the gem, it replaces the default Rails development error pages with much richer ones and even allows you use REPL on the page that you can use to quickly step though the action that errored. It’s a lot easier then adding debugger. Checkout RailsCast #402 for a quick demo.
We were surprised to find that a major issue though; BetterErrors would return 500 responses for every error raised. If you were working on something that used relied on proper error responses (i.e. an API) then this would make development impossible. Instead of abandoning the gem, I decided to fix the issue and improve the project.
Since BetterErrors fundamentally is a middleware, I used debugger, and later ByeBug, to step though the full response process with and without BetterErrors. This helped me understand what was gong on under the hood with Rails and where the handoff was going arry.
When an error is raised in Rails, say from ActiveRecord, etc…
1 2 3 4 5 6 7 8 9 10 11 12 13
Once I found the issue, I wanted to accomplish a few things with the patch:
- Generate the proper error codes.
- Do it as DRYly as possible, did not want to have to have to configure errors twice.
- Make it seamless with Rails, but not dependent.
The patch that I wrote uses
ActionDispatch::ExceptionWrapper, if its available, to map the raised exception to a HTTP status. This is how Rails would normally generate the response, but since BetterErrors’ middleware is taking control, it needs to be checked here.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
This only uses
ActionDispatch if its available, otherwise it defaults to the existing behavior. This means that in a Rails environment, whatever the developer is already doing for custom exception handling does not need to change and it will work seamlessly.
I could see this needing to be expanded for other frameworks or finding a more generic way to handle these errors, but for the time being it scratches my particular itch, and I hope others’ as well.