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?
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.