hanami-router 2.0.0.alpha3 → 2.0.0
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 +136 -0
- data/LICENSE.md +1 -1
- data/README.md +15 -7
- data/hanami-router.gemspec +6 -3
- data/lib/hanami/middleware/app.rb +50 -0
- data/lib/hanami/middleware/body_parser/class_interface.rb +76 -9
- data/lib/hanami/middleware/body_parser/errors.rb +2 -0
- data/lib/hanami/middleware/body_parser/json_parser.rb +3 -3
- data/lib/hanami/middleware/body_parser/parser.rb +63 -0
- data/lib/hanami/middleware/body_parser.rb +9 -22
- data/lib/hanami/middleware/error.rb +1 -1
- data/lib/hanami/middleware/node.rb +59 -0
- data/lib/hanami/middleware/trie.rb +69 -0
- data/lib/hanami/router/block.rb +2 -2
- data/lib/hanami/router/constants.rb +9 -0
- data/lib/hanami/router/errors.rb +85 -0
- data/lib/hanami/router/formatter/csv.rb +62 -0
- data/lib/hanami/router/formatter/human_friendly.rb +57 -0
- data/lib/hanami/router/inspector.rb +46 -0
- data/lib/hanami/router/node.rb +2 -4
- data/lib/hanami/router/params.rb +1 -1
- data/lib/hanami/router/prefix.rb +3 -3
- data/lib/hanami/router/recognized_route.rb +31 -6
- data/lib/hanami/router/redirect.rb +6 -1
- data/lib/hanami/router/route.rb +160 -0
- data/lib/hanami/router/trie.rb +6 -1
- data/lib/hanami/router/url_helpers.rb +7 -6
- data/lib/hanami/router/version.rb +5 -2
- data/lib/hanami/router.rb +179 -51
- metadata +49 -11
- data/lib/hanami/router/error.rb +0 -67
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hanami
|
|
4
|
+
module Middleware
|
|
5
|
+
class BodyParser
|
|
6
|
+
# Body parser abstract class
|
|
7
|
+
#
|
|
8
|
+
# @since 2.0.0
|
|
9
|
+
class Parser
|
|
10
|
+
DEFAULT_MIME_TYPES = [].freeze
|
|
11
|
+
|
|
12
|
+
# Return supported mime types
|
|
13
|
+
#
|
|
14
|
+
# @return [Array<String>] supported MIME types
|
|
15
|
+
#
|
|
16
|
+
# @abstract
|
|
17
|
+
# @since 2.0.0
|
|
18
|
+
#
|
|
19
|
+
# @example
|
|
20
|
+
# require "hanami/middleware/body_parser"
|
|
21
|
+
#
|
|
22
|
+
# class XMLParser < Hanami::Middleware::BodyParser::Parser
|
|
23
|
+
# def self.mime_types
|
|
24
|
+
# ["application/xml", "text/xml"]
|
|
25
|
+
# end
|
|
26
|
+
# end
|
|
27
|
+
attr_reader :mime_types
|
|
28
|
+
|
|
29
|
+
# @api private
|
|
30
|
+
def initialize(mime_types: DEFAULT_MIME_TYPES)
|
|
31
|
+
@mime_types = self.class.mime_types + mime_types
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Parse raw HTTP request body
|
|
35
|
+
#
|
|
36
|
+
# @param body [String] HTTP request body
|
|
37
|
+
#
|
|
38
|
+
# @return [Hash] the result of the parsing
|
|
39
|
+
#
|
|
40
|
+
# @raise [Hanami::Middleware::BodyParser::BodyParsingError] the error
|
|
41
|
+
# that must be raised if the parsing cannot be accomplished
|
|
42
|
+
#
|
|
43
|
+
# @abstract
|
|
44
|
+
# @since 2.0.0
|
|
45
|
+
#
|
|
46
|
+
# @example
|
|
47
|
+
# require "hanami/middleware/body_parser"
|
|
48
|
+
#
|
|
49
|
+
# class XMLParser < Hanami::Middleware::BodyParser::Parser
|
|
50
|
+
# def parse(body)
|
|
51
|
+
# # XML parsing
|
|
52
|
+
# # ...
|
|
53
|
+
# rescue => exception
|
|
54
|
+
# raise Hanami::Middleware::BodyParser::BodyParsingError.new(exception.message)
|
|
55
|
+
# end
|
|
56
|
+
# end
|
|
57
|
+
def parse(body) # rubocop:disable Lint/UnusedMethodArgument
|
|
58
|
+
raise NoMethodError
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -2,20 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
require "hanami/router/params"
|
|
4
4
|
require "hanami/middleware/error"
|
|
5
|
-
require_relative "
|
|
5
|
+
require_relative "../router/constants"
|
|
6
6
|
|
|
7
7
|
module Hanami
|
|
8
8
|
module Middleware
|
|
9
9
|
# @since 1.3.0
|
|
10
10
|
# @api private
|
|
11
11
|
class BodyParser
|
|
12
|
+
require_relative "body_parser/class_interface"
|
|
13
|
+
require_relative "body_parser/parser"
|
|
14
|
+
|
|
12
15
|
# @since 1.3.0
|
|
13
16
|
# @api private
|
|
14
17
|
CONTENT_TYPE = "CONTENT_TYPE"
|
|
15
18
|
|
|
16
19
|
# @since 1.3.0
|
|
17
20
|
# @api private
|
|
18
|
-
MEDIA_TYPE_MATCHER = /\s*[;,]\s
|
|
21
|
+
MEDIA_TYPE_MATCHER = /\s*[;,]\s*/
|
|
19
22
|
|
|
20
23
|
# @since 1.3.0
|
|
21
24
|
# @api private
|
|
@@ -25,9 +28,6 @@ module Hanami
|
|
|
25
28
|
# @api private
|
|
26
29
|
ROUTER_PARAMS = "router.params"
|
|
27
30
|
|
|
28
|
-
# @api private
|
|
29
|
-
ROUTER_PARSED_BODY = "router.parsed_body"
|
|
30
|
-
|
|
31
31
|
# @api private
|
|
32
32
|
FALLBACK_KEY = "_"
|
|
33
33
|
|
|
@@ -35,7 +35,7 @@ module Hanami
|
|
|
35
35
|
|
|
36
36
|
def initialize(app, parsers)
|
|
37
37
|
@app = app
|
|
38
|
-
@parsers =
|
|
38
|
+
@parsers = parsers
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
def call(env)
|
|
@@ -45,8 +45,8 @@ module Hanami
|
|
|
45
45
|
env[RACK_INPUT].rewind # somebody might try to read this stream
|
|
46
46
|
|
|
47
47
|
if (parser = @parsers[media_type(env)])
|
|
48
|
-
env[ROUTER_PARSED_BODY] = parser.parse(body)
|
|
49
|
-
env[ROUTER_PARAMS] = _symbolize(env[ROUTER_PARSED_BODY])
|
|
48
|
+
env[Router::ROUTER_PARSED_BODY] = parser.parse(body)
|
|
49
|
+
env[ROUTER_PARAMS] = _symbolize(env[Router::ROUTER_PARSED_BODY])
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
@app.call(env)
|
|
@@ -54,25 +54,12 @@ module Hanami
|
|
|
54
54
|
|
|
55
55
|
private
|
|
56
56
|
|
|
57
|
-
def build_parsers(parser_names)
|
|
58
|
-
parser_names = Array(parser_names)
|
|
59
|
-
return {} if parser_names.empty?
|
|
60
|
-
|
|
61
|
-
parser_names.each_with_object({}) do |name, parsers|
|
|
62
|
-
parser = self.class.for(name)
|
|
63
|
-
|
|
64
|
-
parser.mime_types.each do |mime|
|
|
65
|
-
parsers[mime] = parser
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
57
|
# @api private
|
|
71
58
|
def _symbolize(body)
|
|
72
59
|
if body.is_a?(::Hash)
|
|
73
60
|
Router::Params.deep_symbolize(body)
|
|
74
61
|
else
|
|
75
|
-
{
|
|
62
|
+
{FALLBACK_KEY => body}
|
|
76
63
|
end
|
|
77
64
|
end
|
|
78
65
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hanami
|
|
4
|
+
module Middleware
|
|
5
|
+
# Trie node to register scopes with custom Rack middleware
|
|
6
|
+
#
|
|
7
|
+
# @api private
|
|
8
|
+
# @since 2.0.0
|
|
9
|
+
class Node
|
|
10
|
+
# @api private
|
|
11
|
+
# @since 2.0.0
|
|
12
|
+
attr_reader :app
|
|
13
|
+
|
|
14
|
+
# @api private
|
|
15
|
+
# @since 2.0.0
|
|
16
|
+
def initialize
|
|
17
|
+
@app = nil
|
|
18
|
+
@children = {}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @api private
|
|
22
|
+
# @since 2.0.0
|
|
23
|
+
def freeze
|
|
24
|
+
@children.freeze
|
|
25
|
+
super
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# @api private
|
|
29
|
+
# @since 2.0.0
|
|
30
|
+
def put(segment)
|
|
31
|
+
@children[segment] ||= self.class.new
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @api private
|
|
35
|
+
# @since 2.0.0
|
|
36
|
+
def get(segment)
|
|
37
|
+
@children.fetch(segment) { self if leaf? }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @api private
|
|
41
|
+
# @since 2.0.0
|
|
42
|
+
def app!(app)
|
|
43
|
+
@app = app
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @api private
|
|
47
|
+
# @since 2.0.0
|
|
48
|
+
def app?
|
|
49
|
+
!@app.nil?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @api private
|
|
53
|
+
# @since 2.0.0
|
|
54
|
+
def leaf?
|
|
55
|
+
@children.empty?
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "./node"
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
module Middleware
|
|
7
|
+
# Trie to register scopes with custom Rack middleware
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
10
|
+
# @since 2.0.0
|
|
11
|
+
class Trie
|
|
12
|
+
# @api private
|
|
13
|
+
# @since 2.0.0
|
|
14
|
+
def initialize(app)
|
|
15
|
+
@app = app
|
|
16
|
+
@root = Node.new
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @api private
|
|
20
|
+
# @since 2.0.0
|
|
21
|
+
def freeze
|
|
22
|
+
@root.freeze
|
|
23
|
+
super
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# @api private
|
|
27
|
+
# @since 2.0.0
|
|
28
|
+
def add(path, app)
|
|
29
|
+
node = @root
|
|
30
|
+
for_each_segment(path) do |segment|
|
|
31
|
+
node = node.put(segment)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
node.app!(app)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @api private
|
|
38
|
+
# @since 2.0.0
|
|
39
|
+
def find(path)
|
|
40
|
+
node = @root
|
|
41
|
+
|
|
42
|
+
for_each_segment(path) do |segment|
|
|
43
|
+
break unless node
|
|
44
|
+
|
|
45
|
+
node = node.get(segment)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
return node.app if node&.app?
|
|
49
|
+
|
|
50
|
+
@root.app || @app
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @api private
|
|
54
|
+
# @since 2.0.0
|
|
55
|
+
def empty?
|
|
56
|
+
@root.leaf?
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
# @api private
|
|
62
|
+
# @since 2.0.0
|
|
63
|
+
def for_each_segment(path, &blk)
|
|
64
|
+
_, *segments = path.split(/\//)
|
|
65
|
+
segments.each(&blk)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
data/lib/hanami/router/block.rb
CHANGED
|
@@ -35,7 +35,7 @@ module Hanami
|
|
|
35
35
|
if value
|
|
36
36
|
@status = value
|
|
37
37
|
else
|
|
38
|
-
@status ||=
|
|
38
|
+
@status ||= Router::HTTP_STATUS_OK
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -59,7 +59,7 @@ module Hanami
|
|
|
59
59
|
#
|
|
60
60
|
# @since 2.0.0
|
|
61
61
|
def params
|
|
62
|
-
env[
|
|
62
|
+
env[Router::PARAMS]
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
# @api private
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hanami
|
|
4
|
+
class Router
|
|
5
|
+
# Base class for all Hanami::Router errors.
|
|
6
|
+
#
|
|
7
|
+
# @since 0.5.0
|
|
8
|
+
# @api public
|
|
9
|
+
class Error < StandardError
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Error raised when no endpoint is specified for a route.
|
|
13
|
+
#
|
|
14
|
+
# Endpoints must be specified by `to:` or a block.
|
|
15
|
+
#
|
|
16
|
+
# @since 2.0.0
|
|
17
|
+
# @api public
|
|
18
|
+
class MissingEndpointError < Error
|
|
19
|
+
# @since 2.0.0
|
|
20
|
+
# @api private
|
|
21
|
+
def initialize(path)
|
|
22
|
+
super("missing endpoint for #{path.inspect}")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Error raised when a named route could not be found.
|
|
27
|
+
#
|
|
28
|
+
# @see Hanami::Router#path
|
|
29
|
+
# @see Hanami::Router#url
|
|
30
|
+
#
|
|
31
|
+
# @since 2.0.0
|
|
32
|
+
# @api public
|
|
33
|
+
class MissingRouteError < Error
|
|
34
|
+
# @since 2.0.0
|
|
35
|
+
# @api private
|
|
36
|
+
def initialize(name)
|
|
37
|
+
super("No route could be found with name #{name.inspect}")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Error raised when variables given for route cannot be expanded into a full path.
|
|
42
|
+
#
|
|
43
|
+
# @see Hanami::Router#path
|
|
44
|
+
# @see Hanami::Router#url
|
|
45
|
+
#
|
|
46
|
+
# @since 2.0.0
|
|
47
|
+
# @api public
|
|
48
|
+
class InvalidRouteExpansionError < Error
|
|
49
|
+
# @since 2.0.0
|
|
50
|
+
# @api private
|
|
51
|
+
def initialize(name, message)
|
|
52
|
+
super("No route could be generated for `#{name.inspect}': #{message}")
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Error raised when an unknown HTTP status code is given.
|
|
57
|
+
#
|
|
58
|
+
# @see Hanami::Router#redirect
|
|
59
|
+
#
|
|
60
|
+
# @since 2.0.0
|
|
61
|
+
# @api public
|
|
62
|
+
class UnknownHTTPStatusCodeError < Error
|
|
63
|
+
# @since 2.0.0
|
|
64
|
+
# @api private
|
|
65
|
+
def initialize(code)
|
|
66
|
+
super("Unknown HTTP status code: #{code.inspect}")
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Error raised when a recognized route is called but has no callable endpoint.
|
|
71
|
+
#
|
|
72
|
+
# @see Hanami::Router#recognize
|
|
73
|
+
# @see Hanami::Router::RecognizedRoute#call
|
|
74
|
+
#
|
|
75
|
+
# @since 0.5.0
|
|
76
|
+
# @api public
|
|
77
|
+
class NotRoutableEndpointError < Error
|
|
78
|
+
# @since 0.5.0
|
|
79
|
+
# @api private
|
|
80
|
+
def initialize(env)
|
|
81
|
+
super %(Cannot find routable endpoint for: #{env[::Rack::REQUEST_METHOD]} #{env[::Rack::PATH_INFO]})
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "csv"
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
class Router
|
|
7
|
+
# Renders a CSV representation of the routes
|
|
8
|
+
#
|
|
9
|
+
# You can forward [CSV generation
|
|
10
|
+
# options](https://ruby-doc.org/stdlib-3.1.0/libdoc/csv/rdoc/CSV.html#class-CSV-label-Options+for+Generating]
|
|
11
|
+
# when calling it:
|
|
12
|
+
#
|
|
13
|
+
# ```
|
|
14
|
+
# require "hanami/router/inspector"
|
|
15
|
+
# require "hanami/router/formatter/csv"
|
|
16
|
+
#
|
|
17
|
+
# Hanami::Router::Inspector.new(
|
|
18
|
+
# routes: Router.routes,
|
|
19
|
+
# formatter: Hanami::Router::Formatter::CSV.new
|
|
20
|
+
# ).call(write_headers: false)
|
|
21
|
+
# ```
|
|
22
|
+
#
|
|
23
|
+
# @api private
|
|
24
|
+
# @since 2.0.0
|
|
25
|
+
module Formatter
|
|
26
|
+
class CSV
|
|
27
|
+
# @api private
|
|
28
|
+
# @since 2.0.0
|
|
29
|
+
DEFAULT_OPTIONS = {
|
|
30
|
+
write_headers: true
|
|
31
|
+
}.freeze
|
|
32
|
+
|
|
33
|
+
# @api private
|
|
34
|
+
# @since 2.0.0
|
|
35
|
+
HEADERS = %w[METHOD PATH TO AS CONSTRAINTS].freeze
|
|
36
|
+
|
|
37
|
+
# @api private
|
|
38
|
+
# @since 2.0.0
|
|
39
|
+
def call(routes, **csv_opts)
|
|
40
|
+
::CSV.generate(**DEFAULT_OPTIONS.merge(csv_opts)) do |csv|
|
|
41
|
+
csv << HEADERS if csv.write_headers?
|
|
42
|
+
routes.reduce(csv) do |acc, route|
|
|
43
|
+
route.head? ? acc : acc << row(route)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def row(route)
|
|
51
|
+
[
|
|
52
|
+
route.http_method.to_s,
|
|
53
|
+
route.path,
|
|
54
|
+
route.inspect_to,
|
|
55
|
+
route.as? ? route.inspect_as : "",
|
|
56
|
+
route.constraints? ? route.inspect_constraints : ""
|
|
57
|
+
]
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hanami
|
|
4
|
+
class Router
|
|
5
|
+
# Renders a human friendly representation of the routes
|
|
6
|
+
#
|
|
7
|
+
# @api private
|
|
8
|
+
# @since 2.0.0
|
|
9
|
+
module Formatter
|
|
10
|
+
class HumanFriendly
|
|
11
|
+
# @api private
|
|
12
|
+
# @since 2.0.0
|
|
13
|
+
NEW_LINE = $/
|
|
14
|
+
private_constant :NEW_LINE
|
|
15
|
+
|
|
16
|
+
# @api private
|
|
17
|
+
# @since 2.0.0
|
|
18
|
+
SMALL_STRING_JUSTIFY_AMOUNT = 8
|
|
19
|
+
private_constant :SMALL_STRING_JUSTIFY_AMOUNT
|
|
20
|
+
|
|
21
|
+
# @api private
|
|
22
|
+
# @since 2.0.0
|
|
23
|
+
MEDIUM_STRING_JUSTIFY_AMOUNT = 20
|
|
24
|
+
private_constant :MEDIUM_STRING_JUSTIFY_AMOUNT
|
|
25
|
+
|
|
26
|
+
# @api private
|
|
27
|
+
# @since 2.0.0
|
|
28
|
+
LARGE_STRING_JUSTIFY_AMOUNT = 30
|
|
29
|
+
private_constant :LARGE_STRING_JUSTIFY_AMOUNT
|
|
30
|
+
|
|
31
|
+
# @api private
|
|
32
|
+
# @since 2.0.0
|
|
33
|
+
EXTRA_LARGE_STRING_JUSTIFY_AMOUNT = 40
|
|
34
|
+
private_constant :EXTRA_LARGE_STRING_JUSTIFY_AMOUNT
|
|
35
|
+
|
|
36
|
+
# @api private
|
|
37
|
+
# @since 2.0.0
|
|
38
|
+
def call(routes)
|
|
39
|
+
routes.filter_map(&method(:format_route_unless_head)).join(NEW_LINE)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def format_route_unless_head(route)
|
|
45
|
+
!route.head? &&
|
|
46
|
+
[
|
|
47
|
+
route.http_method.to_s.ljust(SMALL_STRING_JUSTIFY_AMOUNT),
|
|
48
|
+
route.path.ljust(LARGE_STRING_JUSTIFY_AMOUNT),
|
|
49
|
+
route.inspect_to.ljust(LARGE_STRING_JUSTIFY_AMOUNT),
|
|
50
|
+
route.as? ? "as #{route.inspect_as}".ljust(MEDIUM_STRING_JUSTIFY_AMOUNT) : "",
|
|
51
|
+
route.constraints? ? "(#{route.inspect_constraints})".ljust(EXTRA_LARGE_STRING_JUSTIFY_AMOUNT) : ""
|
|
52
|
+
].join
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "hanami/router/formatter/human_friendly"
|
|
4
|
+
|
|
5
|
+
module Hanami
|
|
6
|
+
class Router
|
|
7
|
+
# Builds a representation of an array of routes according to a given formatter.
|
|
8
|
+
#
|
|
9
|
+
# @see Router.new
|
|
10
|
+
#
|
|
11
|
+
# @since 2.0.0
|
|
12
|
+
# @api private
|
|
13
|
+
class Inspector
|
|
14
|
+
# @param routes [Array<Hanami::Route>]
|
|
15
|
+
# @param formatter [#call] routes formatter, taking routes as an argument and returning its
|
|
16
|
+
# own representation (typically a string). Defaults to {Formatter::HumanFriendly}.
|
|
17
|
+
#
|
|
18
|
+
# @since 2.0.0
|
|
19
|
+
# @api public
|
|
20
|
+
def initialize(routes: [], formatter: Formatter::HumanFriendly.new)
|
|
21
|
+
@routes = routes
|
|
22
|
+
@formatter = formatter
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Adds a route to be inspected.
|
|
26
|
+
#
|
|
27
|
+
# @param route [Route]
|
|
28
|
+
#
|
|
29
|
+
# @since 2.0.0
|
|
30
|
+
# @api public
|
|
31
|
+
def add_route(route)
|
|
32
|
+
@routes.push(route)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Calls the formatter for all added routes.
|
|
36
|
+
#
|
|
37
|
+
# @return [Any] Formatted routes
|
|
38
|
+
#
|
|
39
|
+
# @since 2.0.0
|
|
40
|
+
# @api public
|
|
41
|
+
def call(...)
|
|
42
|
+
@formatter.call(@routes, ...)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/hanami/router/node.rb
CHANGED
|
@@ -36,8 +36,7 @@ module Hanami
|
|
|
36
36
|
# @api private
|
|
37
37
|
# @since 2.0.0
|
|
38
38
|
#
|
|
39
|
-
# rubocop:disable Metrics/
|
|
40
|
-
def get(segment)
|
|
39
|
+
def get(segment) # rubocop:disable Metrics/PerceivedComplexity
|
|
41
40
|
return unless @variable || @fixed
|
|
42
41
|
|
|
43
42
|
found = nil
|
|
@@ -55,7 +54,6 @@ module Hanami
|
|
|
55
54
|
|
|
56
55
|
[found, captured&.named_captures]
|
|
57
56
|
end
|
|
58
|
-
# rubocop:enable Metrics/MethodLength
|
|
59
57
|
|
|
60
58
|
# @api private
|
|
61
59
|
# @since 2.0.0
|
|
@@ -74,7 +72,7 @@ module Hanami
|
|
|
74
72
|
# @api private
|
|
75
73
|
# @since 2.0.0
|
|
76
74
|
def variable?(segment)
|
|
77
|
-
|
|
75
|
+
Router::ROUTE_VARIABLE_MATCHER.match?(segment)
|
|
78
76
|
end
|
|
79
77
|
|
|
80
78
|
# @api private
|
data/lib/hanami/router/params.rb
CHANGED
data/lib/hanami/router/prefix.rb
CHANGED
|
@@ -25,7 +25,7 @@ module Hanami
|
|
|
25
25
|
# @api private
|
|
26
26
|
def relative_join(path, separator = DEFAULT_SEPARATOR)
|
|
27
27
|
_join(path.to_s)
|
|
28
|
-
.gsub(DEFAULT_SEPARATOR_REGEXP, separator)[1
|
|
28
|
+
.gsub(DEFAULT_SEPARATOR_REGEXP, separator)[1..]
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
# @since 2.0.0
|
|
@@ -48,11 +48,11 @@ module Hanami
|
|
|
48
48
|
|
|
49
49
|
# @since 2.0.0
|
|
50
50
|
# @api private
|
|
51
|
-
DEFAULT_SEPARATOR_REGEXP =
|
|
51
|
+
DEFAULT_SEPARATOR_REGEXP = /\//
|
|
52
52
|
|
|
53
53
|
# @since 2.0.0
|
|
54
54
|
# @api private
|
|
55
|
-
DOUBLE_DEFAULT_SEPARATOR_REGEXP =
|
|
55
|
+
DOUBLE_DEFAULT_SEPARATOR_REGEXP = /\/{2,}/
|
|
56
56
|
|
|
57
57
|
# @since 2.0.0
|
|
58
58
|
# @api private
|