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")
    env
  end

  alias_method_chain :extract_request_environment, :logged_in
end

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)
    result
  end

  alias_method_chain :recognition_conditions, :logged_in
end

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

Photo of Steve Richert

Steve is a Michigan State grad in mechanical engineering, but has been programming since he was in single digits with his Commodore 64. QBasic greatness followed.

After putting in his time with PHP, Steve discovered Ruby, Rails, Git and Agile development. Open source greatness followed. After long admiring their work, Steve can finally cross working for Collective Idea off his bucket list.

Steve lives in Grandville with his wonderful wife, Catie and son, Charlie.

Comments:


Post a Comment

(optional)
(optional — will be included as a link.)