Just Do It: Learn Sinatra, Part One

In this 4-part series of tutorials, I’m going to take you through the process of creating a fully functioning To Do List app called ‘Just Do It’, using Sinatra and DataMapper. Hopefully, this will help to demonstrate just how quick and easy it easy to use Sinatra. Let’s begin with the basics of Sinatra.

Installing Sinatra

To get started with Sinatra, you’ll need to have Ruby installed. I would recommend using RVM for this (you can follow this great guide by Glenn Goodrich if you need help). After you have Ruby and Rubygems successfully installed, it’s time to install Sinatra. This is done using Rubygems and is simply a case of opening up a command line and typing:

 
$> gem install sinatra

A Basic Application

To start with, open up your favorite text editor and save the following as main.rb. Now type the following lines.

require 'sinatra' 
get '/' do 
  "Just Do It" 
end 

Note – If you are using a version of Ruby less than 1.9, you will need to put the line require 'rubygems' at the top of this file.

This is about as basic as a Sinatra application can get: You have to require the sinatra gem at the top, then the real action starts on line 3. This is called a handler because it handles routes and actions. The first part (get) states which HTTP method is being used, in this case, HTTP GET, because we are ‘getting’ a page. The next part is a string that corresponds to the route, which is ‘/’ – the root url of the application. The code block specifies what happens when the user visits this url. In this case, we return a simple text string (“Just Do It” ) which will be rendered on the page. The last line of a handler’s code block is always what will be rendered by the browser.

To check that it’s working, we need to start a Sinatra server. Open up a command line, navigate to where your file is saved and type:

 
$> ruby main.rb

After a couple of seconds, you should see the following message:

== Sinatra/1.2.6 has taken the stage on 4567 for development with backup from Thin

Open up your browser and go to http://localhost:4567. You should see the inspirational phrase”Just Do It”. Well done, you’ve successfully created your first Sinatra app. See, I told you it was easy!

Inline Templates

The Just Do It application won’t get very far by just displaying short strings of text, of course. We’re going to have to create some template files that will contain HTML as well as some dynamic content using Ruby. Slim is a fantastic template engine that makes this a much easier task. Before we go on, we need to install the slim gem:

 
gem install slim

Now, go back to main.rb and add the following lines:

 
require 'sinatra' 
require 'slim'

get '/' do 
  slim :index 
end

__END__

@@layout 
doctype html 
html
  head 
    meta charset="utf-8" 
    title Just Do It 
    link rel="stylesheet" media="screen, projection" href="/styles.css" 
    /[if lt IE 9] 
      script src="http://html5shiv.googlecode.com/svn/trunk/html5.js" 
  body 
    h1 Just Do It 
    == yield 

@@index 
h2 My Tasks
ul.tasks
  li Get Milk

First, we make sure that we require the slim gem and then we make a few changes to the handler so the last line now returns a view called ‘index’ that is generated by slim. This view can be seen at the bottom of the file, after ‘@@index’. This is an example of Sinatra’s inline templates, something I consider to be a killer feature as it allows you to keep all your code in the same file – perfect for putting things together quickly. Inline templates always come after the __END__ declaration, and each template begins with @@.

I’ve also included a template called ‘@@layout’. This will automatically be rendered with every view and provides a basic HTML5 scaffolding. The key line in the layout template is right at the end (==yield). The yield statement renders the content from the whichever template was requested by the handler (in this case, ‘index’).

Both of these views use Slim’s minimal syntax. I find this makes writing HTML a much more pleasant experience, but be warned – Slim is white-space sensitive. Indentations of 2 spaces are used to nest elements within each other and Slim is very strict about this being consistent. If you don’t like Slim, there’s a whole variety of other templating languages that can be used with Sinatra, including ERB, Haml and Markaby.

Let’s see how this looks: Kill the server by pressing Ctrl+C and restart it again by typing ruby main.rb. The server will need to be restarted every time you make any changes to your code (if this starts to become a hassle, you might want to try using Shotgun). Reload the page at http://localhost:4567 to see our new layout.

External Views

Now that you are familiar with how Sinatra uses handlers to render views, let’s move away from the inline templates and look at organizing our views into folders.

Before we do that, we need to remove the inline templates: open up main.rb and delete the __END__ declaration and all the templates that come after.

In the same directory where you saved the ‘main.rb’ file, create two folders, one called ‘public’ and the other called ‘views’. The public folder will be used to keep any public facing assets, such as images and stylesheets. The views folder will keep all of our Slim templates. Our existing views need transferring to separate files. Save the following in the views folder as layout.slim:

 
doctype html 
html 
  head 
    meta charset="utf-8" 
    title Just Do It 
    link rel="stylesheet" media="screen, projection" href="/styles.css" 
    /[if lt IE 9] 
      script src="http://html5shiv.googlecode.com/svn/trunk/html5.js" 
  body 
    h1 Just Do It 
    == yield 

And also save the following as index.slim:

h2 My Tasks
ul.tasks
  li Get Milk

It’s a good idea to restart your Sinatra server and make sure our views are still rendered.

Dynamic Content

Now that we are a bit more organized we can have a look at a few more of Sinatra’s features. Let’s create a new handler that takes some dynamic input (in main.rb):

get '/:task' do
  @task = params[:task]
  slim :task
end

You may have noticed that the route contains the string ‘:task’ – this is a named parameter, identifiable by the leading colon(‘:’). Named parameters are values taken from the url that are accessible through the ‘params’ hash. In the first line of the code block, I set an instance variable called ‘@task’ equal to the value of params[:task], which will be whatever is written after the forward slash in the url. Instance variables are useful as they can be referenced in views.

Speaking of views, the new route specifies a ‘task’ view, which doesn’t exist. Copy the following code into a new text file and save it in the views folder as ‘task.slim’:

h2 My Tasks
= @task

This uses the ‘=’ sign to evaluate a Ruby variable. Slim will output the result of whatever Ruby is placed after the ‘=’ sign. In this case it is the value of the @task instance variable, which should match the url. Test this out – kill and restart the server, then go to ‘/http://localhost:4567/get-milk’. You should see the following:

ToDo Task

Simple task route

This is okay, but we can do better. Let’s add a bit of logic into the handler to make it look nicer:

get '/:task' do
  @task = params[:task].split('-').join(' ').capitalize
  slim :task
end

Now when you go to ‘/http://localhost:4567/get-milk’, you should see the following:

Our better task page

Forms

Before we finish part one of the tutorial, lets have a look at adding a task using a form. Open up index.slim and replace the contents with the following:

  form action="/" method="POST"
    input type="text" name="task"
    input.button type="submit" value="New Task >>"

We now need a handler to deal with the form after it has been posted. If you have a look at the code, the action attribute tells the form to submit itself to the url ‘/’ and the method attribute tells it to use the POST method. This leads very nicely on to what I consider to be another one of Sinatra’s killer features – the way the request method is specified in the handler. Add the following handler to ‘main.rb’:

post '/' do
  @task =  params[:task]
  slim :task
end

This code is very similar to the handler we used earlier to list the tasks, but now we are using the form’s input to get a task. The new handler is defined as a POST route, meaning that it only reacts to HTTP POST requests. So, we can define two handlers for the same route – ‘/’ – but give different blocks of code based on the type of request.

When the form is submitted, it sets the value of params[:task] to whatever was entered in the form task text input. You can access any values set in forms by referencing the params hash in a similar way.

Go to http://localhost:4567/ and have a play around adding some tasks with the form. This is a start, but we can’t create a list of tasks and there’s certainly no way of completing and deleting them. We need a way of storing the tasks, and that is what we’ll be covering in part 2!

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.jakub.chodorowicz.pl/ Jakub Chodorowicz

    Nice, thanks. Looking forward to next parts!

    • http://ididitmyway.heroku.com/ Darren Jones

      Cheers Jakub! Glad you enjoyed it and I hope you like the next parts.

      DAZ

  • Andrea

    Great post. I have heard about Sinatra before but it’s the frist time I can see how it works. I’m definitely waiting for the next part.

    (In the article consider adding that when you move the templates into external views main.rb should be modified too to delete lines starting from __END__)

    • http://ididitmyway.heroku.com/ Darren Jones

      Cheers for the comment Andrea. Glad you liked the post and I hope you enjoy the next part as much. You’re right about deleting the inline templates from main.rb – I’ve updated the tutorial to include this. Thanks for pointing it out.

      DAZ

  • Dmitry

    Thanks for this great post. I’ve been wanting to learn Sinatra, but there aren’t enough tutorials like this one.

    I tried to run the code in the second section (Inline Templates), but I got the following error:

    Slim::Parser::SyntaxError at /
    Unexpected indentation main.rb, Line 2 html ^
    file: parser.rb location: syntax_error! line: 443

    • Dmitry

      I think I fixed it? It seems the word “html” on line 10 shouldn’t have been indented. I removed the indent and it ran fine.

      • http://ididitmyway.heroku.com/ Darren Jones

        Hey Dmitry,

        You’re right the html shouldn’t be indented, I’ve corrected the post now. Thanks for spotting that!

        DAZ

  • http://arunanskanthan.com Arunan

    When can we expect the rest of the series? :)

    • http://ididitmyway.heroku.com/ Darren Jones

      Hi Arunan,

      Part 2 is out tomorrow (Wednesday)! Hope you enjoy it.

      DAZ

  • http://benpakmanpakulski.net Irrafttic

    Hiya, I am really glad I’ve found this info. Nowadays bloggers publish only about gossip and web stuff and this is really annoying. A good website with interesting content, this is what I need. Thanks for making this website, and I’ll be visiting again. Do you do newsletters? I Can not find it.

    • http://daz4126.com/ Darren Jones

      Hi irrafttic,

      Glad you liked the post. Hopefully you will enjoy the rest of the series.

      There is indeed a newsletter – you can sign up to it and all the other Sitepoint newsletters here – http://www.sitepoint.com/newsletter/

      DAZ

  • http://thesomanabolicmusclemaximizerau.info CigMizInini

    Obviously I like your web site, but you need to test the spelling on quite a few of your posts. A number of them are rife with spelling problems and I find it very bothersome to inform you. On the other hand I will certainly come again again!

    • http://daz4126.com/ Darren Jones

      Thanks for the feedback CigMizInini. Sorry about the typos being a nuisance, I’ll try to catch them in future posts.

      DAZ

  • Ryan Galgon

    This post mentions using Shotgun for auto-reloading, which is really helpful, but doesn’t work great on Win7 (or at all it seems).

    If you’re getting something like:
    bin/shotgun:142:in `trap’: unsupported signal SIGQUIT (ArgumentError)

    Check out: http://www.sinatrarb.com/contrib/reloader which has details on sinatra-reloader which does work on Win32.

    • http://daz4126.com/ Darren Jones

      Thanks for that tip Ryan – Sinatra Reloader seems to be a popular option at the moment. I don’t have access to Windows for development, so it’s good to get tips from other users.

      cheers,

      DAZ

  • http://www.studio625.net Chris

    “slim gem”…genius! Pun intentional?

    Great tutorial by the way, thanks!

    • http://daz4126.com/ Darren Jones

      Hey Chris, I’ve not noticed that before! Not sure if it’s intentional or not … Thanks for the positive feedback.

      DAZ

  • http://www.bing.com/35c65d36f8f727eae7a269fed23e9837f1b39795 site

    Fantastic Stuff, do you have a twitter account?

  • http://www.yahoo.com/f6e923d929e0b98970235eb9e12f29bea28deae1 source

    Just to let you know your site appears a little bit unusual on Firefox on my notebook using Linux .

  • Lee

    Thanks for the post. I tried to use this with Shotgun and hit a bunch of problems because it was not able to find the views. It was searching in the wrong folder. I was not able to get the initial inline template sample working, but I fixed the problem with the wrong folder by adding this:

    configure do
    set :views, File.dirname(__FILE__) + “/views”
    end

    After that I was able to use Shotgun to run the sample for all the *.slim file references and not have to stop and start the server each time

  • um_ok

    Why would I be calling :task when I’m putting the form in index.slim? That doesn’t work. It does however work if I drop the form directly in task.slim…

    wouldn’t it make more sense, if we’re using index.slim to house the form code then we use add this line to main.rb?

    post ‘/’ do
    @index = params[:index]
    slim :index
    end

    Works like a charm.