– programmer, co-founder openredis


Composing apps

Modularity is probably one of the most important properties of maintainable software. Quoting from Wikipedia:

Modularity is a logical partitioning of the "software design" that allows complex software to be manageable for the purpose of implementation and maintenance. The logic of partitioning may be based on related functions, implementation considerations, data links, or other criteria.

Personally, it’s all about managing complexity. If it was left to computers, they couldn’t care less if an OS was written in one main function. But we humans, work best when thinking about separate parts of a bigger whole.

Real world example please

Ok now that the philosophical part is out of the way, let’s take the most basic example of modularity in a web app.

For the sake of argument, Let’s say that we want to put up a support website for a commercial product. We would need the following:

  1. A Blog (for hype generation purposes :-)
  2. A support tracking system
  3. Documentation

Assuming we have for the domian, we will have the following URL endpoints:

Now let us pretend that we want to specifically write all of this in one Rack app. How do we go about doing this?

We’ll make use of the Rack::Builder method called map.


require File.expand_path("site", File.dirname(__FILE__))

map "/blog" do
  run Blog

map "/support" do
  run Support

map "/docs" do
  run Docs

Pretty simple and elegant looking right? Just a few things you need to be aware of:

  1. The path you pass to map needs to have a prefixed slash.
  2. The app is exactly the same as a standard rack app. It should respond to a call method and should respond with a rack tuple.
  3. The PATH_INFO is adjusted within the context of the mounted app. So if you go to /support/submit, the PATH_INFO will simply be /submit.

We’ll make use of our pet project Frank which was discussed in the previous article, Understanding Rack.

# site.rb

require File.expand_path("../helloworld/frank", File.dirname(__FILE__))

class Blog < Frank
  get "/" do
    "Main Blog"

  get "/post/:id" do |id|
    "Displaying post #{id}"

class Support < Frank
  get "/" do
    "Main Support Site"

  get "/submit" do
    "<form method='post'><textarea name='issue'></textarea>" +
    "<input type='submit' name='submit value='Help'></form>"

  post "/submit" do
    "Received issue: %s" % request.params["issue"]

class Docs < Frank
  get "/" do
    "We're here to help!"

  get "/download" do
    "Download our documentation in PDF or read it online!"

Play by play

So what just happened? If you noticed, the apps are matching against URLs as if they were on the root of the domain.

The reason for this is that when Rack maps a sub app, it mutates two environment variables, namely SCRIPT_NAME and PATH_INFO.

The following realtime state should illustrate what I’m trying to say.

1. We receive the request /docs/download. SCRIPT_NAME="", PATH_INFO=/docs/download
2. Rack intercepts /docs and transfers control to the sub app. SCRIPT_NAME="/docs", PATH_INFO=/download
3. Our app parses against PATH_INFO. SCRIPT_NAME="/docs", PATH_INFO=/download

Also, see the actual source code for more details:

You might be wondering though, how about redirects? How should the app know what the actual full PATH_INFO should be?

Luckily Rack has thought of that for us. If we’re using Rack::Request, (which most of us do), then accessing Rack::Request#path will yield the concatenation of SCRIPT_NAME and PATH_INFO.

If you must access the original values, you can through their respective accessors, namely path_info and script_name.