Sinatra: Building an ActiveRecord and Postgres application

As you may already know Sinatra is a great Ruby framework for building small web apps and APIs. Although it's very different to Ruby on Rails, there are sometimes cases where the choice of which framework to use can be hard. Is the app small enough for Sinatra? is the app big enough for Rails? When this is the case one option would be to use Sinatra with some Ruby on Rails components such as ActiveRecord. This would then make the upgrade path from Sinatra to Ruby on Rails smoother if ever needed.

In this article we will work through a short example of building a Sinatra web application for taking notes, using ActiveRecord and Postgres.

The first step is to create a Gemfile:

source 'https://rubygems.org'

gem "sinatra"
gem "pg"
gem "activerecord"
gem "sinatra-activerecord"

We require four gems: sinatra, for obvious reasons. pg, for postgres support. activerecord, to interact with the database. sinatra-activerecord, to add some extension methods and rake tasks for Sinatra and ActiveRecord. bundle install can now be run to install these gems.

To allow us to use the rake commands from sinatra-active record we need to add rakefile.rb.

require "./app"
require "sinatra/activerecord/rake"

Next the config.ru can be created to load the application

require './app'
run Sinatra::Application

Now this is all in place the application can be built in a file named app.rb

require 'sinatra' require 'sinatra/activerecord'

At the top of the application we need to require sinatra and sinatra/activerecord. We can then add in the database details. (If you don't have a postgres database, go create one now.)

db = URI.parse('postgres://user:pass@localhost/dbname')

ActiveRecord::Base.establish_connection(
  :adapter  => db.scheme == 'postgres' ? 'postgresql' : db.scheme,
  :host     => db.host,
  :username => db.user,
  :password => db.password,
  :database => db.path[1..-1],
  :encoding => 'utf8'
)

First we get the database URL and parse it, this can be a full database URL as shown above, or an environment variable for hosting on platforms such as Heroku. The ActiveRecord connection is the established using the data from the database URL. The next step would be to define a database.

class Note < ActiveRecord::Base
end

In Ruby on Rails this would be a model, but for Sinatra we just drop it straight into app.rb. We now need to create a migration to generate the notes table. Running the command rake -T will list all of the available rake commands. Running rake db:create_migration NAME=create_notes will generate a db folder with a migration, in that file we can add the following.

class CreateNotes < ActiveRecord::Migration
  def up
    create_table :notes do |t|
      t.string :title
      t.text :body

      t.timestamps
    end
  end

  def down
    drop_table :notes
  end
end

Here we're adding a title column, and a body column. We also ask ActiveRecords to add timestamps, which will add a created_at and updated_at column. ActiveRecord will also create an id column by default. If this ever needs to be reversed we add in the drop_table method to drop the notes table. To run this we use the rake db:migrate command. Now the database has been added, we can add the routes to app.rb.

get "/" do
  @notes = Note.order("created_at DESC")
  redirect "/new" if @notes.empty?
  erb :index
end

get "/new" do
  erb :new
end

post "/new" do
  @note = Note.new(params[:note])
  if @note.save
    redirect "note/#{@note.id}"
  else
    erb :new
  end
end

get "/note/:id" do
  @note = Note.find_by_id(params[:id])
  erb :note
end

The first route it for "/", this gets all notes in descending created order, if this returns empty, then the user is redirected to "/new" otherwise the index view is loaded. The "/new" route just loads the new view, all this view contains is a form with a title and body field which posts to "/new". The post route for "/new" creates a new note from the form parameters, if this saves the user is redirected to the note, otherwise it just loads the new view. Finally the "/note/:id" route loads a specific note based on the id, then loads this in the note view. All of the views along with the full code can be found on GitHub.

Once complete, running the command bundle exec rackup will start the application, which will be accessible at http://localhost:9292/

For more help with Sinatra, ActiveRecord or Postgres, please get in touch

Twitter