While I’ve been working on my little book about Sinatra, I’ve picked up a few helper methods that Sinatra provides. Some of these I already knew about, but hadn’t really used much and some I didn’t even know about.
Sinatra has a bucket load of helper methods that contribute to making it such a nice experience for the developer. I’ve collected a few of them here for with some examples. Some of these may be new to you, or like me you may have heard of them before and just be grateful of the reminder.
Enable and Disable
You can make app-wide settings (or module-wide settings if you’re using a modular-style app) easily in Sinatra using the
set :title, "My Amazing Blog"
set :comments, true
set :tags ,false
But if the setting is a boolean value, you can use the
disable methods instead. This not only uses less code, but also reads much better.
You can also use this for built-in settings:
Sinatra also creates a handy helper method for any boolean settings to help check if they are true. Basically it’s the name of the setting followed by a question mark. This allows us to write easy to read and follow code that checks if a settings is true or not:
if settings.comments? then display @page.comments
redirect to('/') unless settings.?
The following methods make checking which environment is being used a cinch:
This means you can write some nice looking conditional statements, such as:
require 'sinatra/reloader' if development?
run! unless test?
Halt and Pass
These two methods can be used in route handlers to control the HTTP request cycle.
halt will stop a request in its tracks, with an optional message or view and status code. It can be used if a process is taking too long or a critical error has occurred. Other uses include if somebody is trying to view an unauthorized page.
get '/important-process' do
halt 500 if something_bad_happened?
"Everything is okay"
get '/secret-page' do
halt 401,"This page is secret" unless logged_in?
"For your eyes only"
pass method can be used to pass the method down the chain to the next matching route. In the example below we have a standard route that says hello, but if the name entered in the URL is ‘daz’ then I want to pass the request on to the more specific route handler that will display a much nicer message:
get '/:name' do
pass if params[:name] == 'daz'
get '/daz' do
"Well hello there DAZ!"
Of course in the example above it would just be easier to change the order of the route handlers, but there may be instances where you don’t have control over this.
If you want to know if a request was made using Ajax, then the
Request.xhr? helper method comes in handy. This will return true if the XML HTTP Request object was used. I often use this to do redirect if Ajax wasn’t used, like in the example below which is used to delete a page. If Ajax is used then the page will just be deleted and we want the user to stay on the ‘/pages’ page, but if Ajax isn’t used then we will have to manually redirect them back to the ‘/pages’ page:
delete '/page/:id' do
page = Page.get params[:id]
redirect to('/pages') unless request.xhr?
If you have a route handler that basically performs an operation and then sends the user back to the page they came from then the
back helper method is just what you need to make you code read nicely. Used along with the redirect helper method this will simply send the user back to the page from whence they came. For example, the delete page route handler that we saw :
delete '/page/:id' do
page = Page.get params[:id]
redirect back unless request.xhr?
In Sinatra you can can create named parameters by placing a colon in front of them in the route definition, like so:
get '/hello/:name' do
This will store whatever is entered in the place of :name in
params[:name]. We can be more general than this and use * in the route. This allows any amount of text to be entered. It is accessible through params[:splat], for example:
get '/goodbye/*/hello/*' do
The URL ‘/goodbye/shoes/hello/flipflops’ would give params[:splat] = [“shoes”,”flipflops”]
You can also use it with block parameters if you don’t like using the the word splat. This also makes your code more readable and self-explanatory:
get '/*.*' do |path,ext|
In the example above, the route ‘/application.js’ would result in variables
path = 'application' and
ext = 'js', which could then be used inside the route handler block.
You can also use the splat to grab urls of indeterminate length:
get '/blog/ *' do |path|
@page = Page.first(:slug => path.first)
In the example above the route entered after ‘/blog’ can be accessed using
path.first. For example, a route such as ‘/blog/all/about/sinatra’, would result in
path.first = '/all/about/sinatra'. This can be useful when dealing with things like pretty URLs.
You can control how pages are cached by setting the headers directly, but there are also the
etag helpers that make cache control a piece of cake.
For example, if you had a page model in a simple CMS system, you could use the following:
@page = Page.get(params[:id])
For quick and dirty cache control for all pages, you can use the application start time to set the
etag headers globally. While the application start time may not strictly be the actual time all the files were last modified, we can usually assume that if the application has been restarted then something must have changed. In this case, It’s worth flushing the cache out and reloading the pages anyway. To set the application start time, place the following code in a configure block:
set :start_time, Time.now
This works because the code in a configure block is only run once at start up. Now we can use this setting to set the
etag headers. The settings is a
Time object, so it is fine to use for the
last_modified header, but it will need to be converted to a string in order to be used for the
etag header. If the following code is placed in a before block then it will be applied to all pages:
Now you should get a 304 status code instead of 200 if you refresh a page that hasn’t been modified. This is a useful way of reducing the load on the server in an application by avoiding any needless round trips to the server.
That’s All Folks
What all these methods do is make the code much easier to read and maintain. If you look at some of the code samples above, they read almost like a full sentence of English and make it very obvious what is being done. These methods also let you perform neat tricks and use less code. This is the beauty of using Sinatra – the code is short and sweet. Most of these helper methods are covered in more detail in the book. Have you used any of these and found them useful, have I missed any? Leave your answers in the comments below.