Singing with Sinatra


Welcome to Track 1 of "Singing with Sinatra." In this mini-series we'll be taking a look at Sinatra; a small, yet incredibly powerful DSL for quickly creating Ruby web applications. In this part, we'll get started with Sinatra by playing around with a few routes, learning how to access URL parameters and how to POST data between pages.

If you haven't worked with Ruby before, you should check out the Ruby for Newbies session, where Andrew will guide you through the process of installing Ruby on your system and learning the basics of the language.

First thing we need to do is install the Sinatra RubyGem. Enter the following into the Terminal:

gem install sinatra

Also install the 'shotgun' gem, which we'll use later:

gem install shotgun

Depending on how you have RubyGems set up on your system, you may need to prefix the gem install commands with sudo.

The Very Basics

Open your text editor and create a new file named basics.rb. Right at the top we need to require RubyGems and the Sinatra gem:

require 'rubygems'
require 'sinatra'

Note: If you're running Ruby 1.9 (which you should be ;)), you can drop the require 'rubygems' line as Ruby automatically loads RubyGems anyway.

Let's start off by creating the classic "Hello World". Add the following to your basics.rb application file:

get '/' do
  "Hello, World!"

This is a 'Route'. Here, we're telling Sinatra that if the home, or root, URL '/' is requested, using the normal GET HTTP method, to display "Hello, World!"

Now, in the terminal, let's start up the server by typing ruby basics.rb. We're told that Sinatra has "taken the stage" on port 4567, and if we go to http://localhost:4567/ in a browser, we see "Hello, World!".

So let's try another page:

get '/about' do
  'A little about me.'

Whenever you make a change to your Sinatra app you have to restart the server.

This mean if the '/about' URL is requested (using the GET HTTP method), "A little about me." will display.

Whenever you make a change to your Sinatra app you have to restart the server. So to save us the hassle of constantly stopping & starting the server during development, we'll use the Shotgun gem we installed earlier.

Stop the current Sinatra server with Ctrl-C. Now we can run shotgun basics.rb and Shotgun will automatically restart the server every time we refresh the page. This is useful when we're doing a lot of development work, but as the whole application is being restarted, it can be slow.

As Shotgun's listening on a different port, let's move to port 9393 and go to http://localhost:9393/about in your browser. You should see the phrase we set.

Accessing URL Parameters

You can also access parameters from the URL. Add the following to your basics.rb file:

get '/hello/:name' do

In this example, we have a route where anything after '/hello/' will be contained in a params array with the key :name. The params array contains all GET and POST variables. If you're coming from a PHP background, this is similar to the $_REQUEST global array.

In the browser, go to, for example, http://localhost:9393/hello/dan and you should see the name displayed back ("dan").

You could incorporate the :name into a string by wrapping it in ${...}. Try replacing the params[:name] line with:

"Hello there, #{params[:name]}."

As you'd expect, we can use all the normal Ruby methods on the variable, such as .upcase, .reverse etc.

"Hello there #{params[:name].upcase}."

You can set the route to accept multiple query string variables like so:

get '/hello/:name/:city' do
  "Hey there #{params[:name]} from #{params[:city]}."

As well as normal variables in a URL, Sinatra allows you to include retrieve wildcard query strings, known as a 'splat', by using an asterisk like so:

get '/more/*' do

Anything included in the URL after /more/ will be accessible through the :splat key in the params array.

View Files & POST

Now let's do something a little more interesting. Let's create a form to retrieve data from the user, then do something with it. We'll also make use of a "view file", which allows us to split the markup for a view into a separate file. Add the following route to your basics.rb app file:

get '/form' do
  erb :form

This route will load the form.erb ERB (Embedded Ruby) file from a views/ directory. ERB files are typically normal HTML files which allow you to include Ruby code inside <% … %> tags, which will be parsed before being sent to the browser - just like how you include PHP or ASP code in a webpage.

So create a views/ directory in the same folder as the basics.rb file. And inside views/, create the file named form.erb with the following content inside:

hello from the view

Point your browser at http://localhost:9393/form and you should see the message we set in the view file.

Now we know that everything's working, change the contents of that file to:

<form action="/form" method="post">
  <input type="text" name="message">
  <input type="submit">

Refresh the page and you should see the form:

But, enter a message into the text box and click the submit button, and you'll see Sinatra's error page informing us that a route doesn't exist for this URL.

You may be wondering why this is, seeing as the form is submitting to /form, the same URL the form is on, so there shouldn't be a problem. Well, the difference is that we're retrieving this page through the POST HTTP method - and as you can see on the error page Sinatra presents us, Sinatra requires a different route for each HTTP method.

So add the following route to the Sinatra app:

post '/form' do
  "You said '#{params[:message]}'"

This route is for the POST method, not GET. Also, all POST variables are available in the same params array as GET variables, so we can retrieve the message submitted with the form. Try it out!

You Could Totally Work For MI5 Now!

So what next? Let's create a way to 'encrypt' a message we send so that it can only be read by our intended recipient. Add the following route to the Sinatra app:

get '/secret' do
  erb :secret

You're probably getting the hang of this by now. This route will load the ERB view file at views/secret.erb. Create that view file with the following:

<h3>Super Secret MI5 Message Encryptor!</h3>
<form action="/secret" method="post">
  <input type="text" name="secret">
  <input type="submit">

And create the route for the POST method:

post '/secret' do

OK, so now we've got a message encryptor which uses a special 'reverse' method to make the message seemingly unreadable, we need a way to decrypt the message:

get '/decrypt/:secret' do

There you have it! A super secret, highly secure message encryptor good enough for any country's intelligence agency.

Legal Disclaimer: When we say "highly secure" and "encryption", we're actually just reversing the letters. Sorry.


One final thing we'll touch on is 404 error pages. Right now if you go to a URL which there isn't a route for (eg. /danisthebest), you'll see Sinatra's fancy error message. On a real app we'd want to display our own error. To do this, simply use the not_found route, like so:

not_found do
  status 404
  'not found'

Here we're using Sinatra's status method to set the HTTP status code of the page to 404 (you could use this same method to set any status code, such as 201 when something is created, or 403 when a login was unsuccessful).

You could further refactor the not_found route by using Sinatra's halt method:

not_found do
  halt 404, 'page not found'


So you've performed your first duet with Sinatra! Hopefully you can see how easy this DSL makes creating web apps.

On the next track, we'll be creating a simple "to-do" app hooked up to a SQLite database. We'll also take a look at the elusive PUT and DELETE HTTP methods no-one's heard of.

Note: You can browse the final project files for this tutorial over at GitHub.