Rack, and Why It Matters
Posted by Adam Wiggins on June 19, 2008 at 05:37 PM
Rack is one of the most important developments in the Ruby web space in the past year. I suspect it's been slow to get attention because the benefits are a bit subtle. Witness the Rails core team being confused about Rack just a few months ago. So if you don't get what the deal is with Rack, don't feel bad - you're in good company.
James covered Rack in his Railsconf talk, partially at my insistence. (His talk was about Mongrel handlers, but Rack middleware is a newer and better way to achieve the same end.) It's worth noting that he asked the crowd - a couple hundred Rubyists - whether they had heard of Rack, and almost every single hand went up. But when he asked if they knew what it was for, not a single hand was raised.
So what's the deal with Rack? In short, Rack provides a standard interface between the web app server and the app framework. This is useful in light of the multiplying number of web app servers (Webrick, Mongrel, Thin, Ebb...) and frameworks (Rails, Merb, Sinatra, Ramaze...). A standard not only reduces the amount of code the framework authors have to write, but it makes the layers in the stack more pluggable. Pluggability encourages experimentation (which means more innovation over time), and generally makes the whole stack more robust.
One implication of Rack is that you can skip the app framework altogether. I've always liked using use standalone Mongrels running tiny Ruby apps without a framework for internal daemons. These days, I generally use Sinatra for that purpose - but there's still something cool about skipping the use of any framework and just coding down to the metal.
Want to try it out yourself? Stick this code into hello.ru:
class HelloHandler def call(env) [ 200, { 'Content-type' => 'text/plain' }, 'hello, world' ] end end run HelloHandler.new
Then at the shell:
rackup hello.ru & curl http://localhost:9292/
Congratulations, you've just made a frameworkless Ruby web application in five lines of code.
A Rack handler is anything that can respond to the call method and returns an array with the status code, output headers, and output body. Handlers can be the end of the request chain, or do input and output filtering anywhere in the middle (hence "middleware"). Here's an example from Marc-André Cournoyer. Though his example is presented for Thin, you can run his code on any Rack-compatible server.
In the real world, what is Rack middleware useful for? We recently ported the Heroku toolbar to Rack middleware. The previous implementation was several hundred lines of very hard-to-follow monkeypatching of ActionController, combined with a rarely-used and poorly-maintained plugin framework for Mongrel call GemPlugins. (Which I nominate for Most Confusing Name Ever.) That code was hard to read and nearly impossible to spec, but it's the only way we could make it work with the traditional Mongrel/Rails setup. It was also very tightly coupled to a particular version of Rails and Mongrel.
Ricardo (one of the new Heroku devs) banged out the Rack middle port in just a couple of days. It's a fraction the number of lines of code, and can be speced normally. Plus, our toolbar is now compatible with any Ruby app server or framework.
Because Rack separates the layers of the stack more cleanly, it was way easier to hook the Heroku toolbar code into the right place. Take that lesson and generalize it, and you'll start to glimpse the significance of Rack.
Comments
There are 7 comments on this post. Post yours →
Cool... is this "standard interface between the web app server and the app framework" comparable to WSGI in the python world?
These days, I generally use Sinatra for that purpose - but there's still something cool about skipping the use of any framework and just coding down to the metal.
I spent the first 10 years of my career programming almost exclusively in assembler. My first job out of college (in 1974) was to write a realtime disk operating system for a 16-bit minicomputer —I started with a two pass assembler and a loader. Both on paper tape.
I cannot adequately express how uproriously funny it is to see what you refer to as "coding down to the metal". I mean, like, LOL.
I'm surprised that nobody from audience at RailsConf knew what Rack is for. I think that Rack is one of the best thing happened to Ruby during last months. It gives flexibility because we can inject some layers (middleware) and do other interesting stuff (like combining several applications into one). Rack reduce dependency between framework (rails, merb, camping etc) and application server (mongrel, thin, ebb) so there is no need to worry about "is this new framework runs on this server?" or "is this new server support this framework?". It is simple and amazing.
@Charlie - Yes, in my limited understanding of WSGI, it serves exactly the same purpose of Rack.
@Adam, Charlie: Rack is based on WSGI specification. Author admitted it several times in blog posts, docs.
Nice post. I like how simple it is to make a 4 line server. Using rack, will your 'call' function be called with many threads?
@chris - That's up to the app server (Mongrel, Thin, etc). A stock Mongrel is multithreaded by default, Thin is not, although Thin can now handle specific requests that need to be threaded via the deferred? patch.
Post a comment
Required fields in bold.