Dynamic Rails Routes with Warden Devise and Constraints

My rails application has a variety of users, and I want to make the root of my app load something different based on the needs of each user after log in. For example, if a student logs in, I want to load the students/dashboard page. If a Teacher logs in, I want to load the teachers/dashboard etc.

Rails recently added a constraints option to routes which allows you to dynamically define routes based on the request coming in.

Warden, our authentication gem, conveniently places the user type field in the session. This session is available through the request object which allows us to branch on the type of user logged in. Just make sure to wrap this in devise’s authenticated call so we know the session is setup. This can also be moved into a class, for the example I left it in the lambda.


authenticated do
  root :to => 'teachers/dashboards#show', :constraints => lambda{ |req| req.session['warden.user.user.key'][0] == 'Teacher' }
  root :to => 'students/dashboards#show', :constraints => lambda{ |req| req.session['warden.user.user.key'][0] == 'Student' }
end

Another way to solve this same problem would be to have a single controller dedicated to routing users around based on their type. All root requests would go to this controller and the user would then be redirected based on type. I prefer to have as much of my routing knowledge as possible be in the routes file, so to me the warden session access is the better choice.

Devise also offers a route based solution with the devise_scopes and scope blocks. Unfortunately, I found these to be buggy and push complexities into other areas of my application.