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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -3
- data/README.md +192 -154
- data/hanami-router.gemspec +23 -20
- data/lib/hanami/middleware/body_parser.rb +17 -13
- data/lib/hanami/middleware/body_parser/class_interface.rb +56 -56
- data/lib/hanami/middleware/body_parser/errors.rb +7 -4
- data/lib/hanami/middleware/body_parser/json_parser.rb +5 -3
- data/lib/hanami/middleware/error.rb +16 -0
- data/lib/hanami/router.rb +262 -149
- data/lib/hanami/router/version.rb +3 -1
- data/lib/hanami/routing.rb +193 -0
- data/lib/hanami/routing/endpoint.rb +122 -104
- data/lib/hanami/routing/endpoint_resolver.rb +20 -16
- data/lib/hanami/routing/prefix.rb +102 -0
- data/lib/hanami/routing/recognized_route.rb +40 -26
- data/lib/hanami/routing/resource.rb +9 -7
- data/lib/hanami/routing/resource/action.rb +58 -33
- data/lib/hanami/routing/resource/nested.rb +4 -1
- data/lib/hanami/routing/resource/options.rb +3 -1
- data/lib/hanami/routing/resources.rb +6 -4
- data/lib/hanami/routing/resources/action.rb +11 -6
- data/lib/hanami/routing/routes_inspector.rb +22 -20
- data/lib/hanami/routing/scope.rb +112 -0
- metadata +47 -25
- data/lib/hanami-router.rb +0 -1
- data/lib/hanami/routing/error.rb +0 -7
- data/lib/hanami/routing/force_ssl.rb +0 -212
- data/lib/hanami/routing/http_router.rb +0 -220
- data/lib/hanami/routing/http_router_monkey_patch.rb +0 -38
- data/lib/hanami/routing/namespace.rb +0 -98
- data/lib/hanami/routing/parsers.rb +0 -113
- data/lib/hanami/routing/parsing/json_parser.rb +0 -33
- data/lib/hanami/routing/parsing/parser.rb +0 -61
- data/lib/hanami/routing/route.rb +0 -71
data/lib/hanami-router.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require 'hanami/router'
|
data/lib/hanami/routing/error.rb
DELETED
@@ -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
|