hanami-router 1.3.2 → 2.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
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