More User-centric Routes: Rails 2

Writing routes that are conditional upon whether a user is logged in is easy with Rails 3 but if you find yourself (as many of us do) stuck with a Rails 2 app, here’s how to achieve the same fancy routes without the latest Rails.

Required reading: User-centric Routing in Rails 3

What makes the approach above work for Rails 3 is that the routes have easy access to the request object. It’s not quite as easy with Rails 2, but with a little monkeying around we can still gain access to the request object from the routes.

The groundwork for this approach was laid by Jamis Buck and although his solution is a bit hackish, it works like a charm!

Before I go on to demonstrate how to use Jamis technique to check for a logged in user, I should mention that there’s a far better solution… Upgrade to Rails 3! If at all possible, that’s your best bet. Rails 3.0 is a huge improvement over Rails 2. Plus, Rails 3.1 is right around the corner! But if it’s just not a possibility, read on.

The Hard Part

In order to give the routes access to the request, we need to:

  1. Include login information that the routes can read
  2. Add a login condition that the routes can understand

First thing’s first. In config/initializers/logged\_in.rb, we can open up the route set and hijack the request environment to include whether or not a user is logged in.

class ActionController::Routing::RouteSet
  def extract_request_environment_with_logged_in(request)
    env = extract_request_environment_without_logged_in(request)
    env[:logged_in] = request.cookies.key?("user_token")

  alias_method_chain :extract_request_environment, :logged_in

Behind the long method names, this simply overrides an existing method and adds a key/value pair to the env hash that usually results. If cookies\["user\_token"\] is set, our user is logged in. And our routes can see that. But that’s only half the equation.

Next, in the same file, we open up the route and add a condition for our new environment information.

class ActionController::Routing::Route
  def recognition_conditions_with_logged_in
    result = recognition_conditions_without_logged_in
    result << "conditions[:logged_in] == env[:logged_in]" if conditions.key?(:logged_in)

  alias_method_chain :recognition_conditions, :logged_in

Once again, we’re simply overriding an existing method and tacking a condition onto the usual array of condition strings that would result. Condition strings? Yes, the old Rails routing internals were a little wonky.

The Easy Part

Now we’re ready to add the new condition to the application routes:

map.root :controller => "static", :action => "home", :conditions => {:logged_in => false}
map.root :controller => "users", :action => "show", :conditions => {:logged_in => true}

If you can put the slight ugliness in config/initializers/logged\_in.rb out of your mind, this works quite nicely and keeps your routes clean.

Strongly recommended reading: Upgrading to Rails 3


Add a Comment

Hmm...that didn't work.

Something went wrong while adding your comment. If you don't mind, please try submitting it again.

Comment Added!

Your comment has been added to this post. Please refresh this page to view it.

Optional. If added, we will display a link to the website in your comment.
Optional. Never shared or displayed in your comment.