hanami-router 2.3.0.beta2 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +80 -1
- data/README.md +0 -4
- data/hanami-router.gemspec +4 -4
- data/lib/hanami/middleware/body_parser/class_interface.rb +7 -7
- data/lib/hanami/middleware/body_parser/form_parser.rb +2 -7
- data/lib/hanami/middleware/body_parser/json_parser.rb +4 -4
- data/lib/hanami/middleware/body_parser/parser.rb +11 -7
- data/lib/hanami/middleware/body_parser.rb +17 -18
- data/lib/hanami/router/mounted_path.rb +6 -3
- data/lib/hanami/router/version.rb +1 -1
- data/lib/hanami/router.rb +75 -31
- metadata +19 -25
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c9a6b9498cdc6f09a3792b52accbe0fda997f5de907fb0e523408a0a8437465c
|
|
4
|
+
data.tar.gz: 40fc3193dcd6e471f1464852844456748f1ef8deb53ad630290e1343abdfbbbd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b514e367ea3eb3b3be40f89d19b5a00388365b1a39fd0dc5bc98c1f971ff2831a4fcc80c3c463079d4a61773e0375504cfd9afa198aae7d1c42d762fb2e56def
|
|
7
|
+
data.tar.gz: ef993a463b78db8deba2a6b6304aeb408961ee6ae89e92e62f8b79e52f662a6c0d7155921aee78c1ddce67055fa218294b09afefb19674d27aa1edb32622c0e8
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,85 @@
|
|
|
1
1
|
# Hanami::Router
|
|
2
2
|
|
|
3
|
-
Rack compatible HTTP router for Ruby
|
|
3
|
+
Rack compatible HTTP router for Ruby.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
### Deprecated
|
|
12
|
+
|
|
13
|
+
### Removed
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
### Security
|
|
18
|
+
|
|
19
|
+
[Unreleased]: http://github.com/hanami/hanami-router/compare/v2.3.1...main
|
|
20
|
+
|
|
21
|
+
## [2.3.1] - 2025-12-17
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- Require Rack version 2.2.16 or higher, which is the earliest version of Rack 2.x that will run on Ruby 4.0 as well as our currently supported Ruby versions (3.2, 3.3, 3.4). (@cllns in 2b15582)
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- Set correct Rack `"SCRIPT_NAME"` value for mounts under dynamic prefixes. (@timriley in #294).
|
|
30
|
+
|
|
31
|
+
For example, given a mount under `scope '/stations/:station_id'`, the "SCRIPT_NAME" will be e.g. `"/stations/42"` rather than `"/stations/:station_id"`.
|
|
32
|
+
- For a mount with a prefix, allow the Rack `"PATH_INFO"` value to remain an empty string when a request is made for that exact prefix only. (@timriley in #295)
|
|
33
|
+
|
|
34
|
+
Given the following:
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
mount ->(env) { [200, {}, [env["PATH_INFO"]] }, at: "/settings"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
When a request is made to `"/settings"`, the `"SCRIPT_NAME"` will be `"/settings"` and `"PATH_INFO"` will be `""`. This ensures that the Rack environment can be used to reconstruct the same path as used for the original request.
|
|
41
|
+
|
|
42
|
+
To ensure a mounted router instance can still route to its root, treat `""` the same as `"/"` for the purposes of matching routes.
|
|
43
|
+
|
|
44
|
+
[2.3.1]: http://github.com/hanami/hanami-router/compare/v2.3.0...v2.3.1
|
|
45
|
+
|
|
46
|
+
## v2.3.0 - 2025-11-12
|
|
47
|
+
|
|
48
|
+
### Changed
|
|
49
|
+
|
|
50
|
+
- Allow scopes to be given a custom name prefix, with `as:`. This prefix is given to any named routes within the scope. (@timriley in #286)
|
|
51
|
+
- Allow route names given to `as:` to specify their own _prefix_, which goes before any name prefixes added by the scopes the route is nested within. To specify a prefix, provide an array to `as:`. (@timriley in #286)
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
scope "backend" do
|
|
55
|
+
scope "admin", as: :secret do
|
|
56
|
+
get "/cats/new", to: ->(*) { [200, {}, ["OK"]] }, as: [:new, :cat]
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
router.path(:new_backend_secret_cat)
|
|
61
|
+
# => "/backend/admin/cats/new
|
|
62
|
+
```
|
|
63
|
+
- As part of Rack v2/v3 compatibility, improve coordination between `Hanami::Middleware::BodyParser` and `Hanami::Router` regarding access of the request body. (@timriley in #287)
|
|
64
|
+
|
|
65
|
+
`BodyParser` will now make `env["rack.input"]` rewindable, and rewind the body after accessing it, ensuring downstream users can still read `"rack.input"` if needed. `Router` will only attempt to make `"rack.input"` rewindable if it hasn’t already been made so.
|
|
66
|
+
- Change `Hanami::Middleware::BodyParser::Parser` to refer to `media_types` instead of `mime_types`. (@timriley in #289)
|
|
67
|
+
|
|
68
|
+
A body parser subclass should now look like this:
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
class CustomParser < Hanami::Middleware::BodyParser::Parser
|
|
72
|
+
def self.media_types = ["application/custom"]
|
|
73
|
+
def parse(body)
|
|
74
|
+
body # Your parsing logic here
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
```
|
|
78
|
+
- Allow `Hanami::Middleware::BodyParser` to be initialized with a single body parser, instead of requiring an array. (@timriley in #288)
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
Hanami::Middleware::BodyParser.new(app, MyCustomParser)
|
|
82
|
+
```
|
|
4
83
|
|
|
5
84
|
## v2.3.0.beta2 - 2025-10-17
|
|
6
85
|
|
data/README.md
CHANGED
|
@@ -6,8 +6,6 @@ Rack compatible, lightweight, and fast HTTP Router for Ruby and [Hanami](http://
|
|
|
6
6
|
|
|
7
7
|
[](https://badge.fury.io/rb/hanami-router)
|
|
8
8
|
[](https://github.com/hanami/router/actions?query=workflow%3Aci+branch%3Amain)
|
|
9
|
-
[](https://codecov.io/gh/hanami/router)
|
|
10
|
-
[](https://depfu.com/github/hanami/router?project=Bundler)
|
|
11
9
|
|
|
12
10
|
## Contact
|
|
13
11
|
|
|
@@ -20,8 +18,6 @@ Rack compatible, lightweight, and fast HTTP Router for Ruby and [Hanami](http://
|
|
|
20
18
|
|
|
21
19
|
## Installation
|
|
22
20
|
|
|
23
|
-
__Hanami::Router__ supports Ruby (MRI) 3.1.+
|
|
24
|
-
|
|
25
21
|
Add this line to your application's Gemfile:
|
|
26
22
|
|
|
27
23
|
```ruby
|
data/hanami-router.gemspec
CHANGED
|
@@ -7,8 +7,8 @@ require "hanami/router/version"
|
|
|
7
7
|
Gem::Specification.new do |spec|
|
|
8
8
|
spec.name = "hanami-router"
|
|
9
9
|
spec.version = Hanami::Router::VERSION
|
|
10
|
-
spec.authors = ["
|
|
11
|
-
spec.email = ["
|
|
10
|
+
spec.authors = ["Hanakai team"]
|
|
11
|
+
spec.email = ["info@hanakai.org"]
|
|
12
12
|
spec.description = "Rack compatible HTTP router for Ruby"
|
|
13
13
|
spec.summary = "Rack compatible HTTP router for Ruby and Hanami"
|
|
14
14
|
spec.homepage = "http://hanamirb.org"
|
|
@@ -20,15 +20,15 @@ Gem::Specification.new do |spec|
|
|
|
20
20
|
spec.metadata["rubygems_mfa_required"] = "true"
|
|
21
21
|
spec.required_ruby_version = ">= 3.2"
|
|
22
22
|
|
|
23
|
-
spec.add_dependency "rack", ">= 2.
|
|
23
|
+
spec.add_dependency "rack", ">= 2.2.16"
|
|
24
24
|
spec.add_dependency "mustermann", "~> 3.0"
|
|
25
25
|
spec.add_dependency "mustermann-contrib", "~> 3.0"
|
|
26
26
|
spec.add_dependency "csv", "~> 3.3"
|
|
27
27
|
|
|
28
|
-
spec.add_development_dependency "bundler", ">= 1.6", "< 3"
|
|
29
28
|
spec.add_development_dependency "rake", "~> 13"
|
|
30
29
|
spec.add_development_dependency "rack-test", "~> 2.0"
|
|
31
30
|
spec.add_development_dependency "rspec", "~> 3.8"
|
|
31
|
+
spec.add_development_dependency "ostruct" # Remove once we drop support for Rack 2
|
|
32
32
|
|
|
33
33
|
spec.add_development_dependency "rubocop", "~> 1.0"
|
|
34
34
|
spec.add_development_dependency "rubocop-performance", "~> 1.0"
|
|
@@ -53,22 +53,22 @@ module Hanami
|
|
|
53
53
|
# @api private
|
|
54
54
|
# @since 2.0.0
|
|
55
55
|
def build_parsers(parser_specs, registry = {})
|
|
56
|
-
return DEFAULT_BODY_PARSERS if parser_specs.empty?
|
|
57
|
-
|
|
58
56
|
parsers = Array(parser_specs).flatten(0)
|
|
59
57
|
|
|
58
|
+
return DEFAULT_BODY_PARSERS if parsers.empty?
|
|
59
|
+
|
|
60
60
|
parsers.each_with_object(registry) do |spec, memo|
|
|
61
61
|
if spec.is_a?(Hash) && spec.size > 1
|
|
62
62
|
spec.each do |key, value|
|
|
63
63
|
build_parsers([key => [value]], memo)
|
|
64
64
|
end
|
|
65
65
|
else
|
|
66
|
-
name, *
|
|
66
|
+
name, *media_types = Array(*spec).flatten(0)
|
|
67
67
|
|
|
68
|
-
parser = build(name,
|
|
68
|
+
parser = build(name, media_types: media_types.flatten)
|
|
69
69
|
|
|
70
|
-
parser.
|
|
71
|
-
memo[
|
|
70
|
+
parser.media_types.each do |type|
|
|
71
|
+
memo[type] = parser
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
74
|
end
|
|
@@ -78,7 +78,7 @@ module Hanami
|
|
|
78
78
|
|
|
79
79
|
# @api private
|
|
80
80
|
# @since 1.3.0
|
|
81
|
-
PARSER_METHODS = %i[
|
|
81
|
+
PARSER_METHODS = %i[media_types parse].freeze
|
|
82
82
|
|
|
83
83
|
# @api private
|
|
84
84
|
# @since 2.0.0
|
|
@@ -9,17 +9,12 @@ module Hanami
|
|
|
9
9
|
# @since 2.0.1
|
|
10
10
|
# @api private
|
|
11
11
|
class FormParser < Parser
|
|
12
|
-
# @since 2.0.1
|
|
13
12
|
# @api private
|
|
14
|
-
|
|
15
|
-
"multipart/form-data"
|
|
16
|
-
].freeze
|
|
13
|
+
MEDIA_TYPES = ["multipart/form-data"].freeze
|
|
17
14
|
|
|
18
15
|
# @since 2.0.1
|
|
19
16
|
# @api private
|
|
20
|
-
def self.
|
|
21
|
-
MIME_TYPES
|
|
22
|
-
end
|
|
17
|
+
def self.media_types = MEDIA_TYPES
|
|
23
18
|
|
|
24
19
|
# Parse a multipart body payload (form file uploading)
|
|
25
20
|
#
|
|
@@ -9,11 +9,11 @@ module Hanami
|
|
|
9
9
|
# @since 1.3.0
|
|
10
10
|
# @api private
|
|
11
11
|
class JsonParser < Parser
|
|
12
|
-
# @since 1.3.0
|
|
13
12
|
# @api private
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
MEDIA_TYPES = ["application/json", "application/vnd.api+json"].freeze
|
|
14
|
+
|
|
15
|
+
# @api private
|
|
16
|
+
def self.media_types = MEDIA_TYPES
|
|
17
17
|
|
|
18
18
|
# Parse a json string
|
|
19
19
|
#
|
|
@@ -7,11 +7,14 @@ module Hanami
|
|
|
7
7
|
#
|
|
8
8
|
# @since 2.0.0
|
|
9
9
|
class Parser
|
|
10
|
-
|
|
10
|
+
class << self
|
|
11
|
+
def media_types = []
|
|
12
|
+
alias_method :mime_types, :media_types
|
|
13
|
+
end
|
|
11
14
|
|
|
12
|
-
# Return supported
|
|
15
|
+
# Return supported media types
|
|
13
16
|
#
|
|
14
|
-
# @return [Array<String>] supported
|
|
17
|
+
# @return [Array<String>] supported media types
|
|
15
18
|
#
|
|
16
19
|
# @abstract
|
|
17
20
|
# @since 2.0.0
|
|
@@ -20,15 +23,16 @@ module Hanami
|
|
|
20
23
|
# require "hanami/middleware/body_parser"
|
|
21
24
|
#
|
|
22
25
|
# class XMLParser < Hanami::Middleware::BodyParser::Parser
|
|
23
|
-
# def self.
|
|
26
|
+
# def self.media_types
|
|
24
27
|
# ["application/xml", "text/xml"]
|
|
25
28
|
# end
|
|
26
29
|
# end
|
|
27
|
-
attr_reader :
|
|
30
|
+
attr_reader :media_types
|
|
31
|
+
alias_method :mime_types, :media_types
|
|
28
32
|
|
|
29
33
|
# @api private
|
|
30
|
-
def initialize(
|
|
31
|
-
@
|
|
34
|
+
def initialize(media_types: [])
|
|
35
|
+
@media_types = self.class.media_types + media_types
|
|
32
36
|
end
|
|
33
37
|
|
|
34
38
|
# Parse raw HTTP request body
|
|
@@ -39,23 +39,31 @@ module Hanami
|
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def call(env)
|
|
42
|
-
|
|
43
|
-
return @app.call(env) if body.nil? || body.empty?
|
|
42
|
+
return @app.call(env) if env.key?(Router::ROUTER_PARSED_BODY)
|
|
44
43
|
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
input = env[RACK_INPUT]
|
|
45
|
+
return @app.call(env) unless input
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
parser = @parsers[media_type(env)]
|
|
48
|
+
return @app.call(env) unless parser
|
|
49
|
+
|
|
50
|
+
# The input in Rack 3 is not rewindable. For compatbility with Rack 2, make the input
|
|
51
|
+
# rewindable, rewind it for reading, then rewind once more before returning to the app.
|
|
52
|
+
input = env[RACK_INPUT] = Rack::RewindableInput.new(input) unless input.respond_to?(:rewind)
|
|
53
|
+
input.rewind
|
|
54
|
+
body = input.read
|
|
55
|
+
input.rewind
|
|
56
|
+
|
|
57
|
+
return @app.call(env) if body.nil? || body.empty?
|
|
58
|
+
|
|
59
|
+
env[Router::ROUTER_PARSED_BODY] = parser.parse(body, env)
|
|
60
|
+
env[ROUTER_PARAMS] = _symbolize(env[Router::ROUTER_PARSED_BODY])
|
|
52
61
|
|
|
53
62
|
@app.call(env)
|
|
54
63
|
end
|
|
55
64
|
|
|
56
65
|
private
|
|
57
66
|
|
|
58
|
-
# @api private
|
|
59
67
|
def _symbolize(body)
|
|
60
68
|
if body.is_a?(::Hash)
|
|
61
69
|
Router::Params.deep_symbolize(body)
|
|
@@ -64,14 +72,6 @@ module Hanami
|
|
|
64
72
|
end
|
|
65
73
|
end
|
|
66
74
|
|
|
67
|
-
# @api private
|
|
68
|
-
def _parse(env, body)
|
|
69
|
-
@parsers[
|
|
70
|
-
media_type(env)
|
|
71
|
-
].parse(body)
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
# @api private
|
|
75
75
|
def media_type(env)
|
|
76
76
|
ct = content_type(env)
|
|
77
77
|
return unless ct
|
|
@@ -79,7 +79,6 @@ module Hanami
|
|
|
79
79
|
ct.split(MEDIA_TYPE_MATCHER, 2).first.downcase
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
-
# @api private
|
|
83
82
|
def content_type(env)
|
|
84
83
|
content_type = env[CONTENT_TYPE]
|
|
85
84
|
content_type.nil? || content_type.empty? ? nil : content_type
|
|
@@ -14,9 +14,12 @@ module Hanami
|
|
|
14
14
|
if @prefix.to_s == "/"
|
|
15
15
|
env[::Rack::SCRIPT_NAME] = EMPTY_STRING
|
|
16
16
|
else
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
# To set SCRIPT_NAME, use the actual matched portion of the path, not the prefix string
|
|
18
|
+
# itself. This is important for prefixes with dynamic segments like "/stations/:id". In
|
|
19
|
+
# this case, we want e.g. "/stations/42" as SCRIPT_NAME, not "/stations/:id".
|
|
20
|
+
matched_path = match.to_s
|
|
21
|
+
env[::Rack::SCRIPT_NAME] = env[::Rack::SCRIPT_NAME].to_s + matched_path
|
|
22
|
+
env[::Rack::PATH_INFO] = env[::Rack::PATH_INFO].sub(matched_path, EMPTY_STRING)
|
|
20
23
|
end
|
|
21
24
|
|
|
22
25
|
[@app, match.named_captures]
|
data/lib/hanami/router.rb
CHANGED
|
@@ -107,12 +107,10 @@ module Hanami
|
|
|
107
107
|
return not_allowed(env) || not_found(env)
|
|
108
108
|
end
|
|
109
109
|
|
|
110
|
-
# Rack 3 no longer requires "rack.input" to be rewindable. Force input to be
|
|
111
|
-
#
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if Hanami::Router.rack_3? && env[::Rack::RACK_INPUT]
|
|
115
|
-
env[::Rack::RACK_INPUT] = Rack::RewindableInput.new(env[::Rack::RACK_INPUT])
|
|
110
|
+
# Rack 3 no longer requires "rack.input" to be rewindable. Force input to be rewindable to
|
|
111
|
+
# maintain Rack 2 behavior while we're still supporting both.
|
|
112
|
+
if (input = env[::Rack::RACK_INPUT]) && !input.respond_to?(:rewind)
|
|
113
|
+
env[::Rack::RACK_INPUT] = Rack::RewindableInput.new(input)
|
|
116
114
|
end
|
|
117
115
|
|
|
118
116
|
endpoint.call(
|
|
@@ -167,7 +165,8 @@ module Hanami
|
|
|
167
165
|
#
|
|
168
166
|
# @param path [String] the relative URL to be matched
|
|
169
167
|
# @param to [#call] the Rack endpoint
|
|
170
|
-
# @param as [Symbol] a unique name for the route
|
|
168
|
+
# @param as [Symbol, Array<Symbol>] a unique name for the route, or a ["prefix", "name"] array,
|
|
169
|
+
# to add a prefix to the name when nested within scopes.
|
|
171
170
|
# @param constraints [Hash] a set of constraints for path variables
|
|
172
171
|
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
|
173
172
|
#
|
|
@@ -203,6 +202,19 @@ module Hanami
|
|
|
203
202
|
# router.path(:welcome) # => "/"
|
|
204
203
|
# router.url(:welcome) # => #<URI::HTTP http://localhost/>
|
|
205
204
|
#
|
|
205
|
+
# @example Named route with prefix inside scope
|
|
206
|
+
# require "hanami/router"
|
|
207
|
+
#
|
|
208
|
+
# router = Hanami::Router.new do
|
|
209
|
+
# scope "backend" do
|
|
210
|
+
# scope "admin", as: :secret do
|
|
211
|
+
# get "/cats/new", to: ->(*) { [200, {}, ["OK"]] }, as: [:new, :cat]
|
|
212
|
+
# end
|
|
213
|
+
# end
|
|
214
|
+
# end
|
|
215
|
+
#
|
|
216
|
+
# router.path(:new_backend_secret_cat) # => "/backend/admin/cats/new
|
|
217
|
+
#
|
|
206
218
|
# @example Constraints
|
|
207
219
|
# require "hanami/router"
|
|
208
220
|
#
|
|
@@ -218,7 +230,8 @@ module Hanami
|
|
|
218
230
|
#
|
|
219
231
|
# @param path [String] the relative URL to be matched
|
|
220
232
|
# @param to [#call] the Rack endpoint
|
|
221
|
-
# @param as [Symbol] a unique name for the route
|
|
233
|
+
# @param as [Symbol, Array<Symbol>] a unique name for the route, or a ["prefix", "name"] array,
|
|
234
|
+
# to add a prefix to the name when nested within scopes.
|
|
222
235
|
# @param constraints [Hash] a set of constraints for path variables
|
|
223
236
|
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
|
224
237
|
#
|
|
@@ -236,7 +249,8 @@ module Hanami
|
|
|
236
249
|
#
|
|
237
250
|
# @param path [String] the relative URL to be matched
|
|
238
251
|
# @param to [#call] the Rack endpoint
|
|
239
|
-
# @param as [Symbol] a unique name for the route
|
|
252
|
+
# @param as [Symbol, Array<Symbol>] a unique name for the route, or a ["prefix", "name"] array,
|
|
253
|
+
# to add a prefix to the name when nested within scopes.
|
|
240
254
|
# @param constraints [Hash] a set of constraints for path variables
|
|
241
255
|
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
|
242
256
|
#
|
|
@@ -254,7 +268,8 @@ module Hanami
|
|
|
254
268
|
#
|
|
255
269
|
# @param path [String] the relative URL to be matched
|
|
256
270
|
# @param to [#call] the Rack endpoint
|
|
257
|
-
# @param as [Symbol] a unique name for the route
|
|
271
|
+
# @param as [Symbol, Array<Symbol>] a unique name for the route, or a ["prefix", "name"] array,
|
|
272
|
+
# to add a prefix to the name when nested within scopes.
|
|
258
273
|
# @param constraints [Hash] a set of constraints for path variables
|
|
259
274
|
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
|
260
275
|
#
|
|
@@ -272,7 +287,8 @@ module Hanami
|
|
|
272
287
|
#
|
|
273
288
|
# @param path [String] the relative URL to be matched
|
|
274
289
|
# @param to [#call] the Rack endpoint
|
|
275
|
-
# @param as [Symbol] a unique name for the route
|
|
290
|
+
# @param as [Symbol, Array<Symbol>] a unique name for the route, or a ["prefix", "name"] array,
|
|
291
|
+
# to add a prefix to the name when nested within scopes.
|
|
276
292
|
# @param constraints [Hash] a set of constraints for path variables
|
|
277
293
|
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
|
278
294
|
#
|
|
@@ -290,7 +306,8 @@ module Hanami
|
|
|
290
306
|
#
|
|
291
307
|
# @param path [String] the relative URL to be matched
|
|
292
308
|
# @param to [#call] the Rack endpoint
|
|
293
|
-
# @param as [Symbol] a unique name for the route
|
|
309
|
+
# @param as [Symbol, Array<Symbol>] a unique name for the route, or a ["prefix", "name"] array,
|
|
310
|
+
# to add a prefix to the name when nested within scopes.
|
|
294
311
|
# @param constraints [Hash] a set of constraints for path variables
|
|
295
312
|
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
|
296
313
|
#
|
|
@@ -308,7 +325,8 @@ module Hanami
|
|
|
308
325
|
#
|
|
309
326
|
# @param path [String] the relative URL to be matched
|
|
310
327
|
# @param to [#call] the Rack endpoint
|
|
311
|
-
# @param as [Symbol] a unique name for the route
|
|
328
|
+
# @param as [Symbol, Array<Symbol>] a unique name for the route, or a ["prefix", "name"] array,
|
|
329
|
+
# to add a prefix to the name when nested within scopes.
|
|
312
330
|
# @param constraints [Hash] a set of constraints for path variables
|
|
313
331
|
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
|
314
332
|
#
|
|
@@ -326,7 +344,8 @@ module Hanami
|
|
|
326
344
|
#
|
|
327
345
|
# @param path [String] the relative URL to be matched
|
|
328
346
|
# @param to [#call] the Rack endpoint
|
|
329
|
-
# @param as [Symbol] a unique name for the route
|
|
347
|
+
# @param as [Symbol, Array<Symbol>] a unique name for the route, or a ["prefix", "name"] array,
|
|
348
|
+
# to add a prefix to the name when nested within scopes.
|
|
330
349
|
# @param constraints [Hash] a set of constraints for path variables
|
|
331
350
|
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
|
332
351
|
#
|
|
@@ -344,7 +363,8 @@ module Hanami
|
|
|
344
363
|
#
|
|
345
364
|
# @param path [String] the relative URL to be matched
|
|
346
365
|
# @param to [#call] the Rack endpoint
|
|
347
|
-
# @param as [Symbol] a unique name for the route
|
|
366
|
+
# @param as [Symbol, Array<Symbol>] a unique name for the route, or a ["prefix", "name"] array,
|
|
367
|
+
# to add a prefix to the name when nested within scopes.
|
|
348
368
|
# @param constraints [Hash] a set of constraints for path variables
|
|
349
369
|
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
|
350
370
|
#
|
|
@@ -362,7 +382,8 @@ module Hanami
|
|
|
362
382
|
#
|
|
363
383
|
# @param path [String] the relative URL to be matched
|
|
364
384
|
# @param to [#call] the Rack endpoint
|
|
365
|
-
# @param as [Symbol] a unique name for the route
|
|
385
|
+
# @param as [Symbol, Array<Symbol>] a unique name for the route, or a ["prefix", "name"] array,
|
|
386
|
+
# to add a prefix to the name when nested within scopes.
|
|
366
387
|
# @param code [Integer] a HTTP status code to use for the redirect
|
|
367
388
|
#
|
|
368
389
|
# @raise [Hanami::Router::UnknownHTTPStatusCodeError] when an unknown redirect code is given
|
|
@@ -379,6 +400,7 @@ module Hanami
|
|
|
379
400
|
# inherit the given path as path prefix and as a named routes prefix.
|
|
380
401
|
#
|
|
381
402
|
# @param path [String] the scope path to be used as a path prefix
|
|
403
|
+
# @param as: [String, Symbol] the name prefix to use for nested routes
|
|
382
404
|
# @param blk [Proc] the routes definitions withing the scope
|
|
383
405
|
#
|
|
384
406
|
# @since 2.0.0
|
|
@@ -395,13 +417,13 @@ module Hanami
|
|
|
395
417
|
# end
|
|
396
418
|
#
|
|
397
419
|
# router.path(:v1_users) # => "/v1/users"
|
|
398
|
-
def scope(path, &blk)
|
|
420
|
+
def scope(path, as: nil, &blk)
|
|
399
421
|
path_prefix = @path_prefix
|
|
400
422
|
name_prefix = @name_prefix
|
|
401
423
|
|
|
402
424
|
begin
|
|
403
425
|
@path_prefix = @path_prefix.join(path.to_s)
|
|
404
|
-
@name_prefix = @name_prefix.join(path.to_s)
|
|
426
|
+
@name_prefix = @name_prefix.join((as || path).to_s)
|
|
405
427
|
instance_eval(&blk)
|
|
406
428
|
ensure
|
|
407
429
|
@path_prefix = path_prefix
|
|
@@ -620,7 +642,12 @@ module Hanami
|
|
|
620
642
|
# @since 2.0.0
|
|
621
643
|
# @api private
|
|
622
644
|
def fixed(env)
|
|
623
|
-
|
|
645
|
+
path_info = env[::Rack::PATH_INFO]
|
|
646
|
+
# Treat empty PATH_INFO as "/" for route matching. This allows root routes (defined as "/") to
|
|
647
|
+
# match the empty PATH_INFO that is set for requests to a mount without a trailing slash.
|
|
648
|
+
path_info = DEFAULT_PREFIX if path_info == EMPTY_STRING
|
|
649
|
+
|
|
650
|
+
@fixed.dig(env[::Rack::REQUEST_METHOD], path_info)
|
|
624
651
|
end
|
|
625
652
|
|
|
626
653
|
# @since 2.0.0
|
|
@@ -818,15 +845,13 @@ module Hanami
|
|
|
818
845
|
end
|
|
819
846
|
|
|
820
847
|
if as
|
|
821
|
-
as =
|
|
848
|
+
as = prefixed_name(as)
|
|
822
849
|
add_named_route(path, as, constraints)
|
|
823
850
|
end
|
|
824
851
|
|
|
825
852
|
if inspect?
|
|
826
853
|
@inspector.add_route(
|
|
827
|
-
Route.new(
|
|
828
|
-
http_method: http_method, path: path, to: to || endpoint, as: as, constraints: constraints, blk: blk
|
|
829
|
-
)
|
|
854
|
+
Route.new(http_method:, path:, to: to || endpoint, as:, constraints:, blk:)
|
|
830
855
|
)
|
|
831
856
|
end
|
|
832
857
|
end
|
|
@@ -862,8 +887,8 @@ module Hanami
|
|
|
862
887
|
|
|
863
888
|
# @since 2.0.0
|
|
864
889
|
# @api private
|
|
865
|
-
def add_named_route(path,
|
|
866
|
-
@url_helpers.add(
|
|
890
|
+
def add_named_route(path, name, constraints)
|
|
891
|
+
@url_helpers.add(name, Segment.fabricate(path, **constraints))
|
|
867
892
|
end
|
|
868
893
|
|
|
869
894
|
# @since 2.0.0
|
|
@@ -890,13 +915,32 @@ module Hanami
|
|
|
890
915
|
@path_prefix.join(path).to_s
|
|
891
916
|
end
|
|
892
917
|
|
|
893
|
-
# @since x.x.x
|
|
894
918
|
# @api private
|
|
895
|
-
def
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
919
|
+
def prefixed_name(name)
|
|
920
|
+
prefix, suffix = name_parts(name)
|
|
921
|
+
|
|
922
|
+
name = @name_prefix.relative_join(suffix, PREFIXED_NAME_SEPARATOR).to_s
|
|
923
|
+
name = prefix + PREFIXED_NAME_SEPARATOR + name if prefix
|
|
924
|
+
|
|
925
|
+
normalized_name(name)
|
|
926
|
+
end
|
|
927
|
+
|
|
928
|
+
# Returns a [prefix, suffix] array for a given route name.
|
|
929
|
+
#
|
|
930
|
+
# @api private
|
|
931
|
+
def name_parts(name)
|
|
932
|
+
name = Array(name)
|
|
933
|
+
|
|
934
|
+
if name.size < 2
|
|
935
|
+
[nil, name.first&.to_s]
|
|
936
|
+
else
|
|
937
|
+
[name.first&.to_s, name[1..].join("_")]
|
|
938
|
+
end
|
|
939
|
+
end
|
|
940
|
+
|
|
941
|
+
# @api private
|
|
942
|
+
def normalized_name(name)
|
|
943
|
+
name.gsub(UNDERSCORED_NAME_REGEXP, "_").to_sym
|
|
900
944
|
end
|
|
901
945
|
|
|
902
946
|
# Returns a new instance of Hanami::Router with the modified options.
|
metadata
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hanami-router
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.3.
|
|
4
|
+
version: 2.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
-
|
|
7
|
+
- Hanakai team
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
@@ -15,14 +15,14 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - ">="
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version:
|
|
18
|
+
version: 2.2.16
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version:
|
|
25
|
+
version: 2.2.16
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: mustermann
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -65,26 +65,6 @@ dependencies:
|
|
|
65
65
|
- - "~>"
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
67
|
version: '3.3'
|
|
68
|
-
- !ruby/object:Gem::Dependency
|
|
69
|
-
name: bundler
|
|
70
|
-
requirement: !ruby/object:Gem::Requirement
|
|
71
|
-
requirements:
|
|
72
|
-
- - ">="
|
|
73
|
-
- !ruby/object:Gem::Version
|
|
74
|
-
version: '1.6'
|
|
75
|
-
- - "<"
|
|
76
|
-
- !ruby/object:Gem::Version
|
|
77
|
-
version: '3'
|
|
78
|
-
type: :development
|
|
79
|
-
prerelease: false
|
|
80
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
81
|
-
requirements:
|
|
82
|
-
- - ">="
|
|
83
|
-
- !ruby/object:Gem::Version
|
|
84
|
-
version: '1.6'
|
|
85
|
-
- - "<"
|
|
86
|
-
- !ruby/object:Gem::Version
|
|
87
|
-
version: '3'
|
|
88
68
|
- !ruby/object:Gem::Dependency
|
|
89
69
|
name: rake
|
|
90
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -127,6 +107,20 @@ dependencies:
|
|
|
127
107
|
- - "~>"
|
|
128
108
|
- !ruby/object:Gem::Version
|
|
129
109
|
version: '3.8'
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: ostruct
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - ">="
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '0'
|
|
117
|
+
type: :development
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - ">="
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '0'
|
|
130
124
|
- !ruby/object:Gem::Dependency
|
|
131
125
|
name: rubocop
|
|
132
126
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -157,7 +151,7 @@ dependencies:
|
|
|
157
151
|
version: '1.0'
|
|
158
152
|
description: Rack compatible HTTP router for Ruby
|
|
159
153
|
email:
|
|
160
|
-
-
|
|
154
|
+
- info@hanakai.org
|
|
161
155
|
executables: []
|
|
162
156
|
extensions: []
|
|
163
157
|
extra_rdoc_files: []
|