I'm providing an alternate approach as this SO question comes up near the top when searching for role based routing in Rails.
I recently needed to implement something similar but wanted to avoid having a large number of conditionals in the controller - this was compounded by the fact that each of my user roles required completely different data to be loaded and presented. I opted to move the deciding logic to the routing layer by using a Routing Constraint.
# app/constraints/role_route_constraint.rb
class RoleRouteConstraint
def initialize(&block)
@block = block || lambda { |user| true }
end
def matches?(request)
user = current_user(request)
user.present? && @block.call(user)
end
def current_user(request)
User.find_by_id(request.session[:user_id])
end
end
The most important part of the above code is the matches?
method which will determine whether or not the route will match. The method is passed the request
object which contains various information about the request being made. In my case, I'm looking up the :user_id
stored in the session cookie and using that to find the user making the request.
You can then use this constraint when defining your routes.
# config/routes.rb
Rails.application.routes.draw do
get 'home', to: 'administrators#home', constraints: RoleRouteConstraint.new { |user| user.admin? }
get 'home', to: 'instructors#home', constraints: RoleRouteConstraint.new { |user| user.instructor? }
get 'home', to: 'students#home', constraints: RoleRouteConstraint.new { |user| user.student? }
end
With the above in place, an administrator making a request to /home
would be routed the home action of the AdministratorsController
, an instructor making a request to /home
would be routed to the home action of the InstructorsController
, and a student making a request to /home
would be routed to the home action of the StudentsController
.
More Information
If you're looking for more information, I recently wrote about this approach on my blog.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…