Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails route scope default based on current_user

I have a route something like:

scope ":department", department: /admin|english|math/, defaults: { department: 'admin' }

Is it possible to make the default department for this route to be based on the current_user.department.name?

If this is not possible, what is another way of solving my problem. The problem is that I want all links to default to the current scope unless otherwise noted. I'm currently doing the following in LOTS of places:

# links to /math/students
link_to 'students', students_path(department: current_user.department.name.downcase)
like image 712
Kyle Decot Avatar asked Jun 14 '13 15:06

Kyle Decot


2 Answers

If I understand correctly, what you want is to be able to write:

link_to 'students', students_path

and have the department option automatically set based on the current user.

Here's a solution that's like some of the others that have been offered: define helpers for each route that requires a department. However, we can do this programmatically.

Here we go: app/helpers/url_helper.rb

module UrlHelper

  Rails.application.routes.routes.named_routes.values.
    select{ |route| route.parts.include?(:department) }.each do |route|
      define_method :"department_#{route.name}_path" do |*args|
        opts = args.extract_options!
        if args.size > 0
          keys = route.parts - [:department]
          opts.merge!(Hash[*keys.zip(args).flatten])
        end
        opts.reverse_merge!(department: current_user.department.name.downcase)
        Rails.application.routes.url_helpers.send(:"#{route.name}_path", opts)
      end
  end

end

You now have helper methods like department_students_path for every route that has a :department path segment. These will work just like students_path -- you can pass in opts, you can even set the :department explicitly and it will override the default. And they stay up to date with changes to your routes.rb without you having to maintain it.

You might even be able to name them the same as the original helpers, i.e.,

define_method :"#{route.name}_path"

without having to prefix them with department_--I didn't do that because I'd rather avoid naming collisions like that. I'm not sure how that would work (which method would win the method lookup when calling it from a view template), but you might look into it.

You can of course repeat this block for the _url helper methods, so you'll have those in addition to the _path ones.

To make the helpers available on controllers as well as views, just include UrlHelper in your ApplicationController.

So I think this satisfies your criteria:

  1. You can call a helper method for paths scoped to :department which will default to the current_user's department, so that you don't have to explicitly specify this every time.
  2. The helpers are generated via metaprogramming based on the actually defined named routes that have a :department segment, so you don't have to maintain them.
  3. Like the built-in rails url_helpers, these guys can take positional args for other path segments, like, department_student_path(@student). However, one limitation is that if you want to override the department, you need to do so in the final opts hash (department_student_path(@student, department: 'math')). Then again, in that case you could always just do student_path('math', @student), so I don't think it's much of a limitation.
like image 147
gregates Avatar answered Oct 22 '22 23:10

gregates


Route contraints can accept a proc, so you could solve this problem with the following "hack":

concern :student_routes do
  # all your routes where you want this functionality
end

# repeat this for all departments
scope ":department", constraints: lambda{|req| req.session[:user_id] && User.find(req.session[:user_id]).department.name.downcase == "english"}, defaults: { department: 'english' } do
  concerns :student_routes
end

Route concerns is a feature of rails 4. If you don't use rails 4, can you get the feature with this gem: https://github.com/rails/routing_concerns.

You can also just copy all the routes for students to all of the scopes.

like image 30
jokklan Avatar answered Oct 22 '22 23:10

jokklan