Rails 3 has an incredibly handy feature I recently used at frēstyl, but unfortunately it is not well documented: you can map a route directly to a Rack application.

Why would you want to do this? Imagine that your user profiles have pretty urls which include the username — something like http://website.com/users/awesomesocks . However, the username can change, which then changes the profile url. What you would like is to have both a permanent url and pretty url that point to the same place. When you visit the permanent url — http://website.com/users/42876543, for example — you are redirected to the most up-to-date pretty url. Assuming you maintain a cached mapping of permanent urls to pretty urls, there is no reason to bring the full Rails stack into the equation. So like a good performance-minded coder, you think of Rack.

If you add something like the following to your routes file:

match '/users/:permanent' => PermaRouter, :constraints => { :permanent => /\d+/ }

all requests that match the route conditions will be sent to PermaRouter. According to the Rails 3 documentation, as long as [PermaRouter] responds to call and returns a [status, headers, body] , the router won’t know the difference between the Rack application and an action (Routing to Rack Applications). Specifically, this means that somewhere in your load path you need a class like the following:

class PermaRouter def self.call(env) ... end end

Fleshing out this example a bit more, you want to look up the permanent id in your cache, replace the :permanent portion of the url with the username, and redirect to the result:

class PermaRouter def self.call(env) request = Rack::Request.new(env) params = env['action_dispatch.request.path_parameters'] permanent = params[:permanent] username = PermaCache.find(permanent) [301, { "Location" => request.url.sub(permanent, username) }, [""]] end end

The above code works like a charm. Excellent! Almost…

What happens if someone navigates to the url with an invalid :permanent parameter? If we leave the code as-is, sub will complain about converting nil to a String when the username is not found in the cache. This means someone navigated to an invalid permanent url, and we want the application to behave as it would with a normal 404. Your application likely has a custom 404 page for just such an occasion. However, there is a bit of a challenge in this.

Notice that in the above code, we do not have an @app instance variable. If this Rack application were part of the middleware stack, in the failure case, we would just continue up the middleware chain with @app.call(env) . Unfortunately, we do not have this luxury. So how do we make our app behave correctly? The answer is hidden deep inside the rack-mount gem. You must add 'X-Cascade' => 'pass' to the returned headers. When the routing code encounters this in the response from your Rack application, it passes execution off to the rest of your Rails app. Now we may rejoice. The final code ends up looking like this:

class PermaRouter def self.call(env) request = Rack::Request.new(env) params = env['action_dispatch.request.path_parameters'] permanent = params[:permanent] username = PermaCache.find(permanent) if username [301, { "Location" => request.url.sub(permanent, username) }, [""]] else [404, { 'X-Cascade' => 'pass' }, ["Not Found"]] end end end

What we have in the end is an extremely fast way to handle our special redirection case dynamically, and to allow the rest of the application to take over and behave as it normally would when bad parameters are supplied.

pj