Sinatra: A Real Site

2010/05/31

sidcom.me.uk

The Site

I have a young relative who draws a lot of comic strips, and thinking it would be nice if he could publish them, I looked around for an Open Source system.

I wanted something that was as near to XKCD as possible - latest comic, next/previous and random.

Not finding anything that was both pretty and ultra-simple, I decided to write my own.

The version for my relative is here - the system will be up on GitHub as soon as I finish making it configurable.

Technologies

Web Framework

If you've read the title of this post, you won't be surprised to read that I chose Sinatra.

Database

I decided to use DataMapper as the ORM.

I'm also using SQLite for both development and production.

HTML and CSS

As will be clear from the example site, I didn't work over hard on making a fresh graphic look -

all I did was strip out a lot of HTML that was there to make rounded corners,

and replaced it with Mozilla and WebKit specific rounding.

I then translated the HTML into HAML and

the CSS into SASS (SCSS).

Development

Documentation for Sinatra, DataMapper, HAML and SASS is quite good, so things went quite smoothly.

What follows is a list of the phases I went through while developing (on Ubuntu), and especially the bits I had trouble with.

DataMapper

Installation

$ sudo gem install dm-core
$ sudo gem install data_objects
$ sudo gem install data_mapper
$ sudo gem install do_sqlite3

Using DataMapper in Code

require 'dm-core'
require 'data_mapper'

DataMapper.setup({
  :adapter => 'sqlite3',
  :host => 'localhost',
  :username => '',
  :password => '',
  :database => 'db/sidcom_development'
})

class Comic
  include DataMapper::Resource

  property :title, :text
  property :permalink, :text
  property :created_at, :datetime
  property :updated_at, :datetime
end

Comic.auto_upgrade!

The 'Comic.auto_upgrade!' bit handles migrations -

at startup, table columns are adjusted to match the declaration in the equivalent class.

Rack

Authentication

This application has a 'public' and an 'admin' area, so I actually created two Sinatra Apps, wrapping one in Rack::Auth::Digest::MD5.

A local settings file holds user names and hashed (kinda salted) passwords.

Sinatra

Sinatra apps have access to a number of globals, I used:

Helpers

This app being a little more complex than my first, I decided to implement some Rails-like helpers.

The most important helper is url_for.

Sinatra - by design - includes routing in the structure of an app, so it cannot provide pre-cooked methods of the sort.

HAML

I found HAML very easy to start using, what I didn't immediately understand was:

  • how to interpolate variables,

  • how to call functions,

  • and how to include partials.

These are actually all the same problem, but it took me a while to realize that!

In HAML, you can interpolate the Ruby way (with '#{...}') or the HAML way (with '=').

Variable Interpolation

Just use Ruby String interpolation:

%h1 Hello #{ @name }!

Calling Functions

Use HAML evaluation syntax:

%h1

= @name

!

HAML partials

Again, these can be evaluated as above:

= haml(:_my_partial, :layout => false)

SASS

Generating CSS

I chose to keep my SCSS files in a subdirectory under my views directory, and generate CSS from them under the static path.

During development, you need to keep the static versions up to date, and to do so, you just need to keep a process running as follows:

$ sass --watch views/stylesheets:static/stylesheets

Conclusion

Sinatra handles small sites very well.

The code can be kept DRY, and if you decide to, Sinatra projects can be quite easily transformed into Rails projects.

Actually, I have doubts whether it was worth doing this project in Sinatra -

a stripped-down Rails 3 project would have been just as lightweight and easy to develop.

But, then again, it's useful to run a system through its paces, to have an idea what areas it can usefully be applied to.