By Darren Jones

Build a Sinatra MVC Framework

By Darren Jones

In the last chapter of my book, Jump Start Sinatra, I suggested that the code produced from the book could be refactored into a MVC structure similar to Rails. I left this as an exercise for the reader to complete, but have decided to write up my attempt on here. If you don’t have the book then shame on you(!), but you should be able to follow along with most of what is written here in a general sense. The excellent Padrino shows what can be achieved by building on top of Sinatra, so it should be a fun project to build a microframework from scratch that could be used to make organizing code a bit easier, while maintaining the simplicity of Sinatra.

Songs By Sinatra

In the book, we build a sample application called Songs by Sinatra. This is a site dedicated to the songs of the great Frank Sinatra. It allows a user who is logged in to add songs by Ol’ Blue Eyes, including the title, date, length and lyrics. It also allows visitors to the site to ‘like’ these songs. The live site can be seen here.


Creating the File Structure

The first job is to create the file structure. Since we are using an MVC structure, it makes sense to have ‘models’, ‘views’ and ‘controllers’ folders. I also decided to create a ‘helpers’ folder and ‘lib’ folder for any extensions and middleware.

In the book, we created a small piece of middleware to handle assets (such as CoffeeScript and Sass files), so we have an ‘assets’ folder, as well as the standard public folder for all the publicly available resources, such as images.

Here’s a diagram of my folder structure:


One Controller to Rule them All

I also suggest in the book to write a global controller called ApplicationController. This should use all the views, layouts and register any extensions used by the whole application.

$:.unshift(File.expand_path('../../lib', __FILE__))

require 'sinatra/base'
require 'slim'
require 'sass'
require 'coffee-script'
require 'v8'
require 'sinatra/auth'
require 'sinatra/contact'
require 'sinatra/flash'
require 'asset-handler'

class ApplicationController < Sinatra::Base

  helpers ApplicationHelpers

  set :views, File.expand_path('../../views', __FILE__)
  enable :sessions, :method_override

  register Sinatra::Auth
  register Sinatra::Contact
  register Sinatra::Flash

  use AssetHandler

  not_found{ slim :not_found }

This requires all the necessary gems that are used and then creates an ApplcationController class. This will be the base class for all controllers. It registers the ApplicationHelpers, which will be where all application-wide helpers go.

The views folder needs to be set here, as it is not located in the same directory as our application_controller.rb file which is what Sinatra expects. This is easy to change, though, using the set command.

We also enable sessions and method_override here. Sessions are needed to use sinatra-flash and will also be required for most applications. The method_override setting is used to allow browsers to support HTTP methods such as PUT, PATCH and DELETE using a POST method and hidden input field in a form.


In the book, I went through building an Auth extension module. I also explained how to write some helper methods for sending a contact email. I extracted these and the contact routes into their own extension so that it could be set separately

The settings for these can be set in the ApplicationController, so the code in the extensions doesn’t need to be edited at all.


Other Controllers

The other controllers now inherit from the ApplicationController class. There are two controllers in this application – the WebsiteController, responsible for the main part of the site and the SongController, responsible for all the CRUD operations performed on the Song model.

class WebsiteController < ApplicationController
  helpers WebsiteHelpers

  get '/' do
    slim :home

  get '/about' do
    @title = "All About This Website"
    slim :about

class SongController < ApplicationController
  helpers SongHelpers

  get '/' do
    slim :songs

  get '/new' do
    slim :new_song

  get '/:id' do
    slim :show_song

  post '/songs' do
    flash[:notice] = "Song successfully added"
    redirect to("/#{}")

  get '/:id/edit' do
    slim :edit_song

  put '/:id' do
    flash[:notice] = "Song successfully updated"
    redirect to("/#{}")

  delete '/:id' do
    flash[:notice] = "Song deleted"
    redirect to('/')

  post '/:id/like' do
    @song.likes =
    redirect to("/#{}") unless request.xhr?
    slim :like, :layout => false


There is only one model in this case – the Song model. In the model directory, there is just one file song.rb that creates the Song class and sets up all the DataMapper properties:

require 'dm-core'
require 'dm-migrations'

class Song
  include DataMapper::Resource
  property :id, Serial
  property :title, String
  property :lyrics, Text
  property :length, Integer
  property :released_on, Date
  property :likes, Integer, :default => 0

  def released_on=date
    super Date.strptime(date, '%m/%d/%Y')


Note that I’m using DataMapper for this model, but could easily use another ORM. In fact, I could even use a different ORM in a another model.


Each controller has its own helper file, so there is application-helpers.rb, website-helpers.rb, and song-helpers.rb. These are created as a module and then each controller has to explicilty register the associated helpers module. Here is the application helpers file:

module ApplicationHelpers
  def css(*stylesheets) do |stylesheet|
        "<link href="/#{stylesheet}.css" media="screen, projection" rel="stylesheet" />"

 def current?(path='/')
   request.path_info==path ? "current": nil

It is registered in application_controller.rb with the following line:

helpers ApplicationHelpers

The application helpers are where all the global helper methods go. The two above are used in the layout to make it easier to include links to CSS files and another helper to add a class of ‘current’ to a link if it is linking to the current page.


The is where all the configuration is done:

require 'sinatra/base'

Dir.glob('./{models,helpers,controllers}/*.rb').each { |file| require file }

SongController.configure :development do
  DataMapper.setup(:default, "sqlite3://#{Dir.pwd}/development.db")

SongController.configure :production do
  DataMapper.setup(:default, ENV['DATABASE_URL'])

map('/songs') { run SongController }
map('/') { run WebsiteController }

First of all we require Sinatra::Base (as opposed to Sinatra, since we are using a modular-style structure). Then we require all the files kept in the models, helpers and controllers folders.

After this we do a bit of configuration for the SongController class to set up the database for the production and development environments. This could have been placed in the song_controller.rb file, but the seemed the best place to put the configuration.

Last of all we use the handy map methods that Rack gives us to create a namespace for each controller.

That’s All Folks

Sinatra’s flexibiltiy and modular style made it very easy to refactor the application into a framework-style structure. I think it hits a nice balance between code organization and still keeping things relatively simple. You still have to do a few more things by hand – like setting up views and database connections by hand.

Going forward I think I’d like to look at perhaps adding a few more extensions, using a better Asset Handler and organizing the database and ORM a bit better. I’ve put all the code on GitHub and called it ‘Jump Start’. The name seems doubly appropriate given how it came about and because it helps you ‘jump start’ your Sinatra projects. Would you use Jump Start? Can you think of any ways to improve it? Have you created your own bespoke framework using Sinatra? As usual, let us know in the comments below.

The most important and interesting stories in tech. Straight to your inbox, daily. Get Versioning.
Login or Create Account to Comment
Login Create Account