I’m referring not to an instrument of torture, nor industrial furniture for computers, but to the increasingly popular Rack Web server interface. I’ve found, first, that the explanations of what it does aren’t that great, and second, that it’s ideal for my current at-work project. I bet it’d be useful for lots of others too, so here’s my shot at an introduction.

What I’m Doing · Helping implement the compute part of the Sun Cloud API; my part is a bridge from the RESTful HTTP messaging in the API to the back-end infrastructure from our recent Q-layer acquisition. It’s short bridge, since the Q-layer semantics are pretty generic. The shape of their API is quite a bit different, though, so there is some pipe-fitting required. On the other hand, I’m not doing any persistence to speak of.

Since I need to get this done in a hurry, I wanted to write the code in Ruby. I also wanted a nice friendly-looking URI space, since this is apt to be the first implementation of the API that anyone sees.

So what I needed was something that would grab requests from a Web server and hand over the details to arbitrary Ruby code and let me figure out what to do. Routing, bah. MVC, pfui. ORM, what’s that?

Rack · I knew this thing was out there, and I knew that it was being used in both Rails and Merb, and that the kool kids thought it was a kool tool. But the explanations were sort of arm-wavey and I was having trouble figuring out what it actually did. Most helpful was Dan Webb’s 8 minutes on Rack.

Well, here’s what it does: you wire it up to a Web server and give it an object with a call method, and for every HTTP request it calls call with one argument, a Hash containing everything you’d ever want to know about the request.

How would I “wire it up to a Web server”, you ask? It comes with built-in wiring for most of ’em. For example, here’s how I wired it up to Mongrel.

web = ComputeAPI::Web.new Rack::Handler::Mongrel.run(web, :Port=>8080)

Not much to it, is there?

What’s The Argument? · I was poking around trying to figure out exactly might be in that Hash that Web#call method was going to get, and decided that it’d be easier to just print it out than find the docs:

class HelloWorld def initialize(name) @name = name end def call(env) r = [ "<html><head><title>#{@name}</title></head>" + "<body><h2>#{@name}</h2><ul>" ] env.each_pair do |n, v| r << "<li><p>" r << "#{n} => #{v}" r << "</p></li>" end r << "</ul></body></html>" [200, { 'Content-Type' => 'text/html' }, r ] end end require 'rubygems' require 'rack' Rack::Handler::Mongrel.run(HelloWorld.new("Tim"), :Port => 4321)

Notice the other Rack trick: that triple of HTTP status, HTTP headers, and message body that you have to return. Anyhow, I’m not going to reproduce the page of HTML this thing gives you; But I do recommend running this if you’re going to play with Rack, to find out what’s really in that hash.

To do that, put the above in a file called x.rb or some such, then say ruby x.rb , then point your browser at http://localhost:4321/foo?bar=baz .

Dispatching · So, here’s that Web#call method that Rack calls for me:

def call(env) uri = path(env) case env['REQUEST_METHOD'] when 'GET' then get(uri, env) when 'POST' then post(uri, env) when 'PUT' then put(uri, env) when 'DELETE' then delete(uri, env) end end # chop off the common prefix, leaving what we dispatch on def path(env) env['REQUEST_URI'][@base.length .. -1] end def get(uri, env) headers = {} type = nil case uri when '/' rep = VDC.new(@cloud).representation type = 'VDC' when %r{^/vnets/(.*)$} rep = get_vnet($1) type = 'VNet' if rep # ...lots more regexps elided... if (type) headers['Content-Type'] = "application/vnd.com.sun.cloud.#{type}+json" [ 200, headers, rep ] else headers['Content-Type'] = "text/plain" [ 404, headers, "Can't fetch #{uri}" ] end end

I’ve omitted some error-handling. It’s hard to imagine how you could do this any more simply.

Intended Usage · I think the people who built Rack were looking at WSGI and thinking of something to slip in under big complicated frameworks like Rails and Merb and so on. And I gather it works great for that.

But if you need to do some lightweight Web pipe-fitting, I think Rack is hard to beat.

And I was thinking how easy it might be to glue together Rack and the AtomPub protocol and something like Redis or CouchDB and have yourself a handy-dandy low-rent media publishing system. Restrain yourself, boyo, you’re a cloud-jockey these days.