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 logo

Get a better logo, guys.

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.



Contributions

Comment feed for ongoing:Comments feed

From: Peter Keane (Mar 25 2009, at 22:04)

Right on -- this is an excellent combination. A good key-val store (like CouchDB, Redis, SimpleDB, GAE, etc.) running behind an AtomPub server will be killer -- a simple, easily replicated application stack.

We use an AtomPub server as a "higher-level" object store to build on top of. While CouchDB or whatever is a backend to you AtomPub server, the AtomPub server is the backend (or a piece of it) for the cool Mashups that others will build on top. I think it's as that high level protocol (not just a publishing engine) that AtomPub will shine.

[link]

From: jim@jimpick.com (Mar 25 2009, at 22:51)

I like the Rack logo. It's isometric. :-)

[link]

From: Seth Ladd (Mar 25 2009, at 23:08)

I find that Sinatra is really the sweet spot, especially for what you are doing. More than Rack, less than Rails. I'd give it a serious look. It runs under Rack, so anywhere you can use Rack you can use Sinatra.

[link]

From: David (Mar 26 2009, at 00:10)

I second the Sinatra recommendation -- what Seth said...

[link]

From: Mike Moore (Mar 26 2009, at 00:57)

Sinatra is awesomesauce for sure.

Also check out Jon Crosby's excellent presentation on Rack from the recent MountainWest RubyConf:

http://mwrc2009.confreaks.com/13-mar-2009-11-05-in-a-world-of-middleware-who-needs-monolithic-applications-jon-crosby.html

[link]

From: Chris Dent (Mar 26 2009, at 02:29)

I love WSGI. Rack looks pretty cool but with a somewhat simpler signature than WSGI, resulting in a somewhat more complex return. I'm not sure which I would prefer. Is there a Perl thing like WSGI and Rack? If not I've been thinking about making one.

Fairly raw WSGI is what drives TiddlyWeb, http://tiddlyweb.peermore.com/, and it is exactly a "handy-dandy-low-rent media publishing system", especially if you choose to think about it outside the TiddlyWiki context. In the TiddlyWiki context it may be even lower rent.

[link]

From: Ray Krueger (Mar 26 2009, at 03:52)

I was just going to mention Sinatra too :P

So I guess I'll "Third" what Seth said, and add a bit.

Sinatra adds just enough framework around routing to be awesome, without any additional bloat. You'd at least be able to get rid of those boiler-plate case statements. For this one usecase Rack looks fine but as soon as you start to add different resources to this API I think it'll get kinda clunky.

[link]

From: Edward (Mar 26 2009, at 05:43)

I love Rack. It's good for simple web applications where a full Rails deployment is overkill. My site http://www.ordoacerbus.com is written directly on Rack in about a screenful of Ruby code. My hosting site has Passenger which makes it run reasonably efficiently.

I haven't tried Sinatra as suggested by some of your other commenters so I'll take a look at that.

[link]

From: Eric Meyer (Mar 26 2009, at 05:46)

Hey Tim, could you maybe link to a copy of the page of HTML that thing produces? I'd be interested in seeing it, and it would serve as an interesting bit of "here's what Rack does for you" documentation.

[link]

From: John Hart (Mar 26 2009, at 10:17)

Based on my limited research, Perl's Catalyst framework goes further up the stack, but at its lowest level looks pretty similar to how you describe Rack.

Paths are mapped to subs using annotation, be they literal:

sub bar : Path('foo/bar') {...}

or regexps:

sub bar : Regex('^item(\d+)/order(\d+)$') {}

[link]

From: Matt Todd (Mar 26 2009, at 14:36)

I think it's important to mention that deployment is very simple for Rack apps with Phusion Passenger (an Apache mod_rack module, essentially).

Also, check out this Rack-based framework I wrote in about 150 lines of code; it's called, pardon my language, Fuck: http://github.com/mtodd/fuck

A simplistic example of the power that Rack gives for creating clean frameworks beyond your common MVC mega-framework.

Lastly, Rack middleware are an important part to why Rack is so great for framework/utility developers and to application developers alike: they solve a great number of common idiomatic problems easily and consistently, and do so in ways that are easily layered and added to existing Rack-based apps!

[link]

From: Jochen Kupperschmidt (Mar 26 2009, at 16:00)

I have a Python background and the introduction of WSGI really made me happy. Seeing Rack gain popularity in the Ruby world is good news.

As opposed to WSGI, Rack only passes the environment mapping to the app, which is nice. There have been plans for an updated version of WSGI that drops the `start_response` callable altogether for quite a while, but it will take some time and effort to make that happen on a common and widespread basis.

Oh, and at least for Python, the environment mapping is mostly what CGI will use, but including some additional items like file streams for input and errors as well as information if the app uses multiple threads or processes and if it maybe run more than once.

P.S.: I've tried to submit my comment four times and I always got a different error message. Almost looks like there's a random error string generator behind it :)

[link]

From: So Awesome Man (Mar 26 2009, at 23:06)

@eric_meyer I happened to be googling and executing code I found... ;) the output that thing produces can be found here: http://gist.github.com/86562

@tbray obrigado for the post! Rack is awesome and so is your example!

[link]

From: Pete Lacey (Mar 27 2009, at 06:21)

I forget where I stole this from, but here's a simple explanation of Rack: Rack is to Ruby what the Servlet API is to Java, a low-level Web programming interface that you can count on being there.

[link]

From: Ed Davies (Mar 28 2009, at 11:56)

What I find interesting is that for a couple of years now somebody could have written something very similar for Java (Jack? - and it could have trivially added an option for multithreading) but, as far as I know, nobody did. Is there something about Java which makes it culturally difficult to just do something simple?

[link]

From: lenz (Mar 29 2009, at 14:44)

Rack looks interesting but have you had a look at mochiweb? it is erlang and not rails but looks quite similar and i did some wiring of different APIs with it the same way you did with yours. a plus is that you can deploy new versions of it on a running node with no downtime and all the other erlang goodies. worth a look i guess.

[link]

author · Dad
colophon · rights

March 25, 2009
· Technology (90 fragments)
· · Ruby (93 more)
· · Web (396 more)

By .

The opinions expressed here
are my own, and no other party
necessarily agrees with them.

A full disclosure of my
professional interests is
on the author page.

I’m on Mastodon!