Rails version: '~> 4.2.7.1'
Spree version: '3.1.1'
TlDr:
How do I get route as /api/products/:id
or controller and action of that route in a middleware of Rails 4 application.
Details:
I am adding a middleware in my rails app which is similar to gem scout_statsd_rack. This adds following middleware to rails app to send metrics via statsd:
def call(env)
(status, headers, body), response_time = call_with_timing(env)
statsd.timing("#{env['REQUEST_PATH']}.response", response_time)
statsd.increment("#{env['REQUEST_PATH']}.response_codes.#{status.to_s.gsub(/\d{2}$/,'xx')}")
# Rack response
[status, headers, body]
rescue Exception => exception
statsd.increment("#{env['REQUEST_PATH']}.response_codes.5xx")
raise
end
def call_with_timing(env)
start = Time.now
result = @app.call(env)
[result, ((Time.now - start) * 1000).round]
end
What I want is to find current route in the middleware so that I can send metrics specific to each route.
I tried approach described here, which tells env['PATH_INFO']
can provide path, which it does, but it gives with URL params like this: /api/products/4
but what I want is /api/products/:id
as my puropose is to track performance of /api/products/:id
API.
env['REQUEST_PATH']
and env['REQUEST_URI']
also gives same response.
I tried answer provided here and here:
Rails.application.routes.router.recognize({"path_info" => env['PATH_INFO']})
or like this
Rails.application.routes.router.recognize(env['PATH_INFO'])
But it gave following error:
NoMethodError (undefined method
path_info' for {"path_info"=>"/api/v1/products/4"}:Hash):
find_routes'
vendor/bundle/gems/actionpack-4.2.7.1/lib/action_dispatch/journey/router.rb:100:in
vendor/bundle/gems/actionpack-4.2.7.1/lib/action_dispatch/journey/router.rb:59:inrecognize'
call'
vendor/bundle/gems/scout_statsd_rack-0.1.7/lib/scout_statsd_rack.rb:27:in
This answer discusses request.original_url
, but How do I access variable request
, I think it should be same as env
but not able to get route as want from this.
Edit #1
You can see the sample repo here, with code of rails middleware here, Setup of this can be done as stated in README and than this API can be hit: http://localhost:3000/api/v1/products/1
.
Edit #2
I tried approach given by @MichałMłoźniak like following:
def call(env)
(status, headers, body), response_time = call_with_timing(env)
request = ActionDispatch::Request.new(env)
request = Rack::Request.new("PATH_INFO" => env['REQUEST_PATH'], "REQUEST_METHOD" => env["REQUEST_METHOD"])
Rails.application.routes.router.recognize(request) { |route, params|
puts "I am here"
puts params.inspect
puts route.inspect
}
But I got following response:
I am here
{}
#<ActionDispatch::Journey::Route:0x007fa1833ac628 @name="spree", @app=#<ActionDispatch::Routing::Mapper::Constraints:0x007fa1833ace70 @dispatcher=false, @app=Spree::Core::Engine, @constraints=[]>, @path=#<ActionDispatch::Journey::Path::Pattern:0x007fa1833acc90 @spec=#<ActionDispatch::Journey::Nodes::Slash:0x007fa1833ad230 @left="/", @memo=nil>, @requirements={}, @separators="/.?", @anchored=false, @names=[], @optional_names=[], @required_names=[], @re=/\A\//, @offsets=[0]>, @constraints={:required_defaults=>[]}, @defaults={}, @required_defaults=nil, @required_parts=[], @parts=[], @decorated_ast=nil, @precedence=1, @path_formatter=#<ActionDispatch::Journey::Format:0x007fa1833ac588 @parts=["/"], @children=[], @parameters=[]>>
I have pushed the changes as well here.