





















































(For more resources related to this topic, see here.)
The first thing to do is set up Sinatra itself, which means creating a Gemfile.
mkdir address-book
cd address-book
source 'https://rubygems.org'gem 'sinatra'
bundle install
You will notice that Bundler will not just install the sinatra gem but also its dependencies. The most important dependency is Rack (http://rack.github.com/), which is a common handler layer for web servers. Rack will be receiving requests for web pages, digesting them, and then handing them off to your Sinatra application.
If you set up your Bundler configuration as indicated in the previous section, you will now have the following files:
You'll need to understand the Gemfile.lock file. It helps you know exactly which versions of your application's dependencies (gems) will get installed. When you run bundle install, if Bundler finds a file called Gemfile.lock, it will install exactly those gems and versions that are listed there. This means that when you deploy your application on the Internet, you can be sure of which versions are being used and that they are the same as the ones on your development machine. This fact makes debugging a lot more reliable. Without Gemfile.lock, you might spend hours trying to reproduce behavior that you're seeing on your deployed app, only to discover that it was caused by a glitch in a gem version that you haven't got on your machine.
So now we can actually create the files that make up the first version of our application.
require 'sinatra/base'class AddressBook < Sinatra::Base get '/' do 'Hello World!' endend
This is the skeleton of the first part of our application.
Line 1 loads Sinatra, line 3 creates our application, and line 4 says we handle requests to '/'—the root path. So if our application is running on myapp.example.com, this means that this method will handle requests to http://myapp.example.com/.
Line 5 returns the string Hello World!. Remember that a Ruby block or a method without explicit use of the return keyword will return the result of its last line of code.
$: << File.dirname(__FILE__)require 'address-book'run AddressBook.new
This file gets loaded by rackup, which is part of the Rack gem. Rackup is a tool that runs rack-based applications. It reads the configuration from config.ru and runs our application.
Line 1 adds the current directory to the list of paths where Ruby looks for files to load, line 2 loads the file we just created previously, and line 4 runs the application.
Let's see if it works.
bundle exec rackup -p 3000
Here rackup reads config.ru, loads our application, and runs it.
We use the bundle exec command to ensure that only our application's gems (the ones in vendor/bundle) get used. Bundler prepares the environment so that the application only loads the gems that were installed via our Gemfile.
The -p 3000 command means we want to run a web server on port 3000 while we're developing.
Illustration 1: The Hello World! output from the application
Have a look at the output in the Terminal window where you started the application.
I got the following (line numbers are added for reference):
1 [2013-03-03 12:30:02] INFO WEBrick 1.3.12 [2013-03-03 12:30:02]
INFO ruby 1.9.3 (2013-01-15) [x86_64-linux]3 [2013-03-03 12:30:02]
INFO WEBrick::HTTPServer#start: pid=28551 port=30004 127.0.0.1 - -
[03/Mar/2013 12:30:06]
"GET / HTTP/1.1" 200 12 0.01425 127.0.0.1 - - [03/Mar/2013 12:30:06]
"GET /favicon.ico HTTP/1.1" 404 445 0.0018
Like it or not, you'll be seeing a lot of logs such as this while doing web development, so it's a good idea to get used to noticing the information they contain.
Line 1 says that we are running the WEBrick web server. This is a minimal server included with Ruby—it's slow and not very powerful so it shouldn't be used for production applications, but it will do for now for application development.
Line 2 indicates that we are running the application on Version 1.9.3 of Ruby. Make sure you don't develop with older versions, especially the 1.8 series, as they're being phased out and are missing features that we will be using in this book.
Line 3 tells us that the server started and that it is awaiting requests on port 3000, as we instructed.
Line 4 is the request itself: GET /. The number 200 means the request succeeded—it is an HTTP status code that means Success .
Line 5 is a second request created by our web browser. It's asking if the site has a favicon, an icon representing the site. We don't have one, so Sinatra responded with 404 (not found).
When you want to stop the web server, hit Ctrl + C in the Terminal window where you launched it.
When developing software, it is very important to manage the source code with a version control system such as Git or Mercurial. Version control systems allow you to look at the development of your project; they allow you to work on the project in parallel with others and also to try out code development ideas (branches) without messing up the stable application.
git init
git add Gemfile Gemfile.lock address-book.rb config.ru
git commit -m "Hello World"
git remote add origin [email protected]:YOUR_ACCOUNT/sinatra-address-book.git
git push
You may need to sort out authentication if this is your first time pushing code. So if you get an error such as the following, you'll need to set up authentication on GitHub:
Permission denied (publickey)
Go to https://github.com/settings/ssh and add the public key that you generated in the previous section.
Now you can refresh your browser, and GitHub will show you your code as follows:
Note that the code in my GitHub repository is marked with tags. If you want to follow the changes by looking at the repository, clone my repo from //github.com/joeyates/sinatra-address-book.git into a different directory and then "check out" the correct tag (indicated by a footnote) at each stage.
To see the code at this stage, type in the following command:
git checkout 01_hello_world
If you type in the following command, Git will tell you that you have "untracked files", for example, .bundle:
git status
To get rid of the warning, create a file called .gitignore inside the project and add the following content:
/.bundle//vendor/bundle/
Git will no longer complain about those directories. Remember to add .gitignore to the Git repository and commit it.
Let's add a README file as the page is requesting, using the following steps:
sinatra-address-book ==================== An example program of various Sinatra functionality.
git add README.md
git commit -m "Add a README explaining the application"
git push
Now that we have a README file, GitHub will stop complaining.
What's more is other people may see our application and decide to build on it. The README file will give them some information about what the application does.
We've used GitHub to host our project, but now we're going to publish it online as a working site. In the introduction, I asked you to create a Heroku account. We're now going to use that to deploy our code.
Heroku uses Git to receive code, so we'll be setting up our repository to push code to Heroku as well.
Now let's create a Heroku app:
heroku createCreating limitless-basin-9090... done,
stack is cedarhttp://limitless-basin-9090.herokuapp.com/ |
[email protected]:limitless-basin-9090.git
Git remote heroku added
My Heroku app is called limitless-basin-9090. This name was randomly generated by Heroku when I created the app. When you generate an app, you will get a different, randomly generated name.
My app will be available on the Web at the http://limitless-basin-9090.herokuapp.com/ address. If you deploy your app, it will be available on an address based on the name that Heroku has generated for it.
Note that, on the last line, Git has been configured too.
To see what has happened, use the following command:
git remote show heroku* remote heroku
Fetch URL: [email protected]:limitless-basin-9090.git
Push URL: [email protected]:limitless-basin-9090.git HEAD branch: (unknown)
Now let's deploy the application to the Internet:
git push heroku master
Now the application is online for all to see:
The initial version of the application, running on Heroku
The page looks a bit sad. Let's set up a standard page structure and use a templating language to lay out our pages.
A templating language allows us to create the HTML for our web pages in a clearer and more concise way.
There are many HTML templating systems available to the Sinatra developer: erb , haml , and slim are three popular choices. We'll be using Slim (http://slim-lang.com/).
Let's add the gem:
gem 'slim'
bundle
We will be keeping our page templates as .slim files. Sinatra looks for these in the views directory.
Let's create the directory, our new home page, and the standard layout for all the pages in the application.
mkdir views
p address book – a Sinatra application
When run via Sinatra, this will create the following HTML markup:
<p>address book – a Sinatra application</p>
doctype html html head title Sinatra Address Book body == yield
Note how Slim uses indenting to indicate the structure of the web page.
The most important line here is as follows:
== yield
This is the point in the layout where our home page's HTML markup will get inserted. The yield instruction is where our Sinatra handler gets called. The result it returns (that is, the web page) is inserted here by Slim.
Finally, we need to alter address-book.rb.
Add the following line at the top of the file:
require 'slim'
Replace the get '/' handler with the following:
get '/' do slim :home end
bundle exec rackup -p 3000
The following is the new home page:
Using the Slim Templating Engine
Have a look at the source for the page. Note how the results of home.slim are inserted into layout.slim.
Let's get that deployed.
git add views/*.slim
Also add the changes made to the other files:
git add address-book.rb Gemfile Gemfile.lock
Commit the changes with a comment:
git commit -m "Generate HTML using Slim"
git push heroku master
To give a slightly nicer look to our pages, we can use Bootstrap (http://twitter.github.io/bootstrap/); it's a CSS framework made by Twitter.
Let's modify views/layout.slim. After the line that says title Sinatra Address Book, add the following code:
link href
There are a few things to note about this line.
="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css"
rel="stylesheet"
Firstly, we will be using a file hosted on a Content Distribution Network (CDN ). Clearly, we need to check that the file we're including is actually what we think it is.
The advantage of a CDN is that we don't need to keep a copy of the file ourselves, but if our users visit other sites using the same CDN, they'll only need to download the file once.
Note also the use of // at the beginning of the link address; this is called a "protocol agnostic URL". This way of referencing the document will allow us later on to switch our application to run securely under HTTPS, without having to readjust all our links to the content.
Now let's change views/home.slim to the following:
div class="container" h1 address book h2 a Sinatra application
We're not using Bootstrap to anywhere near its full potential here. Later on we can improve the look of the app using Bootstrap as a starting point.
Remember to commit your changes and to deploy to Heroku.
As things stand, during local development we have to manually restart our local web server every time we want to see a change. Now we are going to set things up with the following steps so the application reloads after each change:
group :development do gem 'unicorn' gem 'guard' gem 'listen' gem 'rb-inotify', :require => false gem 'rb-fsevent', :require => false gem 'guard-unicorn' end
The group around these gems means they will only be installed and used in development mode and not when we deploy our application to the Web.
Unicorn is a web server—it's better than WEBrick —that is used in real production environments. WEBrick's slowness can even become noticeable during development, while Unicorn is very fast. rb-inotify and rb-fsevent are the Linux and Mac OS X components that keep a check on your hard disk. If any of your application's files change, guard restarts the whole application, updating the changes.
Finally, update your gems:
bundle
guard :unicorn, :daemonize => true do `git ls-files`.
each_line { |s| s.chomp!; watch s }end
mkdir config
listen 3000
guard
Now if you make any changes, the web server will restart and you will get a notification via a desktop message. To see this, type in the following command:
touch address-book.rb
You should get a desktop notification saying that guard has restarted the application.
Note that to shut guard down, you need to press Ctrl + D .
Also, remember to add the new files to Git.
We want our application to be robust. Whenever we make changes and deploy, we want to be sure that it's going to keep working. What's more, if something does not work properly, we want to be able to fix bugs so we know that they won't come back.
This is where testing comes in. Tests check that our application works properly and also act as detailed documentation for it; they tell us what the application is intended for. Our tests will actually be called "specs", a term that is supposed to indicate that you write tests as specifications for what your code should do.
We will be using a library called RSpec . Let's get it installed.
group :test do gem 'rack-test' gem 'rspec'end
bundle
mkdir spec
$: << File.expand_path('../..', __FILE__)
require 'address-book'
require 'rack/test'def app
AddressBook.newendRSpec.configure do |config|
config.include Rack::Test::Methodsend
mkdir spec/integration
require 'spec_helper'describe "Sinatra App" do
it "should respond to GET" do get '/'
expect(last_response).to be_ok
expect(last_response.body).to match(/address book/) endend
What we do here is call the application, asking for its home page.
We check that the application answers with an HTTP status code of 200 (be_ok).
Then we check for some expected content in the resulting page, that is, the address book page.
bundle exec rspec Finished in 0.0295 seconds1 example, 0 failures
Ok, so our spec is executed without any errors.
There you have it. We've created a micro application, written tests for it, and deployed it to the Internet.
This article discussed how to perform the core tasks of Sinatra: handling a GET request and rendering a web page.
Further resources on this subject: