Using scope with dynamic path segments, PATH_INFO and SCRIPT_NAME
Hi. I am a bit stuck when trying to use hanami-router with dynamic path segments using scope.
Consider these two examples and the resulting PATH_INFO and SCRIPT_NAME fields in the rack env. For both examples let's look at the request GET /stations/42/trains.
Example A - scope with dynamic segment
# config.ru
Router = Hanami::Router.new do
scope '/stations/:station_id' do
mount ->(env) { binding.irb }, at: '/'
end
end
run Router
# "SCRIPT_NAME" => "/stations/:station_id"
# "PATH_INFO" => "/stations/42/trains",
# Rack::Request.new(env).path # => "/stations/:station_id/stations/42/trains"
Example B - scope without dynamic segment
Router = Hanami::Router.new do
scope '/stations' do
mount ->(env) { binding.irb }, at: '/'
end
end
run Router
# # "SCRIPT_NAME" => "/stations"
# # "PATH_INFO" => "/42/trains"
# Rack::Request.new(env).path # => "/stations/42/trains"
The issue I have with this is that using a scope with a dynamic segment (Ex A) makes Rack::Request#path unusable. – I think it somehow makes sense that SCRIPT_NAME is actually "/stations/:station_id" here, but it's hard to get the actual request when using Rack::Request#path.
Questions:
- Is this behavior intended?
- My assumption is that you should be able to get to the actual request path by concatenating
SCRIPT_NAME + PATH_INFO- Is that wrong?
@dcr8898 pointed out this part of the rack spec:
When combined with SCRIPT_NAME, PATH_INFO, and QUERY_STRING, these variables can be used to reconstruct the original the request URL.
So I think we don't adhere to the rack spec in this case. Again, all this applies only if we really want to support dynamic segments in scope, but I know that this would be a very useful feature.
Probaby related to https://github.com/hanami/hanami-api/issues/29, https://github.com/hanami/hanami-router/pull/248
all this applies only if we really want to support dynamic segments in
scope, but I know that this would be a very useful feature.
Yeah, I totally want us to support that, and to comply with the Rack spec while doing so :)
I need a bit of time to digest this properly and get you a helpful reply. I'll come back to you soon.
Sorry, bit of a drive-by comment here - and I’m not a Hanami expert, or a Rack expert for that matter!
SCRIPT_NAME is a external constant that shouldn’t be interpolated by Hanami and shouldn’t influence its routing.
In both examples Rack::Request#path is correct.
In example A the request would be served and (presumably if Hanami behaves correctly) links generated within Hanami will include SCRIPT_NAME - so generating a link to station 53’s trains will be /stations/:station_id/stations/53/trains.
In example B the request would be a 404 because there’s no route that matches /42/trains.
This allows you to write a Rack app as a self-contained unit with a standard / root but then mount it on a separate path and everything should continue to Just Work (something something dependency injection).
SCRIPT_NAME is a external constant
In this case, SCRIPT_NAME is set by hanami-router. It has to set SCRIPT_NAME in order to make mounted rack apps work. SCRIPT_NAME is not an external constant.
Maybe I wasn’t clear about this: The listed values for SCRIPT_NAME are from the view of the mounted app. It’s what you get when you are in that ‚binding.irb‘ session.
Thanks again for this really great report, @ahx. I've just fixed this in #294. Does that match your expectations? Did you want to give it a quick try before I merge?
Hi Tim, thanks for working on this. I tried https://github.com/hanami/hanami-router/pull/294 and it works just as expected. Thanks for the work on this and your always friendly and forward facing communications. 👍🏼
Ah I see so scope is acting somewhat like a Rack proxy for its children, that's really cool 👏🏻
@ahx Thank you so much for verifying my changes! I'll release these ASAP :)
And hi @benpickles! Just wanted to say I really appreciated your input in this one, I love seeing people taking an interest in our stuff :)