hanami-router 1.3.2 → 2.0.0.alpha1

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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -3
  3. data/README.md +192 -154
  4. data/hanami-router.gemspec +23 -20
  5. data/lib/hanami/middleware/body_parser.rb +17 -13
  6. data/lib/hanami/middleware/body_parser/class_interface.rb +56 -56
  7. data/lib/hanami/middleware/body_parser/errors.rb +7 -4
  8. data/lib/hanami/middleware/body_parser/json_parser.rb +5 -3
  9. data/lib/hanami/middleware/error.rb +16 -0
  10. data/lib/hanami/router.rb +262 -149
  11. data/lib/hanami/router/version.rb +3 -1
  12. data/lib/hanami/routing.rb +193 -0
  13. data/lib/hanami/routing/endpoint.rb +122 -104
  14. data/lib/hanami/routing/endpoint_resolver.rb +20 -16
  15. data/lib/hanami/routing/prefix.rb +102 -0
  16. data/lib/hanami/routing/recognized_route.rb +40 -26
  17. data/lib/hanami/routing/resource.rb +9 -7
  18. data/lib/hanami/routing/resource/action.rb +58 -33
  19. data/lib/hanami/routing/resource/nested.rb +4 -1
  20. data/lib/hanami/routing/resource/options.rb +3 -1
  21. data/lib/hanami/routing/resources.rb +6 -4
  22. data/lib/hanami/routing/resources/action.rb +11 -6
  23. data/lib/hanami/routing/routes_inspector.rb +22 -20
  24. data/lib/hanami/routing/scope.rb +112 -0
  25. metadata +47 -25
  26. data/lib/hanami-router.rb +0 -1
  27. data/lib/hanami/routing/error.rb +0 -7
  28. data/lib/hanami/routing/force_ssl.rb +0 -212
  29. data/lib/hanami/routing/http_router.rb +0 -220
  30. data/lib/hanami/routing/http_router_monkey_patch.rb +0 -38
  31. data/lib/hanami/routing/namespace.rb +0 -98
  32. data/lib/hanami/routing/parsers.rb +0 -113
  33. data/lib/hanami/routing/parsing/json_parser.rb +0 -33
  34. data/lib/hanami/routing/parsing/parser.rb +0 -61
  35. data/lib/hanami/routing/route.rb +0 -71
@@ -1 +0,0 @@
1
- require 'hanami/router'
@@ -1,7 +0,0 @@
1
- module Hanami
2
- module Routing
3
- # @since 0.5.0
4
- class Error < ::StandardError
5
- end
6
- end
7
- end
@@ -1,212 +0,0 @@
1
- require 'rack/request'
2
- require 'hanami/utils/deprecation'
3
-
4
- module Hanami
5
- module Routing
6
- # Force ssl
7
- #
8
- # Redirect response to the secure equivalent resource (https)
9
- #
10
- # @since 0.4.1
11
- # @api private
12
- class ForceSsl
13
- # Https scheme
14
- #
15
- # @since 0.4.1
16
- # @api private
17
- SSL_SCHEME = 'https'.freeze
18
-
19
- # @since 0.4.1
20
- # @api private
21
- HTTPS = 'HTTPS'.freeze
22
-
23
- # @since 0.4.1
24
- # @api private
25
- ON = 'on'.freeze
26
-
27
- # Location header
28
- #
29
- # @since 0.4.1
30
- # @api private
31
- LOCATION_HEADER = 'Location'.freeze
32
-
33
- # Default http port
34
- #
35
- # @since 0.4.1
36
- # @api private
37
- DEFAULT_HTTP_PORT = 80
38
-
39
- # Default ssl port
40
- #
41
- # @since 0.4.1
42
- # @api private
43
- DEFAULT_SSL_PORT = 443
44
-
45
- # Moved Permanently http code
46
- #
47
- # @since 0.4.1
48
- # @api private
49
- MOVED_PERMANENTLY_HTTP_CODE = 301
50
-
51
- # Temporary Redirect http code
52
- #
53
- # @since 0.4.1
54
- # @api private
55
- TEMPORARY_REDIRECT_HTTP_CODE = 307
56
-
57
- # @since 0.4.1
58
- # @api private
59
- HTTP_X_FORWARDED_SSL = 'HTTP_X_FORWARDED_SSL'.freeze
60
-
61
- # @since 0.4.1
62
- # @api private
63
- HTTP_X_FORWARDED_SCHEME = 'HTTP_X_FORWARDED_SCHEME'.freeze
64
-
65
- # @since 0.4.1
66
- # @api private
67
- HTTP_X_FORWARDED_PROTO = 'HTTP_X_FORWARDED_PROTO'.freeze
68
-
69
- # @since 0.4.1
70
- # @api private
71
- HTTP_X_FORWARDED_PROTO_SEPARATOR = ','.freeze
72
-
73
- # @since 0.4.1
74
- # @api private
75
- RACK_URL_SCHEME = 'rack.url_scheme'.freeze
76
-
77
- # @since 0.4.1
78
- # @api private
79
- REQUEST_METHOD = 'REQUEST_METHOD'.freeze
80
-
81
- # @since 0.4.1
82
- # @api private
83
- IDEMPOTENT_METHODS = ['GET', 'HEAD'].freeze
84
-
85
- EMPTY_BODY = [].freeze
86
-
87
- # Initialize ForceSsl.
88
- #
89
- # @param active [Boolean] activate redirection to SSL
90
- # @param options [Hash] set of options
91
- # @option options [String] :host
92
- # @option options [Integer] :port
93
- #
94
- # @since 0.4.1
95
- # @api private
96
- def initialize(active, options = {})
97
- @active = active
98
- @host = options[:host]
99
- @port = options[:port]
100
-
101
- _redefine_call
102
- end
103
-
104
- # Set 301 status and Location header if this feature is activated.
105
- #
106
- # @param env [Hash] a Rack env instance
107
- #
108
- # @return [Array]
109
- #
110
- # @see Hanami::Routing::HttpRouter#call
111
- #
112
- # @since 0.4.1
113
- # @api private
114
- def call(env)
115
- end
116
-
117
- # Check if router has to force the response with ssl
118
- #
119
- # @return [Boolean]
120
- #
121
- # @since 0.4.1
122
- # @api private
123
- def force?(env)
124
- !ssl?(env)
125
- end
126
-
127
- private
128
-
129
- # @since 0.4.1
130
- # @api private
131
- attr_reader :host
132
-
133
- # Return full url to redirect
134
- #
135
- # @param env [Hash] Rack env
136
- #
137
- # @return [String]
138
- #
139
- # @since 0.4.1
140
- # @api private
141
- def full_url(env)
142
- "#{ SSL_SCHEME }://#{ host }:#{ port }#{ ::Rack::Request.new(env).fullpath }"
143
- end
144
-
145
- # Return redirect code
146
- #
147
- # @param env [Hash] Rack env
148
- #
149
- # @return [Integer]
150
- #
151
- # @since 0.4.1
152
- # @api private
153
- def redirect_code(env)
154
- if IDEMPOTENT_METHODS.include?(env[REQUEST_METHOD])
155
- MOVED_PERMANENTLY_HTTP_CODE
156
- else
157
- TEMPORARY_REDIRECT_HTTP_CODE
158
- end
159
- end
160
-
161
- # Return correct default port for full url
162
- #
163
- # @return [Integer]
164
- #
165
- # @since 0.4.1
166
- # @api private
167
- def port
168
- if @port == DEFAULT_HTTP_PORT
169
- DEFAULT_SSL_PORT
170
- else
171
- @port
172
- end
173
- end
174
-
175
- # @since 0.4.1
176
- # @api private
177
- def _redefine_call
178
- return unless @active
179
-
180
- Hanami::Utils::Deprecation.new('force_ssl option is deprecated, please delegate this behaviour to Nginx/Apache or use a Rack middleware like `rack-ssl`')
181
-
182
- define_singleton_method :call do |env|
183
- [redirect_code(env), { LOCATION_HEADER => full_url(env) }, EMPTY_BODY] if force?(env)
184
- end
185
- end
186
-
187
- # Adapted from Rack::Request#scheme
188
- #
189
- # @since 0.4.1
190
- # @api private
191
- def scheme(env)
192
- if env[HTTPS] == ON
193
- SSL_SCHEME
194
- elsif env[HTTP_X_FORWARDED_SSL] == ON
195
- SSL_SCHEME
196
- elsif env[HTTP_X_FORWARDED_SCHEME]
197
- env[HTTP_X_FORWARDED_SCHEME]
198
- elsif env[HTTP_X_FORWARDED_PROTO]
199
- env[HTTP_X_FORWARDED_PROTO].split(HTTP_X_FORWARDED_PROTO_SEPARATOR)[0]
200
- else
201
- env[RACK_URL_SCHEME]
202
- end
203
- end
204
-
205
- # @since 0.4.1
206
- # @api private
207
- def ssl?(env)
208
- scheme(env) == SSL_SCHEME
209
- end
210
- end
211
- end
212
- end
@@ -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