Sinatra filters are special methods invoked for each individual request and evaluated in the same context as the routes. They provide an efficient way to organize logic and make variables available across multiple routes. There are two types of filters, before
and after
, named for when they are executed in relation to a particular request.
The before
filter's use cases include authentication, logging, and the declaration of instance variables that should be available to all or a several routes within the same request-response cycle. This is an important distinction! Variables declared inside a before
filter will be initialized before each request and made available to all the routes that follow in that particular cycle. This behavior is different from regular instance variables declared in the body of the application, which can retain their value across different requests.
Here is a hypothetical example, showcasing the use of before
in a version of the Umbrella app that implements user authentication.
before do
# Redirect to login route if user not authenticated
unless session[:user_id]
redirect to('/login')
end
# Log the request
logger.info "Request: #{request.request_method} #{request.path} Params: #{params.inspect}"
# Pre-process some data from the request to make it available to the routes
if params[:data]
@user_coordinates = get_coordinates(params.fetch("user_location"))
end
end
# Helper method to retrieve user's location coordinates from Google Maps' API
def get_coordinates(user_location)
# Make a request to Google Map's API and process response to get the specific coordinates
user_coordinates
end
The after
filter has access to a similar context as before
. However, as its name implies, it is invoked after the route block is finished. (Although I have not had the opportunity to use this filter within my applications, I am including the example below for documentation purposes.) Here is another hypothetical example. The filter is used to ensure that the response's Content-Type
header is appropriately set to application/json
before sending it back to the client.
get '/route' do
body({ message: 'This is an endpoint!' }.to_json)
end
get '/another_route' do
body({ data: 'This is another endpoint!' }.to_json)
end
after do
response['Content-Type'] = 'application/json'
end
Lastly, a couple interesting aspects about filters:
1) Multiple before
and after
filters can be defined in the same application. They can be placed before or after the routes they affect, but the order in which they are defined determines the order in which they will be executed.
2) Sinatra filters (like routes) can optionally accept patterns and conditions. In those cases, filters will be evaluated only when the request's path matches the specified pattern. Here is an example from Sinatra's documentation:
# Any request whose path begins with '/protected/' will require authentication
before '/protected/*' do
authenticate!
end
Top comments (0)