Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails Routes based on condition

Tags:

I have three roles: Instuctor, Student, Admin and each have controllers with a "home" view.

so this works fine,

get "instructor/home", :to => "instructor#home"
get "student/home", :to => "student#home"
get "admin/home", :to => "admin#home"

I want to write a vanity url like below which will route based on the role of the user_id to the correct home page.

get "/:user_id/home", :to => "instructor#home" or "student#home" or "admin#home"

How do I accomplish this?

like image 487
sidj Avatar asked Jun 27 '12 15:06

sidj


People also ask

How many types of routes are there in Rails?

Rails RESTful Design which creates seven routes all mapping to the user controller. Rails also allows you to define multiple resources in one line.

How do Rails routes work?

Rails routing is a two-way piece of machinery – rather as if you could turn trees into paper, and then turn paper back into trees. Specifically, it both connects incoming HTTP requests to the code in your application's controllers, and helps you generate URLs without having to hard-code them as strings.

What are Rails resource routes?

Any object that you want users to be able to access via URI and perform CRUD (or some subset thereof) operations on can be thought of as a resource. In the Rails sense, it is generally a database table which is represented by a model, and acted on through a controller.


1 Answers

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.

like image 114
Bart Jedrocha Avatar answered Sep 27 '22 21:09

Bart Jedrocha