One of the signature changes of the Rails 2.3 release is a complete reworking of the Rails internals to be Rack compatible.
If the people I talk to about Rails are any indication, then reaction to this move has ranged all the way from “Wow, that’s really cool” to “I have no idea what you are talking about”.
Here’s a quick look at what Rack is, and what it brings to Rails. I freely admit that I’m only starting to explore this stuff myself, but it’s already starting to change the way I’m thinking about structuring new Rails applications.
What is Rack?
Rack is a simple interface layer that sits between a Ruby web server, such as Mongrel or Phusion Passenger, and a framework, such as Sinatra, Merb, or Rails.
Rack’s goal is to encapsulate the common task of converting a web request into a web response into a common interface that can be implemented by handlers on the server and framework side. In this case, it’s a single method call(env), with a specified input argument, and a specified output format. The spec is pretty simple.
Once you have a simple common structure like Rack in place, you get a couple of interesting benefits.
One is that as the author of a framework, you don’t have to re-invent this particular wheel, and compatibility with all Rack-aware servers comes for free.
Rack Middleware
As an application developer, you also have the ability to create small programs called Rack Middleware, which also conform to the Rack specification. If you are using Rack by itself, you can build up an entire web application just out of middleware pieces. Within Rails, you can inject middleware into your application to do some task either before or after the Rails framework handles the request.
Before tasks might include modifying headers, checking caches, or checking browser data. After tasks might include filtering content, error handling, or logging.
The simplest possible Middleware app looks like this:
class HelloWorld
def call(env)
[200, {"Content-Type" => "text/plain"}, ["Hello world!"]]
end
end
The input is a hash with several pre-defined elements, the output is an array that is basically [status code, headers, body], where the body is a list of items that are concatenated together to make the final response body.
More complex Rack middleware can access the underlying application to get the response for filtering. Let’s say I had a big problem with people typing my name backwards and really wanted to stomp that out:
class LeonIMeanNoelMiddleware
def initialize(app)
@app = app
end
def call(env)
status, headers, response = @app.call(env)
new_response = response.map do |part|
part.gsub(/Leon/, 'Noel')
end
[status, headers, new_response]
end
end
This piece of middleware calls back to the underlying Rack application — conceptually, that’s the framework, but it’s managed by Rack — then goes through each part of the response and replaces my backwards name with my forwards name, passing the new response on to whatever the next middleware component is.
Rack in Rails Server
Making Rails Rack-compatible involves changing Rails internals to match the Rack specification, and then allowing developers to add middleware components to Rails.
There are several places where the Rails internals changed for Rack. On the server side, the server.rb file that is called when you run script/server changed to be Rack compatible. Here’s the small part of it where the Rails server defines itself as a Rack application.
app = Rack::Builder.new {
use Rails::Rack::LogTailer unless options[:detach]
use Rails::Rack::Debugger if options[:debugger]
map map_path do
use Rails::Rack::Static
run inner_app
end
}.to_app
This does a lot in a few lines of code, and I’m not sure I’ve completely unpacked it. It’s using the Rack-provided Rack::Builder, which is a DSL for defining Rack applications.
According to this, the Rails server is:
- A module that tails the log to the console unless the application is running headless
- A module that starts the debugger if requested
- For any path under the Rails URL root (usually, every path):
- Return a static resource the URL points to one
- Run ActionController::Dispatcher.new — that’s the default value of
inner_appunless a custom Rack configuration file is specified
That defines the default Mongrel/WEBrick server that you get from running script/server, which you can customize with your own Rack configuration file if you want.
Rack on Rails Framework
On the framework side of the equation, Rails defines itself in Rack terms inside ActionPack. You can see this stack by running the Rake task rake middleware. You’ll get something like this:
use Rack::Lock
use ActionController::Failsafe
use ActionController::Session::CookieStore, {:secret=>"dae6545a6341007ea6463b1ef257166f855c6c8ae2fbcfd379abd2b38fe52427201335b99dd91f6b4e8f6bfdd8f9bc1b81ba48bf76f3d68e055ed688bdf8c301", :key=>"_huddle_session"}
use ActionController::RewindableInput
use ActionController::ParamsParser
use Rack::MethodOverride
use Rack::Head
use ActiveRecord::QueryCache
run ActionController::Dispatcher.new
Without going into very much detail, this list specifies some of the steps that Rails goes through to respond to each request — the names of these modules should be a rough guide to what they do. All the modules are defined in actionpack/lib/action_controller/middleware — read the source, Luke.
You can customize this stack with your own Middleware by including it in the config block of the environment.rb file. The middleware class needs to be either in the Rails load path, or explicitly included.
config.middleware.use LeonIMeanNoelMiddleware
And In Conclusion, I’m Done — Or Not
Hope that helps as a introduction to Rack on Rails. Be sure to check out the rack-contrib github project to get a sense of what Middleware components have already been created.


Hi,
Thanks for this great explanation.
It’s still important that experienced users share some information about new inside technology in Rails that are not visible at a first glance.
Nicolas.
[...] It’s Only Rack on Rails But I Like It – Noel Rappin goes through and gives a brief introduction to Rack, why it’s good for Rails, and how to use it from a Rails app. [...]
Thanks for the article. Could you let me know where should I include the where should I put the code snippet in server.rb so that I can run WEBrick with Rack? A code sample will be great.
Thanks!
Lee
Nice write up!
This is actually a good starting point if you want to have a thin API for your rails app to integrate with different applications. Best examples are catchers from other Applications.