Search code examples
ruby-on-railsrubyroutesrack

How to find rails route pattern from url


Given a rack request where I know the path, e.g. /things/1, how can I get the route reference e.g. /things/:id?

I can use Rails.application.routes.recognize_path to get the controller and action, but I’m explicitly looking for an obfuscated path.

Is there anyway to get the recognized route given a controller#action, maybe?


Solution

  • Rails v7.1

    They've made it a bit easier:

    request.route_uri_pattern
    #=> "/users/:id/edit(.:format)"
    

    https://api.rubyonrails.org/v7.1.3.2/classes/ActionDispatch/Request.html#method-i-route_uri_pattern


    Rails v7.0

    The only place I knew where that information is available is bin/rails routes. It's using inspector to collect all the information:

    https://github.com/rails/rails/blob/v7.0.4.3/actionpack/lib/action_dispatch/routing/inspector.rb

    Maybe there is something else you'll find there. But I extracted the main bit that you're asking for:

    # in a controller or a template
    
    <% request.routes.router.recognize(request) do |route, _params| %>
      <%= route.path.spec.to_s %> # => /users/:id(.:format)
    <% end %>
    
    # in a console
    
    >> Rails.application.routes.router.recognize(
      ActionDispatch::Request.new(Rack::MockRequest.env_for("/users/1/edit", method: :get))
    ) {}.map {|_,_,route| route.path.spec.to_s }
    => ["/users/:id/edit(.:format)"]
    

    route here is ActionDispatch::Journey::Route instance which has all the information about a route.


    I don't have even a remote clue what this does, but it does it:

    >> Rails.application.routes.routes.simulator.memos("/users/1/edit").first.ast.to_s
    => "/users/:id/edit(.:format)"
    
    # NOTE: if route doesn't match it will `yield` and raise
    # no block given (yield) (LocalJumpError)
    # just rescue or give it an empty block.
    

    https://github.com/rails/rails/blob/v7.0.4.3/actionpack/lib/action_dispatch/journey/gtg/simulator.rb#L25