Sinatra, Datamapper, Sass and Heroku

Here are a few notes on a simple prototype I just built with Sinatra, deploying on Heroku. It’s easy, but like everything (it seems) there are always a few tricks you need to remember.

To start, here’s an edited listing of my pthingy project directory (pthingy stands for phonethingy). The main ruby file is pthingy.rb. It essentially contains everything except the templates which are in the views directory.

doug@zeus ~/code/spikes/pthingy (master)
$ la
.gems
Rakefile
config.ru
pthingy.db
pthingy.rb
public/
seeds.rb
views/
doug@zeus ~/code/spikes/pthingy (master)

Setup

Note you have to require libraries explicitly such as haml and sass. For datamapper, you can require just the specific libraries you need or the big kahuna: datamapper.

require 'rubygems'
require 'sinatra'
require 'dm-core'
require 'dm-timestamps'
require 'haml'
require 'sass'

DataMapper

The keys here include requiring the correct libraries you need as well as performing a setup. Note the class definitions include the association assignments.

## Models / DB
#
# If you want the logs displayed you have to do this before the call to setup
DataMapper::Logger.new($stdout, :debug)

DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{Dir.pwd}/pthingy.db")
configure :development do
  enable :logging, :dump_errors, :raise_errors
end

# log = File.new("sinatra.log", "a")
# STDOUT.reopen(log)
# STDERR.reopen(log)

class Event
  include DataMapper::Resource

  property :id,         Serial   # An auto-increment integer key
  property :event_tag,  String   # An event key string.
  property :created_at, DateTime # A DateTime, for any date you might like.

  has n, :posts

end

class Post
  include DataMapper::Resource

  property :id,         Serial   # An auto-increment integer key
  property :url,        Text     # URL of image.
  property :created_at, DateTime # A DateTime, for any date you might like.

  belongs_to :event
  has n, :comments

end

class Comment
  include DataMapper::Resource

  property :id,         Serial
  property :posted_by,  String   # IP address of submitter
  property :body,       Text
  property :created_at, DateTime # A DateTime, for any date you might like.

  belongs_to :post

end

DataMapper.finalize
require  'dm-migrations'
DataMapper.auto_migrate!
# Create or upgrade all tables at once, like magic
# DataMapper.auto_upgrade!

Need to go back and see about auto-upgrade!

HAML

You can set the default format with the set command. You can set utf-8 for content in a before filter. You can use the haml command to render haml views.

set :haml, {:format => :html5 } # default Haml format is :xhtml

# filter
# set utf-8 for outgoing
before do
 headers "Content-Type" => "text/html; charset=utf-8"
end

## Handlers
#
#
get '/' do
 haml :root
end

Helpers

Helpers for views can be added and used in a similar way as Rails.

helpers do
  def post_image(post)
    url = post.url
    image_str = "<img src=" + url + '>'
    "<a href='/comments/" + post.id.to_s + "'>" + image_str + "</a>"
  end

  def how_long_ago(comment)
    diff = DateTime.now - comment.created_at
    hours, mins, secs, ignore_fractions = Date::day_fraction_to_time(diff)
    "%02d:%02d" % [ mins, secs ] + " minutes ago:"
  end
end

SASS

I stumbled a bit on setting this up. There are some incorrect posts out there. This worked for me. NOTE: the styles.sass file goes in the ./views directory.

get '/style.css' do
  content_type 'text/css', :charset => 'utf-8'
  sass :style
end

Heroku

.gem file

Ok, now to publish on heroku. The first thing to do is create a .gems file in the project directory. While most of these things are very straight forward, you have to add a database adapter. In the case of Heroku that means postgres.Note that in development I was using sqlite3.

Caveat: I don’t think you need to specify both the datamapper library and it’s individual libraries.

sinatra -v 1.0
dm-core --version 1.0.0
dm-timestamps --version 1.0.0
dm-migrations --version 1.0.0
haml --version 3.0.9
datamapper
dm-postgres-adapter
ffaker

config.ru

Nothing complicated required here.

require 'pthingy'
run Sinatra::Application

And that’s it! It should work….

As long as you didn’t do something wrong, of course.