Just Do It: Learn Sinatra, Part Four

Welcome to the last part of this series. The aim of this series of tutorials has been to take somebody who has never used Sinatra from creating an app to deploying it. You can find the other parts of the series at the following links:

After those three parts, we have now finished most of the main functionality of our To Do List app. In this final part we are going to look at using Sass for the CSS before deploying the app on the Heroku cloud hosting service.

A Logo

Every self respecting app needs a logo, so I’ve created a simple ‘tick’ logo that you can find on Github. We can use this in the app by setting it as the background image of the main heading. Open up styles.css and add the following CSS:

h1.logo{
  font-family: helvetica,arial,sans-serif;
  font-size: 24px;
  color: white;
  padding: 64px 0 0;
  margin: 0 auto;
  text-transform: uppercase;
  text-align: center;
  font-weight: normal;
  letter-spacing: 0.3em;
  background: transparent url(/logo.png) 50% 0 no-repeat;
  }

(Note – in case you haven’t noticed, you will need to add a class of ‘logo’ to the h1 element in layout.slim for this to work)

I’ve also used the logo to create a favicon which you can also find on Github. To display this, you will need to add a link to it in the html. This means that layout.slim should now look like this:

doctype html
html
  head
    meta charset="utf-8"
    title Just Do It!
    link rel="shortcut icon" href="/favicon.ico"
    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.logo Just Do It!
    == yield

Getting Sassy

Sass (Syntactically Awesome Stylesheets) is a CSS templating language, which means that you write your styles in Sass which then gets compiled on the server side into the CSS that is sent to the browser. It’s extremely cool and is basically everything that CSS should have been. There are two flavors of Sass – the original ‘Indented Sass’ and the more recent ‘SCSS’ (Sassy CSS). I’m going to use SCSS as this is much more like normal CSS, but it’s definitely worth trying both and seeing which you prefer.

To incorporate Sass into your your Sinatra project, we need to install the Sass gem:

gem install sass

Next we have to add the following handler to the end of main.rb:

get '/styles.css' do 
  scss :styles
end

This basically picks up the reference to styles.css that we already have in our layout file and loads the Sass file called ‘styles.scss’. This file doesn’t exist yet, so go ahead and create it. It should be saved in the ‘views’ directory. Copy the contents of styles.css (in the public folder) into styles.scss and then delete styles.css.

If you take a look at how the app now looks in the browser, nothing should have changed. This is because SCSS is a superset of CSS, so any regualr CSS will just work as expected, so all we have just done is moved from using standard CSS to Scss. But now we’re ready to start turbo-charging our styles.

CSS Variables

One of the most requested items on CSS wish lists is variables. Sass gives us the ability to use variables. For example we can set the colours using variables that are referenced in the markup

$orange:#D78123;
$blue:#1E1641;
$green:#02d330;
$grey: #999;

We can now reference these colours in the rest of the stylesheet. For example, the headings in each list to be orange I can write the following in styles.scss:

.list h1{
  color: $orange;
}

This makes it much easier to maintain as if a number of elements use this color and then I decide to make it a bit darker, I only have to change it in one place. There’s lot’s more you can do with variables, including arithmetic – be sure to check out the documentation.

CSS Mixins

Mixins are a really cool feature of Sass that allow you to create modular and reusable CSS. For example, we will create a mixin called button that contains all the relevant CSS to turn a standard link into a button:

@mixin button($color){
    background:$color;
    border: 2px solid darken($color,15%);
    cursor: pointer;
    color: #fff;
    width: 150px;
    margin-left:4px;
}

I can now add these common styles to any other CSS declaration in my stylesheet by using the @include statememnt:

  .button{
    @include button($orange);
    }

This will add all of the CSS from the button mixin to any element with a class of button. Of course we could have just written the CSS directly within the ‘button’ class, but using a mixin allows us to add it to any other CSS declaration without having to repeat any code. Notice also that the mixin includes a color argument that allows us to have different colored buttons without having to repeat any code. Notice also that we were able to use the orange color that was held in the variable $orange.

Nested CSS

Another cool feature of Sass is the ability to nest your declarations. This can save a lot of typing and adds more structure to your CSS file.

For example, the following CSS was used to style the lists and the headings in each list:

.list{
  float: left;  
  background: #fff;
}
.list  h1{  
    color:$orange;
}

This can be written more efficiently by nesting the h1 declaration inside the .list declaration, like so:

.list{
  float: left;  
  background: #fff;
  h1{  
    color:$orange;
  }
} 

The Scss above will compile to the same CSS as the example above it, but it has the advantage of showing the nested structure. It can also save a lot of typing if you start to use some deeply nested CSS.

Sass is such a powerful tool that makes writing CSS so much easier and it is so simple to implement in Sinatra that isn’t really any reason to not use it in all of your projects. Especially since you can just write standard CSS and incorporate the extra ‘sassy’ features as and when you need them. I have given a few examples here that only begin to touch the surface of what Sass is capable of. I’d encourage you to have a look through this, read the documentation and, most importantly, play around with some of your own code. I promise that once you start using Sass, you won’t go back to regular CSS!

There isn’t enough room to show the full Scss file, but you can see the full code on Github. Now that we’ve finished making the app look pretty, it’s time to deploy it.

Heroku

Heroku is an excellent service that can be used to host Sinatra apps in the cloud. It is free for basic sites, includes database storage and will scale as your app becomes more popular. It takes all of the hassle out of hosting sites and can be used for large scale production sites. They also have excellent documentation and support.

The first thing you need to do, if you don’t have one already, is get a Heroku account. Next, make sure that you have Git installed on your system. If you haven’t, there’s a useful guide on Github. Make sure you pay particular attention to the section on setting up SSH keys as you will need these to access Heroku.

Next up, you’ll need to install the Heroku gem which allows you to interact with your apps on Heroku:

gem install heroku

If it’s your first time on Heroku you will need to add those SSH keys you just created (you only need to do this once):

heroku keys:add

Now we can create the app on Heroku:

heroku create justdoit

(Note that you will have to use a different name for your app, as justdoit is already taken by me!)

Now you’ve set up Heroku, it’s time to get your app ready for deployment.

Bundler

Before we deploy the app to Heorku, we need to use Bundler to manage all of the dependencies that our app has. These include all of the gems that we have been using as well as the gems that they depend on. Bundler installs all of these gems directly into the app’s directory. This means that your app comes ‘bundled’ with all of it’s dependencies so you are not relying on anything being installed on the server by default (Heroku literally has NO gems installed by default anyway). You can also manage the version number of any gems so that you can ensure that the versions used in production are the same as those used in development.

To get started, we need to install the Bundler gem:

  gem install bundler

We now have to create two new files in the main app folder. First of all, we need a Gemfile that lists all of the gems that we have used. Save the following code in a file called Gemfile (no extension) in the root directory of your app:

source :rubygems
gem "sinatra"
gem "datamapper"
gem "slim"
gem "sass"
gem "dm-postgres-adapter", :group => :production
gem "dm-sqlite-adapter", :group => :development

The last two lines specify the database adapters that Datamapper uses to connect to the database. We have so far been using Sqlite on our development machine, but Heroku uses PostgreSQL. Using different databases in different environments is handled by placing the database adapter gems into what Bundler calls groups.

We also need a Rackup file. Open up your text editor and save the following as config.ru:

require 'bundler'
Bundler.require
require './main'

DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{Dir.pwd}/development.db")

run Sinatra::Application

Notice that I have placed the database connection line in this file – it is common to put configuration code in this file. This means that this line now needs removing from main.rb.

Last of all you need to test this out locally, so we run the bundle install command which creates a .bundle directory that contains all of the gems listed in our Gemfile.

bundle install --without production

The --without production flag ensures that any gems in the production group don’t get installed.

There is no need to run this command in production – Heroku will run it automatically if it detects a Gemfile. We do, however, have to tell Heroku to avoid using any gems in the ‘development’ group. This is done with a simple command:

heroku config:add BUNDLE_WITHOUT="development:test" --app justdoit

(note that you will have to use the name of your app instead of justdoit)

Before we move on to deploying the app, we should just check that everything is running fine on our local machine. This is done in a different way now that we have a rackup file. Open up a command line prompt and type the following:

rackup

This will launch the server on port 9292. Go to http://localhost:9292/ to see how it looks.

Deployment

All that is left to do is deploy the app to Heroku. First of all we need to create a file called .gitignore that is saved in the root directory and contains the following 2 lines:

development.db
.bundle

This will stop any unwanted files being added to the git repository. We don’t want the Sqlite database file to be in the git repository because Heroku doesn’t use it. We also don’t want the .bundle directory to be sent to the production server (as it will have its own version).

The next job is to initialize the Git repository:

git init
git add .
git commit -m 'initial deployment'

Last of all, we push the app to Heroku

git push heroku master

As you develop your app, it’s very easy to update the code on Heroku. It’s simply a case of commiting the code into the Git repository then pushing them to Heroku using the follwoing commands:

git add .
git commit -m 'some message about the updates'
git push heroku master

The only thing left to do is to set the database up on the server by running the migrations. The Heroku command line interface allows you to access an irb console that allows you to interact with your app:

heroku console
Ruby console for justdoit.heroku.com
>> DataMapper.auto_migrate!

You can open the website from the command line using the following command:

heroku open

This should take you to your live app. You can see my finished version of Just Do It! – feel free to play around with it. I have also placed all of the source code on github so you can have a look at all the files in their entirety.

That’s All Folks

And that brings to a conclusion this four part series. I hope that it has given you enough information to get started with Sinatra as well as the desire to want to learn more. The best way to learn of course is to experiment yourself and Sinatra has such a low barrier to entry that it makes it incredibly easy to start new apps and play around with code. I’d love to know if any of you want more Sinatra articles and what you would like to see covered – leave any feedback in the comments.

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.

  • SinatraDev

    Darren I used to read your old I Did It My Way blog and I think in this four part series for Ruby Source most of the tutorial was filled with non-Sinatra things like Haml and Sass and CSS. I don’t see the point in adding Haml and Sass to an Intro to Sinatra to tutorial because it’s just more stuff to confuse the newcomers with. Plus whilst making Sinatra apps that use Haml and Sass might be fun when you’re working on a project by yourself, when you have to bring in extra developers, Haml and Sass justs makes the learning curve greater and slows down productivity. I think you should have just stuck with Erb templates to be honest, but to each his own. If you enjoy using them then that’s fine, I have nothing wrong with that but I just think that for a tutorial they don’t add anything. If anything they make the tutorial a bit more bloated.

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

      Hi Sinatradev,

      Thanks for the feedback. I’m sorry that you think that including Sass and Slim (not Haml) added bloat to this series. Part of my objective was to go through the process of building a full app from start to finish, so I felt that there needed to be some CSS in there and to be honest, using Scss doesn’t take a lot more effort than normal CSS. I also feel that Slim is much easier to pick up than erb and use this exclusively myself now. I would disagree a little about these options being difficult to use when working in teams – Scss is a superset of CSS, so if somebody didn’t know it they could just use plain CSS and Slim shouldn’t be too difficult to pick up. But like you said, each to their own (I did mention that there were other options available). Another thing I was trying to show was how easy it is to use these extra technologies with Sinatra. This part didn’t really add any actual Sinatra code, but I wanted to walk people through the steps of how to deploy an app using Bundler and Heroku. I hope it hasn’t ended up being too confusing for anybody trying Sinatra for the first time.

      cheers,

      DAZ

  • Siri

    I just finished this tutorial and ran into an issue.

    When you have us add the styles for h1.logo there is a $logo-color variable which wasn’t defined and doesn’t get defined. Sadly, sass doesn’t provide much error messaging so you have to track this down yourself. Just wanted to save anyone else the trouble.

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

      Thanks for spotting that Siri, I’ve updated the post so that it just states the actual colour for now.

      cheers,

      DAZ

  • rsludge

    I think, these are awesome Sinatra tutorials, I learned many new things from them.
    Thank you, Darren, waiting for your new series.

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

      Cheers rsludge! Your feedback means a lot, I’m really pleased that you learnt some new stuff.

      DAZ

  • Steve Robillard

    I would like to see an article covering security escaping output, sanitizing input etc.

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

      Nice idea Steve. I’ll definitely try to incorporate that into some future posts.

      DAZ

  • John

    Do you have the code from the 4 parts for download somewhere ? I have been following and have hit a few snags that i think are related to putting the wrong code in the file, in the wrong place.

    Anyway to see the complete code so i can compare and learn from it ?

    Thanks,
    John

  • John

    Well I found out what the problem was. I dont quite understand it. But the fix is this:

    Where you use .get method I changed it to .find and it all works great.

    I am using Sinatra on JRuby with Active Record. Here is the code changes:

    require ‘rubygems’
    require ‘sinatra’
    require ‘active_record’
    require ‘haml’

    ## Setup
    configure do
    ActiveRecord::Base.establish_connection({
    :adapter => “jdbcsqlite3″,
    :dbfile => “tasks.sqlite”
    })
    if File.exists?(‘tasks.sqlite’)
    ## Dont remake database file
    else
    ActiveRecord::Schema.define do
    create_table “tasks”, :force => true do |t|
    t.column “name”, :varchar
    t.column “completed_at”, :timestamp
    end
    end
    end
    end

    ## Models
    class Task nil)
    end

    def link
    #{self.name}
    end
    end

    Didnt paste it all, just the DB parts I changed. Changing get to find doesnt need pasting. :)

    Let me know what you think.

    BTW, I use AR since I cannot seem to get dm to work on JRuby. But AR works great. )

    Thanks,
    John

  • baiki

    This is just brilliant! Thanks so much Darren. Just ordered the Sinatra book (you should claim your % from the author) :-)

    Typo here:
    1 git init
    2 git add .
    3 git commit -m ‘initial delployment’

    delpoyment = deployment

    and in previous part:
    given in a from (should be: given in a form)

    Thanks a bunch.

    Baiki

  • Gaston

    Fantastic series of tutorials. Thanks!

  • http://daz4126.com/ Darren Jones

    Hi Baiki,

    That’s really eagle-eyed of you – thanks for spotting those typos! I’ve corrected them both now.

    cheers,

    DAZ

  • ragu

    I actually preferred this combination in your tutorial. Sinatra + Slim + Sass. In fact, after I decided this might be interesting combination, I googled it and found this tutorial. :)

    Thanks!

    • http://daz4126.com/ Darren Jones

      Thanks ragu!

      Glad you like Sinatra + Slim + Sass, I think they are an awesome combination!

      DAZ

  • Alejandro

    I just wanted to say thank you for this excellent tutorial.

    I’ve been studying rails + sintra + heroku the past couple of weeks
    and this tutorial has been the most valuable resource
    in all the internet.

    I see you are writting a book based on this contents….
    I think is going to be great.

  • Vince Yuan

    Excellent tutorial! It introduces Sinatra, DataMapper, bundle and Heroku.

    The code in Part 1~3 works very well. However, the code in Part 4 does not work (maybe because ruby framework and heroku changed). I even tried the code on github.

    At last, I figured it out.
    1. In order to run server locally, add this line at the beginning of main.rb
    require ‘data_mapper’
    Otherwise, when you run rackup, you will see this error:
    main.rb:9:in `': uninitialized constant Task::DataMapper (NameError)
    from /Users/vince/GitRoot/PracticeProjects/Sinatra/JustDoIt/main.rb:8:in `’

    2. In order to run server on heroku, do the following steps:
    a. Create a postgress db on heroku (after u create an application on heroku)
    $ heroku addons:add heroku-postgresql:dev
    b. config heroku
    $ heroku config:set BUNDLE_WITHOUT=”development:test”
    b. Replace DATABASE_URL with HEROKU_POSTGRESQL_AMBER_URL in config.ru
    c. Add this line in Gemfile
    gem “pg”, :group => :production
    d. commit your changes and push to heroku
    e. $ heroku run console
    f. In heroku console, run following commands
    require ‘sinatra’
    require ‘slim’
    require ‘data_mapper’
    require ‘./main’
    DataMapper.setup(:default, ENV['HEROKU_POSTGRESQL_AMBER_URL'] || “sqlite3://#{Dir.pwd}/development.db”)
    DataMapper.auto_migrate!

    Now the app should work on heroku.

  • Vince Yuan

    I need to add more info to my previous comment.

    When u run this command to add a postgress database, you will NOT get HEROKU_POSTGRESQL_AMBER_URL
    $ heroku addons:add heroku-postgresql:dev
    Your postgres db url should be different. Check the output of that command. My second postgress db url is HEROKU_POSTGRESQL_BLUE_URL . The output is

    $ heroku addons:add heroku-postgresql:dev
    Adding heroku-postgresql:dev on XXXXXX… done, v4 (free)
    Attached as HEROKU_POSTGRESQL_BLUE_URL
    Database has been created and is available
    ! This database is empty. If upgrading, you can transfer
    ! data from another database with pgbackups:restore.
    Use `heroku addons:docs heroku-postgresql:dev` to view documentation.

  • Vince Yuan

    I need to add more info to my previous comment.

    When u run this command to add a postgress database, you will NOT get HEROKU_POSTGRESQL_AMBER_URL
    $ heroku addons:add heroku-postgresql:dev
    Your postgres db url should be different. Check the output of that command. My second postgress db url is HEROKU_POSTGRESQL_BLUE_URL . The output is

    $ heroku addons:add heroku-postgresql:dev
    Adding heroku-postgresql:dev on XXXXXX… done, v4 (free)
    Attached as HEROKU_POSTGRESQL_BLUE_URL
    Database has been created and is available
    ! This database is empty. If upgrading, you can transfer
    ! data from another database with pgbackups:restore.
    Use `heroku addons:docs heroku-postgresql:dev` to view documentation..