Chatting on Twitter (2/2)

How to setup Sprockets without Rails. Self referencial association with DataMapper. Delayed job for Sinatra.


In the previous post, we have seen how to embed a chat widget with Coffescript. We mainly covered javascript events sent from the server to the clients using pusher. In this post we will see how to setup Sprockets without Rails, we’ll cover self referential associations for friendship relations, and finally we’ll setup Delayed Job with Sinatra to fetch user’s friends in background.

Source code can be found here: qop source

Sprockets without Rails

Sprockets is a utility to compile coffescript/javascript and stylesheets. It’s very useful when we write lots of javascript and we need a way to package all the files in a single application.js. It also supports ECO templates and automatically creates a global variable to store our javascript templates.

Blatantly taken from sprockets without rails courtesy of Simone Carletti, there is an effective way to setup Sprockets without Rails. The following code goes in a Rakefile.rb.

      require 'rubygems'
      require 'bundler'
      require 'pathname'
      require 'logger'
      require 'fileutils'
      require 'data_mapper'


      ROOT        = Pathname(File.dirname(__FILE__))
      LOGGER      =
      BUNDLES     = %w( application.css application.js )
      BUILD_DIR   = ROOT.join("public")
      SOURCE_DIR  = ROOT.join("assets")

      task :compile do
        sprockets = do |env|
          env.logger = LOGGER


        BUNDLES.each do |bundle|
          assets = sprockets.find_asset(bundle)
          prefix, basename = assets.pathname.to_s.split('/')[-2..-1]
          FileUtils.mkpath BUILD_DIR.join(prefix)

          assets.to_a.each do |asset|
            # strip multiple extensions
            realname = asset.pathname.basename.to_s.split(".")[0..1].join(".")
            asset.write_to(BUILD_DIR.join(prefix, realname))

          assets.write_to(BUILD_DIR.join(prefix, basename))

It is also useful to automatically save and compile javascript files without manually running rake tasks. To do so, we setup a watchr file, and install the gem watchr.

      def run_sprockets
        print "\nCompiling... "
        system('rake compile')
        print "done.\n"

      Signal.trap('INT') { abort("\n") }
      watch( 'assets/javascripts/*') { run_sprockets}
      watch( 'assets/templates/*') { run_sprockets}

Relationship status: It’s not complicated

We use Datamapper for out database models. It’s easy to use and we don’t have to worry about migrations (and that’s ok given this simplicity of this project). Documentation is really good, we reuse part of Self referential many to many relationships section in Associations and we add few utility methods.

      class User
        include DataMapper::Resource

        property :id,         Serial
        property :name,       String
        property :uid,        String, :required => true
        property :nickname,   String
        property :created_at, DateTime
        property :online,     Boolean, :default  => false

        has n, :friendships, :child_key => [ :source_id ]
        has n, :friends, self, :through => :friendships, :via => :target

        has n, :inverse_friendships, 'Friendship', :child_key =>[:target_id]
        has n, :inverse_friends, self, :through =>:inverse_friendships, :via =>:source
        def online!
 = true


        def friend?(friend)
          friends.first(:id => or inverse_friends.first(:id=>

        def offline!
 = false


        def online_friends
          fs = friends.all(:online=>true)
          ls = inverse_friends.all(:online=>true)
          fs + ls


      class Friendship
        include DataMapper::Resource
        belongs_to :source, 'User', :key => true
        belongs_to :target, 'User', :key => true

We have added online and offline methods, a method to get online friends and the inverse_friendship relation. If user A is friend with user B, then it means B is also friend with A. We store this relation only once in the Friendship table and use the inverse friendship relation.

Omniauth Twitter strategy using Sinatra

Omniauth makes it very easy to authenticate with Twitter. We just need to provide a callback method, and store the user informations.

      get '/auth/twitter/callback' do

        auth = request.env["omniauth.auth"]
        user = User.first_or_create({ :uid => auth["uid"]}, {
          :uid => auth["uid"],
          :nickname => auth["info"]["nickname"],
          :name => auth["info"]["name"],
          :created_at =>

        if user.nickname.nil? or user.nickname.empty?
          user.nickname = auth["info"]["nickname"]


        if user and user.nickname and user.friends.empty?
          job =
          Delayed::Job.enqueue job
        session[:user_id] =
        redirect ''

Sinatra and Delayed Job

In the previous code snippet we use Delayed Job to fetch our friends. We need to setup delayed job with Sinatra and define a couple of tasks to start and clear the job queue in our Rakefile.rb. Note that the following tasks are useful when we deploy the code to heroku.

      task :environment do
        require 'delayed_job'
        require 'delayed_job_data_mapper'
        require './models'
        require './job'
        DataMapper.setup(:default, ENV['DATABASE_URL'] || "sqlite3://#{Dir.pwd}/db.sqlite3")

      namespace :jobs do
        desc "Clear the delayed_job queue."
        task :clear => :environment do

        desc "Start a delayed_job worker."
        task :work => :environment do

            :min_priority => ENV['MIN_PRIORITY'],
            :max_priority => ENV['MAX_PRIORITY']).start

Delayed Job is now ready. We need to setup the actual worker:

      class LookupFriends <

        def perform
          friends = Twitter.friend_ids(nickname.to_s)
          user = User.first(:nickname => nickname)
          if friends.ids
            friends.ids.each do |id|
              friend = User.first_or_create(:uid=> id)
              user.friends << friend unless user.friends.first(:uid=> id)


In the previous post we’ve seen how to embed a chat widget inside a Twitter page, using pusher and CORS requests.

Behind the scenes though there are few tricks to talk about. The widgets is a coffescript single page mini application. Sprockets is an important tool to glue Coffescript files together. The database used is necessary to check for online or offline users. Finally, we’ve seen how to setup Delayed Job to fetch data in background, and populate the database with friends data.