Rails meets Sinatra #2 - Mix n' Match 8

Posted by pratik
on Sunday, November 16

In my previous post, we saw how to mount a sinatra application at a specific location for a regular application. But using Rack::Cascade, we can take that solution to a whole new height. That is :

  • Put sinatra code in any of your regular Rails controllers.
  • No need to mount at Sinatra at a specific URI.
  • Have Sinatra work for any URI, gracefully fallback to Rails if no Sinatra method matches the path.
  • Use your models/libraries etc. in both Rails and Sinatra.

So your controllers can look like :

1
2
3
4
5
6
7
8
class HomeController < ApplicationController
  get '/' do
    Item.count.to_s
  end
  
  def index
  end
end

and even :

1
2
3
4
5
6
7
8
9
class SiteController < ApplicationController
  get '/:name' do
    Site.find_by_name(params[:name]).html
  end

  def new
  end

end

And environment.rb:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
RAILS_GEM_VERSION = '2.2.0' unless defined? RAILS_GEM_VERSION
require File.join(File.dirname(__FILE__), 'boot')

Rails::Initializer.run do |config|
  config.gem "sinatra"
  config.time_zone = 'UTC'
  config.action_controller.session = { :session_key => '_monkey_session',  :secret => 'whatever' }
end

set :env, :production
disable :run, :reload

# Remove trailing slash from URIs reaching Sinatra
before { request.env['PATH_INFO'].gsub!(/\/$/, '') if request.env['PATH_INFO'] != '/' }

# Checking AR Connections back to the pool
after { ActiveRecord::Base.clear_active_connections! }

# Preload controllers with Sinatra code
require 'home_controller'
require 'site_controller'

And most important, the rack setup file :

1
2
3
4
5
6
7
8
9
require File.dirname(__FILE__) + '/config/environment'
require 'thin'
  
app = Rack::Builder.new {
  use Rails::Rack::Static
  run Rack::Cascade.new([Sinatra.application, ActionController::Dispatcher.new])
}.to_app
 
Rack::Handler::Thin.run app, :Port => 3000, :Host => "0.0.0.0"

And start your server !

[lifo@219 monkey]$ ruby racked.rb
Comments

Leave a response

  1. DEkartNovember 17, 2008 @ 09:28 AM

    This is a nuke feature! Is there any way to run Merb application in the same way? Try to catch request by merb and fall back to rails?

  2. PratikNovember 17, 2008 @ 12:05 PM

    @DEkart : Merb blindly copied a lot of things from Rails. They renamed classes for many cases, however there are very higher chances of a colision. YMMV PDI :)

  3. DEkartNovember 17, 2008 @ 12:09 PM

    ok. I’m already thinking about how to port my application developed with rails+merb to cascade application

  4. DEkartNovember 17, 2008 @ 12:10 PM

    Does this work with Rails 2.1?

  5. PratikNovember 17, 2008 @ 12:17 PM

    It doesn’t work out of the box on Rails 2.1. But you can easily copy Rails’ rack handler from thin or use thin, and make it work.

  6. ztroyNovember 27, 2008 @ 01:24 PM

    Thanks for your wonderful posts about sinatra, I’ve found out that it is more than worth a look, actually I am trying to integrate it into an existing rails app to handle file uploads, but when i try to run my racked.rb it prompts me for an error?

    undefined method `after’ for main:Object (NoMethodError)

    when i have in my environment.rb setup this line of code:

    1. Checking AR Connections back to the pool after { ActiveRecord::Base.clear_active_connections! }

    running rails -v 2.2.0 / ruby -v ruby 1.8.7 (2008-08-11 patchlevel 72) [i686-darwin9.5.0]

  7. dreamcat4May 24, 2009 @ 01:55 PM

    Its possible to run the thin server as described above. However instead of racked.rb script, its better to make a config.ru file in your rails root. Like this:

    yourrailsapp/config.ru: require File.dirname(__FILE__) + '/config/environment' require 'thin' app = Rack::Builder.new { use Rails::Rack::Static run Rack::Cascade.new([Sinatra::Application, ActionController::Dispatcher.new]) }.to_app run app # This line is different

    Then just run:
    thin start -R config.ru <other server options>

    for example
    thin start -R config.ru -e production -p 3000 -d
    will run your rails app (including sinatra) in the background in production mode.
    Then its just like running a proper server. Hooray !

    Also, not sure why we need this bit:
    # Checking AR Connections back to the pool after { ActiveRecord::Base.clear_active_connections! }
    I decided to leave it out. Seemed to work okay.

  8. Yasser DahabJune 17, 2009 @ 07:53 PM

    It’s also possible to blend a Sinatra application (or multiple Sinatra applications) with Rack::Builder and Rack::Cascade. Just include ‘sinatra/base’ instead of Sinatra, then create a module ModuleName that contains class ClassName < Sinatra::Base

    Then you can mix and match by calling the appropriate ModuleName::ClassName for each of your Sinatra apps.

Comment