Routes.recognize_path on more complicated Routes

For a permissions system i'm writing i'm extending the standard link_to helpers to check if a user has a permission to perform that action before displaying a link to clean-up my code so i don't have to put if checks all over them.

I'm trying to use..

ActionController::Routing::Routes.recognize_path(url, :method => method)

to get the action and controller out so i can check those permissions.

The problem im having is when it comes to more complicated routes the recognize_path method gets things mixed up e.g.

/admin/users/60/edit - Controller: admin/users | Action: 60 | Id: edit - Correct /admin/users/60 - Controller: admin/users | Action: destroy | Id: 60 - Correct /admin/users/60/ban - Controller: admin/users | Action: 60 | Id: ban - Incorrect

As you see the ban link has the action and id the wrong way round.

Is there a correct way around this rather than just swapping the 2 values round?

Also when doing a nested route like....


It errors saying "Only get, put, and delete requests are allowed." even though that route works perfectly fine in a view.

The projects currently running on rails 2.0.1 if that helps.


Hi Danny,

good to see that someone is trying to achieve EXACTLY the same thing like me. I have even tried to dig deep in the link_to implementation to solve this, but it is more than black magic and I soon gave up. Do you have any new ideas regarding the problem?


Hi Jakub

Here's the implementation i ended up with. Its not very clean but it does the job.

[code] def link_to(*args, &block)     unless params[:controller] =~ /admin/       super     else       if args.size > 2         super if action_allowed(args[1], args[2]["method"])       else         super if action_allowed(args[1])       end     end   end

  def link_to_remote(name, options = {}, html_options = nil)     unless params[:controller] =~ /admin/       super     else       super if action_allowed(options[:url], options[:method])     end   end

  def action_allowed(url, method = :get)     return false unless current_user

    path = ActionController::Routing::Routes.recognize_path(url, :method => method) rescue nil

    return true unless path

    return true if current_user.roles.find(:first, :conditions => ["unrestricted = ?", true])

    permissions = Permission.find(:all, :conditions => ["role_id in (?)",])

    if path[:action] =~ /^\d+$/       perm = { |p| p.controller == path[:controller] && p.action == path[:id] }.first     else       perm = { |p| p.controller == path[:controller] && p.action == path[:action] }.first     end

    return true if perm

    false   end[/code]