Zend certified PHP/Magento developer

RubySource: 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:

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!