Error Handling with Blocks
Ryan Stenberg, Former Developer
Article Category:
Posted on
I recently built an API with Sinatra and ran into a recurring challenge when dealing with resource-specific routes (like /objects/:id
). The first thing I had to handle in each of those routes was whether or not a record for both the resource type and id existed. If it didn't, I wanted to send back a JSON response with some meaningful error message letting the API consumer know that they asked for a certain kind of resource with an ID that didn't exist.
My first pass looked something like this:
get '/objects/:id' do |id| object = Object.find_by_id(id) if object.nil? status 404 json(errors: "Object with an ID of #{id} does not exist") else json object end end put '/objects/:id' do |id| object = Object.find_by_id(id) if object.nil? status 404 json(errors: "Object with an ID of #{id} does not exist") else if object.update_attributes(params[:object]) json object else json(errors: object.errors) end end end
Seems ok, but there would be a lot of duplication if I had these if
/else
statements in every resource-specific route. Lately, I've looked for common if
/else
conditionals like this as an opportunity for method abstraction, particularly with the use of blocks and yield
. The following methods are an example of this kind of abstraction:
def ensure_resource_exists(resource_type, id) resource = resource_type.find_by_id(id) if resource.nil? status 404 json(errors: "#{resource_type} with an ID of #{id} does not exist") else yield resource if block_given? end end
Then the initial example would look something like:
get '/objects/:id' do |id| ensure_resource_exists(Object, id) do |obj| json obj end end put '/objects/:id' do |id| ensure_resource_exists(Object, id) do |obj| if obj.update_attributes(params[:object]) json obj else json(errors: obj.errors) end end end
It hides away the distracting error case handling and gives us a readable, declarative method body. Next time you find yourself dealing with repetitive error cases, use blocks like this for great justice!