Most of Sinatra’s well-deserved praise is directed toward its simple, elegant routing DSL. I want to draw attention to another feature that I love that doesn’t get much attention: Sinatra’s
One of the biggest sources of complexity when developing robust applications is all of the error-handling code. Generally, code is easier to understand and is more maintainable when the error handling is not mixed together with the “happy path” code. This is one reason that virtually all modern languages have opted to provide exceptions rather than old C-style error return values.
halt provides similar benefits.
Consider a simple sinatra route:
This is all “happy path” code, and there are a few potential problems that aren’t handled at all:
- The user record for the given
:user_idmay not exist.
- The project record for the given
:project_idmay not exist, or may not belong to the given user.
:task_datedate string may not be in iso8601 format (i.e. YYYY-MM-DD).
Let’s handle each of these so that our API returns semantic HTTP status codes rather than responding with a 500 error:
This is certainly more robust, but I really, really hate this style of code. It makes my eyes bleed just looking at it. Let’s clean it up a bit with some helper methods and
Much, much better. It’s more lines of code, but so much simpler. The error handling for each piece of data is handled directly in the helper method that is responsible for that piece of data. This has the added benefit of making it simpler to implement other routes that need some of these pieces of data (and the corresponding error handling):
This is only possible through the magic of
halt. Under the covers,
throw1 to immediately stop processing the route and return the response given to
halt. Some ruby developers have gone on record as hating ruby’s throw, and it can certainly be abused…but Sinatra’s
halt sure is useful and is only made possible by
The next time you’re building a Sinatra application, I encourage you to consider using
halt to simplify your error handling.