At Least Read This Far

Rack is a "shim" between a web server and a Ruby web framework. It does the "grunt work" - the parsing of the HTTP, establishing the environment "stuff" (or HttpContext if you're a Microsoft person), and working with the web server so you don't have to.

That definition might make it seem like it's some massive chocolate cake framework - bloated and cranky dealing with the low-level machinery of the web. In fact, it's about as elegant as... well elegant can get. In fact I think it defines elegant.

Check it out - this is a Rack app:

class HelloRack def call(env) ["200",{"Content-Type" => "text/plain"}, "Hello World"] end end run HelloRack.new

You can run this by telling Rack to execute it: "rackup app.ru" - and, as you might expect, in your browser is "Hello World":

But what just happened? And who cares? If you break it down - well the short answer something pretty whimsical and fanstastic. "rackup" told Rack to find a web server and load our application, then told the web server to respond to requests on port 9000 (you can change that if you like) with our application. In other words: it put our code file here in touch with a web server - and made them play nice.

Why should you care? The short answer is that Rack freed me from worrying about which server I have installed - Mongrel, Lighty, Webrick, Apache - Rack will work with each of them (and more). This makes Rack very very tantalizing for the people who make frameworks - the Rails guys and Sinatra guys - because worrying about HTTP and "server stuff" sucks.

As Christian Neukirchen, the guy who created Rack, says:>Dabbling in my own web framework experiments, I noticed that there is a lot of code duplication among frameworks since they essentially all do the same things. And still, every Ruby web framework developer is writing his own handlers for every webserver he wants to use. Hopefully, the framework users are satisfied with that choice.

However, in essence, dealing with HTTP is rather easy. In the end, you get a request and return a response

It Really Is That Simple

This is the part that's always terifically hard to explain: Rack is so bleeding simple that it leaves you a bit at a loss. The rules are pretty simple:*Have at least one method called "call"

*"call" should except a single argument: "env" - which is a hash of "HTTP stuff"

*Return a single array, with 3 elements: status code, headers (themselves a hash), and a responseLet's break this down a bit more and I'll start in reverse order with "env" - the environment hash that gets passed in. If you're not a Ruby person, a hash is a key/value structure - like a Dictionary in C#. This is the majority of what Rack does: parse the HTTP into a hash that you can play with.

Here's a basic look at "env":

This is the "core" environment that you get with Rack - but it can be added to as needed (more on that below). This "stuff" is handed to every Rack application and that app, in turn, can do with it what it wants to.

Next - "call". As I mention there's only one method required and it's "call". The method is invoked by Rack during execution and is expected to return some type of response - status code, headers, and whatever. That last part is important:

the response can be anything (sort of).

In the example above, it was a string, but it can also be something much more structured - like an ActionView from Rails. The

only condition is that the response responds to "each".

Or, for C# developers, that it's "enumerable". This makes it easy for Rack to "digest and return" a response: it simply has to enumerate it into a string, and that's what is sent to the browser.

This might seem a bit weird: "how the hell does this underpin a framework?". The answer is like Rack, wonderfully simple:

Rack applications are meant to be chained.

Up to this point I've been referring to things as "Rack applications" - and that's not completely accurate. Well, it is but it doesn't paint the entire picture. There's a 3rd person in the room here that you need to get to know.

Middleware and Applications

OK so far we understand the core of what a Rack app does, and I've also just stated that Rack apps are meant to be chained. Let's refine that statement a bit and say that there are two kinds of Rack apps: "Middleware" (aka "Filters") and Applications.

Applications are the things that get called first, they are a top-level concern and are usually where the developer's code goes. In a Sinatra app this would be "app.rb"

Middleware is consumed by the application along the line - as well as being consumed by other middleware. Each of these bits of middleware works on the response (and, in some cases, the environment variable) - passing them along the chain.

If your brain juices are bubbling right now - they should be. Parsed HTTP, chainable calls - it's the perfect ingredients for a set of utilities! And a set of utilities is the perfect ingredient for... a framework!

The guys that maintain Rack think so too - and

they've done a lot of the heavy-lifting for you:

This is just a fraction of the utilities out there. You have everything you need to build your very own Ruby framework - right now! Well, except for a few more tricks - let's get to that.

There's a ton of middleware out there that you can use - but how do you get it into your framework? And how does it get called? The answer: Rack::Builder.

To understand this, let's create some middleware. It will simply wrap our output with some styling - I'll call it "Rack::Massive":module Rack class Massive def initialize(app) @app = app end

def call(env)
  status, headers, response= @app.call(env)
  [status, headers, "

{response} - it's all small stuff"]

end

end end

This is going to look strange to you - I've introduced a few things here. Hang tight - I'll explain in just a second. We need an app to kick things off here:

class SmallStuff def call(env) ["200", {"Content-Type" => "text/html"}, "Don't Sweat The Small Stuff"] end end

Rack's Henchman - Rack::Builder

OK - here comes the explanation. I have two Rack apps here - the first is a bit of middleware to work on a response as it "travels by". The second is a top-level application that will "kick things off". The difference here is that the middleware introduces a constructor (in Ruby "initialize" is the constructor construct).

Rack will pass along the top-level application into the middleware using this constructor and the middleware will, as you're probably guessing by now, ask for a response from that app.

There's some orchestration happening here: these bits of middleware need to be assembled in some order and the top-level app made available to all - and that's where Rack::Builder comes in. It does a bit more than this, but this post is already long enough!

That's the final part here: we need to tell Rack::Builder what to do and how - this goes in our Rackup file which is, at it's core, simply a set of instructions for Rack::Builder. By convention we'll call it "config.ru" - which is precisely what it is, a "startup configuration":

require 'rubygems' require 'rack'

use Rack::Massive run SmallStuff.new

When you execute this in the browser...

Pretty neat - we told Rack::Builder to "plug in" bits of middleware using the "use" method, and we finally, at the end, told it that our app was SmallStuff. Rack::Builder then executed things in reverse, LIFO-style.

The first thing it did was to execute "call" on SmallStuff, creating the "app" that will get sent along. Then, in reverse order, it passed app through the filters, awaiting a response. It then takes that response and...

Further Discussion

I probably should stop this post here - it's pretty long. But I'm thinking that maybe you'll come back to it later on if you ever need to and I would be remiss in not finishing it up properly.

There are two parts to Rack::Builder's life - the queueing of the Rack middleware and apps, and the building of the response. In the example I just gave, Rack::Builder queues up Massive first, then SmallStuff, and executes "call" on each of the queued bits of middleware.

In our example above, we built up the response in the "call" method - but that's not exactly appropriate nor condoned. It made for a good demo, but it causes some issues in terms of rendering. A better situation is to build on top of the response when Rack asks you to - which is towards the end, when Rack gets around to assembling things.

So it goes a bit like this:*Builder queues up SmallStuff, and then Massive as directed in "config.ru"

*Builder executes "call" in reverse order: starting with SmallStuff and then working its way down to Massive. All the while it's tossing the responses from each call into what I'll call "the response queue"

*Builder then iterates items in "the response queue" - calling "each" on each one, which builds the final response

*The final response is sent off to the web serverIt might twist your brain into a pretzel if, at this point, I told you that Rack::Builder was a Rack application itself. Which is true - you can see the execution of this process right here in Rack::Builder's source:

OK - so why do you care about all of this? It's a good thing to keep in mind when using bits of Rack middleware and plugging them into your apps - this is the execution cycle you'll have to understand. Moreover, if you decide to build out your own middleware someday, it will be worth it to know how to "inject" your response into the response chain properly. Hijacking "call" is considered "not so useful".

So that's the last thing I'll show you. Let's rewrite our Massive filter above to write to the response when asked by Rack. We do this by following a bit of convention. This will look weird if you don't know Ruby - so I'll toss the code out first, and describe it in a second:

class Massive def initialize(app) @app = app end

def call(env) status, headers, @response= @app.call(env) [status, headers, self] end

def each(&block) block.call(" ") @response.each(&block) block.call(" - it's all small stuff") end end

A bit ago I mentioned that all Rack asks is that the response you give - the 3rd item in the return array for "call" - responds to "each". A string will do just that in Ruby, so our "Hello World" example is just fine. In Rails the ActionView will also respond to each, but it does so by working up templates and layouts, etc.

Doing it this way allows for a common approach to building responses - you don't have to rely on the presence of some property like "body" - only the fact that your response will be iterated.

So this code does two things - it puts itself as the response! This may seem really whack, but doing it this way allows for the execution to happen "in proper order", rather than in reverse. By defining "each" on Massive, we're saying "Hey Rack - iterate THIS" and, in the course of the iteration, the fatty div tags get put in place.

If you're a little hazey on the "&block" syntax there -

I wrote a post on them previously that can help you a bit. You can think of them as delegates, if that helps you out.

Summary

Big post, small summary: you should know, understand, and cuddle up to Rack. It's not a new concept - Python's WSGI does the same stuff and was the model Christian followed when creating Rack.

Hope you found this useful...