hanami-router 1.3.1 → 2.0.0.alpha4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +97 -443
  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 +548 -957
  13. data/lib/hanami/router/block.rb +88 -0
  14. data/lib/hanami/router/error.rb +67 -0
  15. data/lib/hanami/router/node.rb +91 -0
  16. data/lib/hanami/router/params.rb +35 -0
  17. data/lib/hanami/router/prefix.rb +67 -0
  18. data/lib/hanami/router/recognized_route.rb +92 -0
  19. data/lib/hanami/router/redirect.rb +28 -0
  20. data/lib/hanami/router/segment.rb +19 -0
  21. data/lib/hanami/router/trie.rb +63 -0
  22. data/lib/hanami/router/url_helpers.rb +40 -0
  23. data/lib/hanami/router/version.rb +4 -1
  24. metadata +61 -41
  25. data/lib/hanami-router.rb +0 -1
  26. data/lib/hanami/routing/endpoint.rb +0 -195
  27. data/lib/hanami/routing/endpoint_resolver.rb +0 -238
  28. data/lib/hanami/routing/error.rb +0 -7
  29. data/lib/hanami/routing/force_ssl.rb +0 -212
  30. data/lib/hanami/routing/http_router.rb +0 -220
  31. data/lib/hanami/routing/http_router_monkey_patch.rb +0 -38
  32. data/lib/hanami/routing/namespace.rb +0 -98
  33. data/lib/hanami/routing/parsers.rb +0 -113
  34. data/lib/hanami/routing/parsing/json_parser.rb +0 -33
  35. data/lib/hanami/routing/parsing/parser.rb +0 -61
  36. data/lib/hanami/routing/recognized_route.rb +0 -219
  37. data/lib/hanami/routing/resource.rb +0 -119
  38. data/lib/hanami/routing/resource/action.rb +0 -402
  39. data/lib/hanami/routing/resource/nested.rb +0 -41
  40. data/lib/hanami/routing/resource/options.rb +0 -74
  41. data/lib/hanami/routing/resources.rb +0 -48
  42. data/lib/hanami/routing/resources/action.rb +0 -156
  43. data/lib/hanami/routing/route.rb +0 -71
  44. 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