hanami-router 1.3.2 → 2.0.0.alpha5

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +43 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +98 -444
  5. data/hanami-router.gemspec +23 -19
  6. data/lib/hanami/middleware/body_parser.rb +21 -15
  7. data/lib/hanami/middleware/body_parser/class_interface.rb +62 -56
  8. data/lib/hanami/middleware/body_parser/errors.rb +7 -4
  9. data/lib/hanami/middleware/body_parser/json_parser.rb +9 -7
  10. data/lib/hanami/middleware/body_parser/parser.rb +58 -0
  11. data/lib/hanami/middleware/error.rb +16 -0
  12. data/lib/hanami/router.rb +608 -955
  13. data/lib/hanami/router/block.rb +88 -0
  14. data/lib/hanami/router/error.rb +67 -0
  15. data/lib/hanami/router/inspector.rb +38 -0
  16. data/lib/hanami/router/node.rb +91 -0
  17. data/lib/hanami/router/params.rb +35 -0
  18. data/lib/hanami/router/prefix.rb +67 -0
  19. data/lib/hanami/router/recognized_route.rb +92 -0
  20. data/lib/hanami/router/redirect.rb +33 -0
  21. data/lib/hanami/router/route.rb +130 -0
  22. data/lib/hanami/router/segment.rb +19 -0
  23. data/lib/hanami/router/trie.rb +63 -0
  24. data/lib/hanami/router/url_helpers.rb +40 -0
  25. data/lib/hanami/router/version.rb +4 -1
  26. metadata +61 -39
  27. data/lib/hanami-router.rb +0 -1
  28. data/lib/hanami/routing/endpoint.rb +0 -195
  29. data/lib/hanami/routing/endpoint_resolver.rb +0 -238
  30. data/lib/hanami/routing/error.rb +0 -7
  31. data/lib/hanami/routing/force_ssl.rb +0 -212
  32. data/lib/hanami/routing/http_router.rb +0 -220
  33. data/lib/hanami/routing/http_router_monkey_patch.rb +0 -38
  34. data/lib/hanami/routing/namespace.rb +0 -98
  35. data/lib/hanami/routing/parsers.rb +0 -113
  36. data/lib/hanami/routing/parsing/json_parser.rb +0 -33
  37. data/lib/hanami/routing/parsing/parser.rb +0 -61
  38. data/lib/hanami/routing/recognized_route.rb +0 -219
  39. data/lib/hanami/routing/resource.rb +0 -119
  40. data/lib/hanami/routing/resource/action.rb +0 -402
  41. data/lib/hanami/routing/resource/nested.rb +0 -41
  42. data/lib/hanami/routing/resource/options.rb +0 -74
  43. data/lib/hanami/routing/resources.rb +0 -48
  44. data/lib/hanami/routing/resources/action.rb +0 -156
  45. data/lib/hanami/routing/route.rb +0 -71
  46. data/lib/hanami/routing/routes_inspector.rb +0 -221
@@ -1,220 +0,0 @@
1
- require 'uri'
2
- require 'http_router'
3
- require 'hanami/routing/endpoint_resolver'
4
- require 'hanami/routing/route'
5
- require 'hanami/routing/parsers'
6
- require 'hanami/routing/force_ssl'
7
- require 'hanami/routing/error'
8
- require 'hanami/utils/path_prefix'
9
- require 'hanami/routing/http_router_monkey_patch'
10
-
11
- module Hanami
12
- module Routing
13
- # Invalid route
14
- # This is raised when the router fails to recognize a route, because of the
15
- # given arguments.
16
- #
17
- # @since 0.1.0
18
- class InvalidRouteException < Hanami::Routing::Error
19
- end
20
-
21
- # HTTP router
22
- #
23
- # This implementation is based on ::HttpRouter (http_router gem).
24
- #
25
- # Hanami::Router wraps an instance of this class, in order to protect its
26
- # public API from any future change of ::HttpRouter.
27
- #
28
- # @since 0.1.0
29
- # @api private
30
- class HttpRouter < ::HttpRouter
31
- # Script name - rack environment variable
32
- #
33
- # @since 0.5.0
34
- # @api private
35
- SCRIPT_NAME = 'SCRIPT_NAME'.freeze
36
-
37
- # Path info - rack environment variable
38
- #
39
- # @since 0.7.0
40
- # @api private
41
- PATH_INFO = 'PATH_INFO'.freeze
42
-
43
- # Default PATH_INFO for Rack requests
44
- #
45
- # @since 0.7.0
46
- # @api private
47
- DEFAULT_PATH_INFO = '/'.freeze
48
-
49
- # URL separator
50
- #
51
- # @since 0.7.0
52
- # @api private
53
- URL_SEPARATOR = '/'.freeze
54
-
55
- # @since 0.5.0
56
- # @api private
57
- attr_reader :namespace
58
-
59
- # @since 0.8.0
60
- # @api private
61
- attr_reader :prefix
62
-
63
- # Initialize the router.
64
- #
65
- # @see Hanami::Router#initialize
66
- #
67
- # @since 0.1.0
68
- # @api private
69
- def initialize(options = {}, &blk)
70
- if options[:parsers]
71
- depecration_message = 'Hanami::Router options[:parsers] is deprecated and it will be removed in future versions'
72
- Hanami::Utils::Deprecation.new(depecration_message)
73
- end
74
- @compiled = false
75
- @uri_parser = URI::Parser.new
76
- super(options, &nil)
77
-
78
- @namespace = options[:namespace] if options[:namespace]
79
- @default_scheme = options[:scheme] if options[:scheme]
80
- @default_host = options[:host] if options[:host]
81
- @default_port = options[:port] if options[:port]
82
- @route_class = options[:route] || Routing::Route
83
- @resolver = options[:resolver] || Routing::EndpointResolver.new(options)
84
- @parsers = Routing::Parsers.new(options[:parsers])
85
- @prefix = Utils::PathPrefix.new(options[:prefix] || '')
86
- @force_ssl = Hanami::Routing::ForceSsl.new(!!options[:force_ssl], host: @default_host, port: @default_port)
87
- end
88
-
89
- # Separator between controller and action name.
90
- #
91
- # @see Hanami::Routing::EndpointResolver::ACTION_SEPARATOR
92
- #
93
- # @since 0.1.0
94
- # @api private
95
- def action_separator
96
- @resolver.action_separator
97
- end
98
-
99
- # Finds a path from the given options.
100
- #
101
- # @see Hanami::Routing::EndpointResolver#find
102
- #
103
- # @since 0.1.0
104
- # @api private
105
- def find(options)
106
- @resolver.find(options)
107
- end
108
-
109
- # Generate a relative URL for a specified named route.
110
- #
111
- # @see Hanami::Router#path
112
- #
113
- # @since 0.1.0
114
- # @api private
115
- def raw_path(route, *args)
116
- _rescue_url_recognition do
117
- _custom_path(super(route, *args))
118
- end
119
- end
120
-
121
- # Generate an absolute URL for a specified named route.
122
- #
123
- # @see Hanami::Router#path
124
- #
125
- # @since 0.1.0
126
- # @api private
127
- def raw_url(route, *args)
128
- _rescue_url_recognition do
129
- _custom_path(super(route, *args))
130
- end
131
- end
132
-
133
- # Support for OPTIONS HTTP verb
134
- #
135
- # @see Hanami::Router#options
136
- #
137
- # @since 0.1.0
138
- # @api private
139
- def options(path, options = {}, &blk)
140
- add_with_request_method(path, :options, options, &blk)
141
- end
142
-
143
- # Allow to mount a Rack app
144
- #
145
- # @see Hanami::Router#mount
146
- #
147
- # @since 0.1.1
148
- # @api private
149
- def mount(app, options)
150
- add("#{ options.fetch(:at) }*", host: options[:host]).to(
151
- @resolver.resolve(to: app)
152
- )
153
- end
154
-
155
- # @api private
156
- def raw_call(env, &blk)
157
- if response = @force_ssl.call(env)
158
- response
159
- else
160
- super(@parsers.call(env))
161
- end
162
- end
163
-
164
- # @api private
165
- def reset!
166
- uncompile
167
- @routes, @named_routes, @root = [], Hash.new{|h,k| h[k] = []}, Node::Root.new(self)
168
- @default_host, @default_port, @default_scheme = 'localhost', 80, 'http'
169
- end
170
-
171
- # @api private
172
- def pass_on_response(response)
173
- super response.to_a
174
- end
175
-
176
- # @api private
177
- def no_response(request, env)
178
- if request.acceptable_methods.any? && !request.acceptable_methods.include?(env['REQUEST_METHOD'])
179
- [405, {'Allow' => request.acceptable_methods.sort.join(", ")}, []]
180
- else
181
- @default_app.call(env)
182
- end
183
- end
184
-
185
- # @api private
186
- def rewrite_partial_path_info(env, request)
187
- if request.path.empty?
188
- env[SCRIPT_NAME] += env[PATH_INFO]
189
- env[PATH_INFO] = DEFAULT_PATH_INFO
190
- else
191
- path_info_before = env[PATH_INFO].dup
192
- env[PATH_INFO] = "/#{@uri_parser.escape(request.path.join(URL_SEPARATOR))}"
193
- env[SCRIPT_NAME] += path_info_before[0, path_info_before.bytesize - env[PATH_INFO].bytesize]
194
- end
195
- end
196
-
197
- private
198
-
199
- # @api private
200
- def _rescue_url_recognition
201
- yield
202
- rescue ::HttpRouter::InvalidRouteException,
203
- ::HttpRouter::TooManyParametersException => e
204
- raise Routing::InvalidRouteException.new("#{ e.message } - please check given arguments")
205
- end
206
-
207
- # @api private
208
- def add_with_request_method(path, method, opts = {}, &app)
209
- super.generate(@resolver, opts, &app)
210
- end
211
-
212
- # @api private
213
- def _custom_path(uri_string)
214
- uri = URI.parse(uri_string)
215
- uri.path = @prefix.join(uri.path)
216
- uri.to_s
217
- end
218
- end
219
- end
220
- end
@@ -1,38 +0,0 @@
1
- # coding: utf-8
2
- #
3
- # This monkey patches http_router to make it Rack 2.0 compatible.
4
- # Details see: https://github.com/hanami/router/issues/136
5
- #
6
- # @api private
7
- class HttpRouter
8
- # @api private
9
- class Node
10
- # @api private
11
- class Path < Node
12
- def to_code
13
- path_ivar = inject_root_ivar(self)
14
- "#{"if !callback && request.path.size == 1 && request.path.first == '' && (request.rack_request.head? || request.rack_request.get?) && request.rack_request.path_info[-1] == ?/
15
- response = ::Rack::Response.new
16
- response.redirect(request.rack_request.path_info[0, request.rack_request.path_info.size - 1], 302)
17
- return response.finish
18
- end" if router.redirect_trailing_slash?}
19
-
20
- #{"if request.#{router.ignore_trailing_slash? ? 'path_finished?' : 'path.empty?'}" unless route.match_partially}
21
- if callback
22
- request.called = true
23
- callback.call(Response.new(request, #{path_ivar}))
24
- else
25
- env = request.rack_request.env
26
- env['router.request'] = request
27
- env['router.params'] ||= {}
28
- #{"env['router.params'].merge!(Hash[#{param_names.inspect}.zip(request.params)])" if dynamic?}
29
- @router.rewrite#{"_partial" if route.match_partially}_path_info(env, request)
30
- response = @router.process_destination_path(#{path_ivar}, env)
31
- return response unless router.pass_on_response(response)
32
- end
33
- #{"end" unless route.match_partially}"
34
- end
35
-
36
- end
37
- end
38
- end
@@ -1,98 +0,0 @@
1
- require 'delegate'
2
- require 'hanami/utils/path_prefix'
3
-
4
- module Hanami
5
- module Routing
6
- # Namespace for routes.
7
- # Implementation of Hanami::Router#namespace
8
- #
9
- # @since 0.1.0
10
- #
11
- # @api private
12
- #
13
- # @see Hanami::Router#namespace
14
- class Namespace < SimpleDelegator
15
- # @api private
16
- # @since 0.1.0
17
- def initialize(router, name, &blk)
18
- @router = router
19
- @name = Utils::PathPrefix.new(name)
20
- __setobj__(@router)
21
- instance_eval(&blk)
22
- end
23
-
24
- # @api private
25
- # @since 0.1.0
26
- def get(path, options = {}, &endpoint)
27
- super(@name.join(path), options, &endpoint)
28
- end
29
-
30
- # @api private
31
- # @since 0.1.0
32
- def post(path, options = {}, &endpoint)
33
- super(@name.join(path), options, &endpoint)
34
- end
35
-
36
- # @api private
37
- # @since 0.1.0
38
- def put(path, options = {}, &endpoint)
39
- super(@name.join(path), options, &endpoint)
40
- end
41
-
42
- # @api private
43
- # @since 0.1.0
44
- def patch(path, options = {}, &endpoint)
45
- super(@name.join(path), options, &endpoint)
46
- end
47
-
48
- # @api private
49
- # @since 0.1.0
50
- def delete(path, options = {}, &endpoint)
51
- super(@name.join(path), options, &endpoint)
52
- end
53
-
54
- # @api private
55
- # @since 0.1.0
56
- def trace(path, options = {}, &endpoint)
57
- super(@name.join(path), options, &endpoint)
58
- end
59
-
60
- # @api private
61
- # @since 0.1.0
62
- def options(path, options = {}, &endpoint)
63
- super(@name.join(path), options, &endpoint)
64
- end
65
-
66
- # @api private
67
- # @since 0.1.0
68
- def resource(name, options = {})
69
- super name, options.merge(namespace: @name.relative_join(options[:namespace]))
70
- end
71
-
72
- # @api private
73
- # @since 0.1.0
74
- def resources(name, options = {})
75
- super name, options.merge(namespace: @name.relative_join(options[:namespace]))
76
- end
77
-
78
- # @api private
79
- # @since 0.1.0
80
- def redirect(path, options = {}, &endpoint)
81
- super(@name.join(path), options.merge(to: @name.join(options[:to])), &endpoint)
82
- end
83
-
84
- # @api private
85
- # @since 1.1.0
86
- def mount(app, options)
87
- super(app, options.merge(at: @name.join(options[:at])))
88
- end
89
-
90
- # Supports nested namespaces
91
- # @api private
92
- # @since 0.1.0
93
- def namespace(name, &blk)
94
- Routing::Namespace.new(self, name, &blk)
95
- end
96
- end
97
- end
98
- end
@@ -1,113 +0,0 @@
1
- require 'hanami/routing/parsing/parser'
2
- require 'hanami/utils/hash'
3
-
4
- module Hanami
5
- module Routing
6
- # @since 0.2.0
7
- # @api private
8
- class Parsers
9
- # @since 0.2.0
10
- # @api private
11
- CONTENT_TYPE = 'CONTENT_TYPE'.freeze
12
-
13
- # @since 0.2.0
14
- # @api private
15
- MEDIA_TYPE_MATCHER = /\s*[;,]\s*/.freeze
16
-
17
- # @since 0.2.0
18
- # @api private
19
- RACK_INPUT = 'rack.input'.freeze
20
-
21
- # @since 0.2.0
22
- # @api private
23
- ROUTER_PARAMS = 'router.params'.freeze
24
-
25
- # @api private
26
- ROUTER_PARSED_BODY = 'router.parsed_body'.freeze
27
-
28
- # @api private
29
- FALLBACK_KEY = '_'.freeze
30
-
31
- # @since 0.2.0
32
- # @api private
33
- def initialize(parsers)
34
- @parsers = prepare(parsers)
35
- _redefine_call
36
- end
37
-
38
- # @since 0.2.0
39
- # @api private
40
- def call(env)
41
- env
42
- end
43
-
44
- private
45
- # @since 0.2.0
46
- # @api private
47
- def prepare(args)
48
- result = Hash.new
49
- args = Array(args)
50
- return result if args.empty?
51
-
52
- args.each do |arg|
53
- parser = Parsing::Parser.for(arg)
54
-
55
- parser.mime_types.each do |mime|
56
- result[mime] = parser
57
- end
58
- end
59
-
60
- result.default = Parsing::Parser.new
61
- result
62
- end
63
-
64
- # @since 0.2.0
65
- # @api private
66
- def _redefine_call
67
- return if @parsers.empty?
68
-
69
- define_singleton_method :call do |env|
70
- body = env[RACK_INPUT].read
71
- return env if body.empty?
72
-
73
- env[RACK_INPUT].rewind # somebody might try to read this stream
74
-
75
- env[ROUTER_PARAMS] ||= {} # prepare params
76
- env[ROUTER_PARSED_BODY] = _parse(env, body)
77
- env[ROUTER_PARAMS] = _symbolize(env[ROUTER_PARSED_BODY]).merge(env[ROUTER_PARAMS])
78
-
79
- env
80
- end
81
- end
82
-
83
- # @api private
84
- def _symbolize(body)
85
- if body.is_a?(Hash)
86
- Utils::Hash.deep_symbolize(body)
87
- else
88
- { FALLBACK_KEY => body }
89
- end
90
- end
91
-
92
- # @api private
93
- def _parse(env, body)
94
- @parsers[
95
- media_type(env)
96
- ].parse(body)
97
- end
98
-
99
- # @api private
100
- def media_type(env)
101
- if ct = content_type(env)
102
- ct.split(MEDIA_TYPE_MATCHER, 2).first.downcase
103
- end
104
- end
105
-
106
- # @api private
107
- def content_type(env)
108
- content_type = env[CONTENT_TYPE]
109
- content_type.nil? || content_type.empty? ? nil : content_type
110
- end
111
- end
112
- end
113
- end