2

In building Sinatra or Padrino apps, I often write code like

get '/resource/:id' do
  resource = Resource.find(params[:id])
  return status 404 if resource.nil?
  # ..
end

Or actually, I like to

flash[:warning] = "A Resource with id #{params[:id]} coud not be found".
redirect back

I think in Rails this is modeled via "Ressources". My controllers tend to be mixed, part of the routes will depend on a resource id (which will be fetched from whatever db), other do not.

Which patterns can be used to dry this up? I know of before handlers (pseudo code, but have not seen a really smart implementation - it is sure out there somewhere!)

 before "*" do
   @resource = Resource.get(params[:id])
   redirect_with_flash if @resource.nil?
 end

or to put similar code in a method to call first in each route with that requirement.

Still, I see similar pieces of code in nearly every Sinatra tutorial, isn't there a better option? I am especially interested in a padrino-approach to that, if I overlooked it.

Here is how the code I would like to have could look like

MyPadrinoApp::App.controllers :user do
  associated_resource = User
  associated_resource_error_flashs = { "404": "A User with %s could not be found" }

  get :show, :with => :id, :resource_bound => :user do
    render '/user/show' # in which @user is available
  end
end
Felix
  • 4,510
  • 2
  • 31
  • 46

1 Answers1

2

If you want to stop processing a request as soon as you know the request is invalid / an error occurred you can use Sinatras halt. It stops further processing immediately and allows you to define a http status code and a message to display, if your App is not about a REST API you can define the corresponding error template.

In your example the request becomes invalid because the requested resource doesn't exists. To answer with a 404 is correct and you can tell halt to use this status code in the response.

A very simple implementation can look like this:

get '/resource/:id' do
  resource = Resource.find(params[:id])
  halt 404, "A Resource with id #{params[:id]} could not be found" if resource.nil?
  # ..
end

A more elegant way is loading the resource using a helper method which cares about the error handling and you are good to use the same call in all your routes.

helpers do
  def load_resource
    Resource.find(params[:id]) || halt(404, "A Resource with id #{params[:id]} could not be found")
  end
end

get '/resource/:id' do
  # load the resource via helper method
  resource = load_resource

  # if the resource doesn't exists, the request and processing is already dropped
  ..
end

There are even more output options for halt, as mentioned you can return a erb template, you can also return JSON instead of plain text and so on. Check the docs here.

maddin2code
  • 1,334
  • 1
  • 14
  • 16
  • Thanks. That seems like a valid and handy approach and `halt` the semantically right thing to use. I will wait for further answers and accept yours as the only available pattern if no more come :) – Felix Sep 25 '14 at 10:33
  • Good, I'm interested in other approaches as well, even if I'm pretty sure halt is the way to go. You can find a more advanced example of using halt here http://myronmars.to/n/dev-blog/2012/01/why-sinatras-halt-is-awesome. – maddin2code Sep 25 '14 at 10:38