hanami-router 2.0.0.alpha1 → 2.0.0.alpha2
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 +16 -0
- data/README.md +11 -401
- data/hanami-router.gemspec +2 -4
- data/lib/hanami/middleware/body_parser.rb +2 -2
- data/lib/hanami/middleware/body_parser/class_interface.rb +10 -4
- data/lib/hanami/middleware/body_parser/json_parser.rb +4 -4
- data/lib/hanami/router.rb +525 -1040
- data/lib/hanami/router/block.rb +88 -0
- data/lib/hanami/router/error.rb +67 -0
- data/lib/hanami/router/node.rb +93 -0
- data/lib/hanami/router/params.rb +35 -0
- data/lib/hanami/router/prefix.rb +65 -0
- data/lib/hanami/router/recognized_route.rb +92 -0
- data/lib/hanami/router/redirect.rb +28 -0
- data/lib/hanami/router/segment.rb +19 -0
- data/lib/hanami/router/trie.rb +63 -0
- data/lib/hanami/router/url_helpers.rb +40 -0
- data/lib/hanami/router/version.rb +2 -1
- metadata +17 -48
- data/lib/hanami/routing.rb +0 -193
- data/lib/hanami/routing/endpoint.rb +0 -213
- data/lib/hanami/routing/endpoint_resolver.rb +0 -242
- data/lib/hanami/routing/prefix.rb +0 -102
- data/lib/hanami/routing/recognized_route.rb +0 -233
- data/lib/hanami/routing/resource.rb +0 -121
- data/lib/hanami/routing/resource/action.rb +0 -427
- data/lib/hanami/routing/resource/nested.rb +0 -44
- data/lib/hanami/routing/resource/options.rb +0 -76
- data/lib/hanami/routing/resources.rb +0 -50
- data/lib/hanami/routing/resources/action.rb +0 -161
- data/lib/hanami/routing/routes_inspector.rb +0 -223
- data/lib/hanami/routing/scope.rb +0 -112
data/hanami-router.gemspec
CHANGED
@@ -23,11 +23,9 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.add_dependency "rack", "~> 2.0"
|
24
24
|
spec.add_dependency "mustermann", "~> 1.0"
|
25
25
|
spec.add_dependency "mustermann-contrib", "~> 1.0"
|
26
|
-
spec.add_dependency "hanami-utils", "~> 2.0.alpha"
|
27
|
-
spec.add_dependency "dry-inflector", "~> 0.1"
|
28
26
|
|
29
27
|
spec.add_development_dependency "bundler", ">= 1.6", "< 3"
|
30
|
-
spec.add_development_dependency "rake", "~>
|
28
|
+
spec.add_development_dependency "rake", "~> 13"
|
31
29
|
spec.add_development_dependency "rack-test", "~> 1.0"
|
32
|
-
spec.add_development_dependency "rspec", "~> 3.
|
30
|
+
spec.add_development_dependency "rspec", "~> 3.8"
|
33
31
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "hanami/
|
3
|
+
require "hanami/router/params"
|
4
4
|
require "hanami/middleware/error"
|
5
5
|
require_relative "body_parser/class_interface"
|
6
6
|
|
@@ -70,7 +70,7 @@ module Hanami
|
|
70
70
|
# @api private
|
71
71
|
def _symbolize(body)
|
72
72
|
if body.is_a?(::Hash)
|
73
|
-
|
73
|
+
Router::Params.deep_symbolize(body)
|
74
74
|
else
|
75
75
|
{ FALLBACK_KEY => body }
|
76
76
|
end
|
@@ -1,11 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "hanami/utils/class"
|
4
|
-
require "hanami/utils/string"
|
5
3
|
require_relative "errors"
|
6
4
|
|
7
5
|
module Hanami
|
8
6
|
module Middleware
|
7
|
+
# HTTP request body parser
|
9
8
|
class BodyParser
|
10
9
|
# @api private
|
11
10
|
# @since 1.3.0
|
@@ -45,11 +44,18 @@ module Hanami
|
|
45
44
|
def require_parser(parser)
|
46
45
|
require "hanami/middleware/body_parser/#{parser}_parser"
|
47
46
|
|
48
|
-
|
49
|
-
Utils::Class.load!("Hanami::Middleware::BodyParser::#{parser}Parser").new
|
47
|
+
load_parser!("#{classify(parser)}Parser").new
|
50
48
|
rescue LoadError, NameError
|
51
49
|
raise UnknownParserError.new(parser)
|
52
50
|
end
|
51
|
+
|
52
|
+
def classify(parser)
|
53
|
+
parser.to_s.split(/_/).map(&:capitalize).join
|
54
|
+
end
|
55
|
+
|
56
|
+
def load_parser!(class_name)
|
57
|
+
Hanami::Middleware::BodyParser.const_get(class_name, false)
|
58
|
+
end
|
53
59
|
end
|
54
60
|
end
|
55
61
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "json"
|
4
4
|
require_relative "errors"
|
5
5
|
|
6
6
|
module Hanami
|
@@ -26,9 +26,9 @@ module Hanami
|
|
26
26
|
# @since 1.3.0
|
27
27
|
# @api private
|
28
28
|
def parse(body)
|
29
|
-
|
30
|
-
rescue
|
31
|
-
raise BodyParsingError.new(
|
29
|
+
JSON.parse(body)
|
30
|
+
rescue StandardError => exception
|
31
|
+
raise BodyParsingError.new(exception.message)
|
32
32
|
end
|
33
33
|
end
|
34
34
|
end
|
data/lib/hanami/router.rb
CHANGED
@@ -1,136 +1,30 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "rack/
|
4
|
-
|
5
|
-
require "hanami/routing"
|
6
|
-
require "hanami/utils/hash"
|
7
|
-
|
8
|
-
# Hanami
|
9
|
-
#
|
10
|
-
# @since 0.1.0
|
3
|
+
require "rack/utils"
|
4
|
+
|
11
5
|
module Hanami
|
12
6
|
# Rack compatible, lightweight and fast HTTP Router.
|
13
7
|
#
|
14
8
|
# @since 0.1.0
|
15
|
-
#
|
16
|
-
# @example It offers an intuitive DSL, that supports most of the HTTP verbs:
|
17
|
-
# require 'hanami/router'
|
18
|
-
#
|
19
|
-
# endpoint = ->(env) { [200, {}, ['Welcome to Hanami::Router!']] }
|
20
|
-
# router = Hanami::Router.new do
|
21
|
-
# get '/', to: endpoint # => get and head requests
|
22
|
-
# post '/', to: endpoint
|
23
|
-
# put '/', to: endpoint
|
24
|
-
# patch '/', to: endpoint
|
25
|
-
# delete '/', to: endpoint
|
26
|
-
# options '/', to: endpoint
|
27
|
-
# trace '/', to: endpoint
|
28
|
-
# end
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
# @example Specify an endpoint with `:to` (Rack compatible object)
|
33
|
-
# require 'hanami/router'
|
34
|
-
#
|
35
|
-
# endpoint = ->(env) { [200, {}, ['Welcome to Hanami::Router!']] }
|
36
|
-
# router = Hanami::Router.new do
|
37
|
-
# get '/', to: endpoint
|
38
|
-
# end
|
39
|
-
#
|
40
|
-
# # :to is mandatory for the default resolver (`Hanami::Routing::EndpointResolver.new`),
|
41
|
-
# # This behavior can be changed by passing a custom resolver to `Hanami::Router#initialize`
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
# @example Specify an endpoint with `:to` (controller and action string)
|
46
|
-
# require 'hanami/router'
|
47
|
-
#
|
48
|
-
# router = Hanami::Router.new do
|
49
|
-
# get '/', to: 'articles#show' # => Articles::Show
|
50
|
-
# end
|
51
|
-
#
|
52
|
-
# # This is a builtin feature for a Hanami::Controller convention.
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
# @example Specify a named route with `:as`
|
57
|
-
# require 'hanami/router'
|
58
|
-
#
|
59
|
-
# endpoint = ->(env) { [200, {}, ['Welcome to Hanami::Router!']] }
|
60
|
-
# router = Hanami::Router.new(scheme: 'https', host: 'hanamirb.org') do
|
61
|
-
# get '/', to: endpoint, as: :root
|
62
|
-
# end
|
63
|
-
#
|
64
|
-
# router.path(:root) # => '/'
|
65
|
-
# router.url(:root) # => 'https://hanamirb.org/'
|
66
|
-
#
|
67
|
-
# # This isn't mandatory for the default route class (`Hanami::Routing::Route`),
|
68
|
-
# # This behavior can be changed by passing a custom route to `Hanami::Router#initialize`
|
69
|
-
#
|
70
|
-
# @example Mount an application
|
71
|
-
# require 'hanami/router'
|
72
|
-
#
|
73
|
-
# router = Hanami::Router.new do
|
74
|
-
# mount Api::App, at: '/api'
|
75
|
-
# end
|
76
|
-
#
|
77
|
-
# # All the requests starting with "/api" will be forwarded to Api::App
|
78
|
-
#
|
79
9
|
class Router # rubocop:disable Metrics/ClassLength
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
#
|
91
|
-
# @see Hanami::Routing::RecognizedRoute#call
|
92
|
-
# @see Hanami::Routing::RecognizedRoute#routable?
|
93
|
-
class NotRoutableEndpointError < Hanami::Routing::Error
|
94
|
-
# @since 0.5.0
|
95
|
-
# @api private
|
96
|
-
REQUEST_METHOD = "REQUEST_METHOD"
|
97
|
-
|
98
|
-
# @since 0.5.0
|
99
|
-
# @api private
|
100
|
-
PATH_INFO = "PATH_INFO"
|
101
|
-
|
102
|
-
# @since 0.5.0
|
103
|
-
def initialize(env)
|
104
|
-
super %(Cannot find routable endpoint for #{env[REQUEST_METHOD]} "#{env[PATH_INFO]}")
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
# Defines root path
|
10
|
+
require "hanami/router/version"
|
11
|
+
require "hanami/router/error"
|
12
|
+
require "hanami/router/segment"
|
13
|
+
require "hanami/router/redirect"
|
14
|
+
require "hanami/router/prefix"
|
15
|
+
require "hanami/router/params"
|
16
|
+
require "hanami/router/trie"
|
17
|
+
require "hanami/router/block"
|
18
|
+
require "hanami/router/url_helpers"
|
19
|
+
|
20
|
+
# URL helpers for other Hanami integrations
|
109
21
|
#
|
110
|
-
# @since 0.7.0
|
111
22
|
# @api private
|
112
|
-
#
|
113
|
-
|
114
|
-
ROOT_PATH = "/"
|
23
|
+
# @since 2.0.0
|
24
|
+
attr_reader :url_helpers
|
115
25
|
|
116
26
|
# Returns the given block as it is.
|
117
27
|
#
|
118
|
-
# When Hanami::Router is used as a standalone gem and the routes are defined
|
119
|
-
# into a configuration file, some systems could raise an exception.
|
120
|
-
#
|
121
|
-
# Imagine the following file into a Ruby on Rails application:
|
122
|
-
#
|
123
|
-
# get '/', to: 'api#index'
|
124
|
-
#
|
125
|
-
# Because Ruby on Rails in production mode use to eager load code and the
|
126
|
-
# routes file uses top level method calls, it crashes the application.
|
127
|
-
#
|
128
|
-
# If we wrap these routes with <tt>Hanami::Router.define</tt>, the block
|
129
|
-
# doesn't get yielded but just returned to the caller as it is.
|
130
|
-
#
|
131
|
-
# Usually the receiver of this block is <tt>Hanami::Router#initialize</tt>,
|
132
|
-
# which finally evaluates the block.
|
133
|
-
#
|
134
28
|
# @param blk [Proc] a set of route definitions
|
135
29
|
#
|
136
30
|
# @return [Proc] the given block
|
@@ -140,913 +34,438 @@ module Hanami
|
|
140
34
|
# @example
|
141
35
|
# # apps/web/config/routes.rb
|
142
36
|
# Hanami::Router.define do
|
143
|
-
# get
|
37
|
+
# get "/", to: ->(*) { ... }
|
144
38
|
# end
|
145
39
|
def self.define(&blk)
|
146
40
|
blk
|
147
41
|
end
|
148
42
|
|
149
|
-
# Initialize the router
|
43
|
+
# Initialize the router
|
150
44
|
#
|
151
|
-
# @param
|
152
|
-
#
|
153
|
-
# @
|
154
|
-
#
|
155
|
-
# @
|
156
|
-
# @
|
157
|
-
#
|
158
|
-
# @option options [Object, #generate] :route the route class
|
159
|
-
# (defaults to `Hanami::Routing::Route`)
|
160
|
-
# @option options [String] :action_separator the separator between controller
|
161
|
-
# and action name (eg. 'dashboard#show', where '#' is the :action_separator)
|
162
|
-
# @option options [Object, #pluralize, #singularize] :inflector
|
163
|
-
# the inflector class (defaults to `Dry::Inflector.new`)
|
164
|
-
#
|
165
|
-
# @param blk [Proc] the optional block to define the routes
|
166
|
-
#
|
167
|
-
# @return [Hanami::Router] self
|
45
|
+
# @param base_url [String] the base URL where the HTTP application is
|
46
|
+
# deployed
|
47
|
+
# @param prefix [String] the relative URL prefix where the HTTP application
|
48
|
+
# is deployed
|
49
|
+
# @param resolver [#call(path, to)] a resolver for route entpoints
|
50
|
+
# @param block_context [Hanami::Router::Block::Context)
|
51
|
+
# @param blk [Proc] the route definitions
|
168
52
|
#
|
169
53
|
# @since 0.1.0
|
170
54
|
#
|
171
|
-
# @
|
172
|
-
# require 'hanami/router'
|
173
|
-
#
|
174
|
-
# endpoint = ->(env) { [200, {}, ['Welcome to Hanami::Router!']] }
|
175
|
-
#
|
176
|
-
# router = Hanami::Router.new
|
177
|
-
# router.get '/', to: endpoint
|
178
|
-
#
|
179
|
-
# # or
|
180
|
-
#
|
181
|
-
# router = Hanami::Router.new do
|
182
|
-
# get '/', to: endpoint
|
183
|
-
# end
|
184
|
-
#
|
185
|
-
# @example Body parsers
|
186
|
-
#
|
187
|
-
# require 'hanami/router'
|
188
|
-
# require 'hanami/middleware/body_parser'
|
189
|
-
#
|
190
|
-
# app = Hanami::Router.new do
|
191
|
-
# patch '/books/:id', to: ->(env) { [200, {},[env['router.params'].inspect]] }
|
192
|
-
# end
|
193
|
-
#
|
194
|
-
# use Hanami::Middleware::BodyParser, :json
|
195
|
-
# run app
|
196
|
-
#
|
197
|
-
# # From the shell
|
198
|
-
#
|
199
|
-
# curl http://localhost:2300/books/1 \
|
200
|
-
# -H "Content-Type: application/json" \
|
201
|
-
# -H "Accept: application/json" \
|
202
|
-
# -d '{"published":"true"}' \
|
203
|
-
# -X PATCH
|
204
|
-
#
|
205
|
-
# # It returns
|
206
|
-
#
|
207
|
-
# [200, {}, ["{:published=>\"true\",:id=>\"1\"}"]]
|
208
|
-
#
|
209
|
-
# @example Custom body parser
|
210
|
-
#
|
211
|
-
# require 'hanami/router'
|
212
|
-
# require 'hanami/middleware/body_parser'
|
55
|
+
# @return [Hanami::Router]
|
213
56
|
#
|
57
|
+
# @example Base usage
|
58
|
+
# require "hanami/router"
|
214
59
|
#
|
215
|
-
#
|
216
|
-
#
|
217
|
-
# ['application/xml', 'text/xml']
|
218
|
-
# end
|
219
|
-
#
|
220
|
-
# # Parse body and return a Hash
|
221
|
-
# def parse(body)
|
222
|
-
# # parse xml
|
223
|
-
# end
|
224
|
-
# end
|
225
|
-
#
|
226
|
-
# app = Hanami::Router.new do
|
227
|
-
# patch '/authors/:id', to: ->(env) { [200, {},[env['router.params'].inspect]] }
|
60
|
+
# Hanami::Router.new do
|
61
|
+
# get "/", to: ->(*) { [200, {}, ["OK"]] }
|
228
62
|
# end
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
# # It returns
|
242
|
-
#
|
243
|
-
# [200, {}, ["{:name=>\"LG\",:id=>\"1\"}"]]
|
244
|
-
#
|
245
|
-
# rubocop:disable Metrics/MethodLength
|
246
|
-
def initialize(scheme: "http", host: "localhost", port: 80, prefix: "", namespace: nil, configuration: nil, inflector: Dry::Inflector.new, not_found: NOT_FOUND, not_allowed: NOT_ALLOWED, &blk)
|
247
|
-
@routes = []
|
248
|
-
@named = {}
|
249
|
-
@namespace = namespace
|
250
|
-
@configuration = configuration
|
251
|
-
@base = Routing::Uri.build(scheme: scheme, host: host, port: port)
|
252
|
-
@prefix = Utils::PathPrefix.new(prefix)
|
253
|
-
@inflector = inflector
|
254
|
-
@not_found = not_found
|
255
|
-
@not_allowed = not_allowed
|
256
|
-
instance_eval(&blk) unless blk.nil?
|
257
|
-
freeze
|
258
|
-
end
|
259
|
-
# rubocop:enable Metrics/MethodLength
|
260
|
-
|
261
|
-
# Freeze the router
|
262
|
-
#
|
263
|
-
# @since 2.0.0
|
264
|
-
def freeze
|
265
|
-
@routes.freeze
|
266
|
-
super
|
63
|
+
def initialize(base_url: DEFAULT_BASE_URL, prefix: DEFAULT_PREFIX, resolver: DEFAULT_RESOLVER, block_context: nil, &blk)
|
64
|
+
# TODO: verify if Prefix can handle both name and path prefix
|
65
|
+
@path_prefix = Prefix.new(prefix)
|
66
|
+
@name_prefix = Prefix.new("")
|
67
|
+
@url_helpers = UrlHelpers.new(base_url)
|
68
|
+
@resolver = resolver
|
69
|
+
@block_context = block_context
|
70
|
+
@fixed = {}
|
71
|
+
@variable = {}
|
72
|
+
@globbed = {}
|
73
|
+
@mounted = {}
|
74
|
+
instance_eval(&blk)
|
267
75
|
end
|
268
76
|
|
269
|
-
#
|
77
|
+
# Resolve the given Rack env to a registered endpoint and invokes it.
|
270
78
|
#
|
271
|
-
#
|
272
|
-
# It's used by `Hanami::Routing::RoutesInspector` to inspect both apps and
|
273
|
-
# routers.
|
79
|
+
# @param env [Hash] a Rack env
|
274
80
|
#
|
275
|
-
# @return [
|
81
|
+
# @return [Array] a finalized Rack env response
|
276
82
|
#
|
277
|
-
# @since 0.
|
278
|
-
|
279
|
-
|
280
|
-
self
|
281
|
-
end
|
83
|
+
# @since 0.1.0
|
84
|
+
def call(env)
|
85
|
+
endpoint, params = lookup(env)
|
282
86
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
#
|
292
|
-
# router = Hanami::Router.new
|
293
|
-
# router.defined? # => false
|
294
|
-
#
|
295
|
-
# router = Hanami::Router.new { get '/', to: ->(env) { } }
|
296
|
-
# router.defined? # => true
|
297
|
-
def defined?
|
298
|
-
@routes.any?
|
87
|
+
unless endpoint
|
88
|
+
return not_allowed(env) ||
|
89
|
+
not_found
|
90
|
+
end
|
91
|
+
|
92
|
+
endpoint.call(
|
93
|
+
_params(env, params)
|
94
|
+
).to_a
|
299
95
|
end
|
300
96
|
|
301
|
-
# Defines a
|
302
|
-
#
|
303
|
-
# @param path [String] the relative URL to be matched
|
304
|
-
#
|
305
|
-
# @param options [Hash] the options to customize the route
|
306
|
-
# @option options [String,Proc,Class,Object#call] :to the endpoint
|
97
|
+
# Defines a named root route (a GET route for "/")
|
307
98
|
#
|
99
|
+
# @param to [#call] the Rack endpoint
|
308
100
|
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
309
101
|
#
|
310
|
-
# @
|
311
|
-
# option passed to the constructor
|
312
|
-
#
|
313
|
-
# @since 0.1.0
|
314
|
-
#
|
315
|
-
# @example Fixed matching string
|
316
|
-
# require 'hanami/router'
|
317
|
-
#
|
318
|
-
# router = Hanami::Router.new
|
319
|
-
# router.get '/hanami', to: ->(env) { [200, {}, ['Hello from Hanami!']] }
|
320
|
-
#
|
321
|
-
# @example String matching with variables
|
322
|
-
# require 'hanami/router'
|
323
|
-
#
|
324
|
-
# router = Hanami::Router.new
|
325
|
-
# router.get '/flowers/:id',
|
326
|
-
# to: ->(env) {
|
327
|
-
# [
|
328
|
-
# 200,
|
329
|
-
# {},
|
330
|
-
# ["Hello from Flower no. #{ env['router.params'][:id] }!"]
|
331
|
-
# ]
|
332
|
-
# }
|
333
|
-
#
|
334
|
-
# @example Variables Constraints
|
335
|
-
# require 'hanami/router'
|
336
|
-
#
|
337
|
-
# router = Hanami::Router.new
|
338
|
-
# router.get '/flowers/:id',
|
339
|
-
# id: /\d+/,
|
340
|
-
# to: ->(env) { [200, {}, [":id must be a number!"]] }
|
341
|
-
#
|
342
|
-
# @example String matching with globbling
|
343
|
-
# require 'hanami/router'
|
344
|
-
#
|
345
|
-
# router = Hanami::Router.new
|
346
|
-
# router.get '/*',
|
347
|
-
# to: ->(env) {
|
348
|
-
# [
|
349
|
-
# 200,
|
350
|
-
# {},
|
351
|
-
# ["This is catch all: #{ env['router.params'].inspect }!"]
|
352
|
-
# ]
|
353
|
-
# }
|
354
|
-
#
|
355
|
-
# @example String matching with optional tokens
|
356
|
-
# require 'hanami/router'
|
357
|
-
#
|
358
|
-
# router = Hanami::Router.new
|
359
|
-
# router.get '/hanami(.:format)',
|
360
|
-
# to: ->(env) {
|
361
|
-
# [200, {}, ["You've requested #{ env['router.params'][:format] }!"]]
|
362
|
-
# }
|
363
|
-
#
|
364
|
-
# @example Named routes
|
365
|
-
# require 'hanami/router'
|
366
|
-
#
|
367
|
-
# router = Hanami::Router.new(scheme: 'https', host: 'hanamirb.org')
|
368
|
-
# router.get '/hanami',
|
369
|
-
# to: ->(env) { [200, {}, ['Hello from Hanami!']] },
|
370
|
-
# as: :hanami
|
371
|
-
#
|
372
|
-
# router.path(:hanami) # => "/hanami"
|
373
|
-
# router.url(:hanami) # => "https://hanamirb.org/hanami"
|
374
|
-
#
|
375
|
-
# @example Duck typed endpoints (Rack compatible objects)
|
376
|
-
# require 'hanami/router'
|
102
|
+
# @since 0.7.0
|
377
103
|
#
|
378
|
-
#
|
104
|
+
# @see #get
|
105
|
+
# @see #path
|
106
|
+
# @see #url
|
379
107
|
#
|
380
|
-
#
|
381
|
-
#
|
382
|
-
# router.get '/rack-app', to: RackApp.new
|
383
|
-
# router.get '/method', to: ActionControllerSubclass.action(:new)
|
108
|
+
# @example Proc endpoint
|
109
|
+
# require "hanami/router"
|
384
110
|
#
|
385
|
-
#
|
111
|
+
# router = Hanami::Router.new do
|
112
|
+
# root to: ->(env) { [200, {}, ["Hello from Hanami!"]] }
|
113
|
+
# end
|
386
114
|
#
|
387
|
-
# @example
|
388
|
-
# require
|
115
|
+
# @example Block endpoint
|
116
|
+
# require "hanami/router"
|
389
117
|
#
|
390
|
-
#
|
391
|
-
#
|
392
|
-
#
|
118
|
+
# router = Hanami::Router.new do
|
119
|
+
# root do
|
120
|
+
# "Hello from Hanami!"
|
393
121
|
# end
|
394
122
|
# end
|
395
123
|
#
|
396
|
-
#
|
397
|
-
#
|
398
|
-
#
|
399
|
-
# @example Duck typed endpoints (string: controller + action)
|
400
|
-
# require 'hanami/router'
|
124
|
+
# @example URL helpers
|
125
|
+
# require "hanami/router"
|
401
126
|
#
|
402
|
-
#
|
403
|
-
#
|
404
|
-
#
|
405
|
-
# # ...
|
406
|
-
# end
|
127
|
+
# router = Hanami::Router.new(base_url: "https://hanamirb.org") do
|
128
|
+
# root do
|
129
|
+
# "Hello from Hanami!"
|
407
130
|
# end
|
408
131
|
# end
|
409
132
|
#
|
410
|
-
#
|
411
|
-
#
|
412
|
-
|
413
|
-
|
414
|
-
# # Hanami::Controller convention.
|
415
|
-
def get(path, to: nil, as: nil, namespace: nil, configuration: nil, **constraints, &blk)
|
416
|
-
add_route(GET, path, to, as, namespace, configuration, constraints, &blk)
|
133
|
+
# router.path(:root) # => "/"
|
134
|
+
# router.url(:root) # => "https://hanamirb.org"
|
135
|
+
def root(to: nil, &blk)
|
136
|
+
get("/", to: to, as: :root, &blk)
|
417
137
|
end
|
418
138
|
|
419
|
-
# Defines a route that accepts
|
139
|
+
# Defines a route that accepts GET requests for the given path.
|
140
|
+
# It also defines a route to accept HEAD requests.
|
420
141
|
#
|
421
142
|
# @param path [String] the relative URL to be matched
|
143
|
+
# @param to [#call] the Rack endpoint
|
144
|
+
# @param as [Symbol] a unique name for the route
|
145
|
+
# @param constraints [Hash] a set of constraints for path variables
|
146
|
+
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
422
147
|
#
|
423
|
-
# @
|
424
|
-
# @option options [String,Proc,Class,Object#call] :to the endpoint
|
148
|
+
# @since 0.1.0
|
425
149
|
#
|
426
|
-
# @
|
150
|
+
# @see #initialize
|
151
|
+
# @see #path
|
152
|
+
# @see #url
|
427
153
|
#
|
428
|
-
# @
|
429
|
-
#
|
154
|
+
# @example Proc endpoint
|
155
|
+
# require "hanami/router"
|
430
156
|
#
|
431
|
-
#
|
157
|
+
# Hanami::Router.new do
|
158
|
+
# get "/", to: ->(*) { [200, {}, ["OK"]] }
|
159
|
+
# end
|
432
160
|
#
|
433
|
-
# @
|
434
|
-
|
435
|
-
add_route(POST, path, to, as, namespace, configuration, constraints, &blk)
|
436
|
-
end
|
437
|
-
|
438
|
-
# Defines a route that accepts a PUT request for the given path.
|
161
|
+
# @example Block endpoint
|
162
|
+
# require "hanami/router"
|
439
163
|
#
|
440
|
-
#
|
164
|
+
# Hanami::Router.new do
|
165
|
+
# get "/" do
|
166
|
+
# "OK"
|
167
|
+
# end
|
168
|
+
# end
|
441
169
|
#
|
442
|
-
# @
|
443
|
-
#
|
170
|
+
# @example Named route
|
171
|
+
# require "hanami/router"
|
444
172
|
#
|
445
|
-
#
|
173
|
+
# router = Hanami::Router.new do
|
174
|
+
# get "/", to: ->(*) { [200, {}, ["OK"]] }, as: :welcome
|
175
|
+
# end
|
446
176
|
#
|
447
|
-
#
|
448
|
-
#
|
177
|
+
# router.path(:welcome) # => "/"
|
178
|
+
# router.url(:welcome) # => "http://localhost/"
|
449
179
|
#
|
450
|
-
# @
|
180
|
+
# @example Constraints
|
181
|
+
# require "hanami/router"
|
451
182
|
#
|
452
|
-
#
|
453
|
-
|
454
|
-
|
183
|
+
# Hanami::Router.new do
|
184
|
+
# get "/users/:id", to: ->(*) { [200, {}, ["OK"]] }, id: /\d+/
|
185
|
+
# end
|
186
|
+
def get(path, to: nil, as: nil, **constraints, &blk)
|
187
|
+
add_route("GET", path, to, as, constraints, &blk)
|
188
|
+
add_route("HEAD", path, to, as, constraints, &blk)
|
455
189
|
end
|
456
190
|
|
457
|
-
# Defines a route that accepts
|
191
|
+
# Defines a route that accepts POST requests for the given path.
|
458
192
|
#
|
459
193
|
# @param path [String] the relative URL to be matched
|
460
|
-
#
|
461
|
-
# @param
|
462
|
-
# @
|
463
|
-
#
|
194
|
+
# @param to [#call] the Rack endpoint
|
195
|
+
# @param as [Symbol] a unique name for the route
|
196
|
+
# @param constraints [Hash] a set of constraints for path variables
|
464
197
|
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
465
198
|
#
|
466
|
-
# @return [Hanami::Routing::Route] this may vary according to the :route
|
467
|
-
# option passed to the constructor
|
468
|
-
#
|
469
|
-
# @see Hanami::Router#get
|
470
|
-
#
|
471
199
|
# @since 0.1.0
|
472
|
-
|
473
|
-
|
200
|
+
#
|
201
|
+
# @see #get
|
202
|
+
# @see #initialize
|
203
|
+
# @see #path
|
204
|
+
# @see #url
|
205
|
+
def post(path, to: nil, as: nil, **constraints, &blk)
|
206
|
+
add_route("POST", path, to, as, constraints, &blk)
|
474
207
|
end
|
475
208
|
|
476
|
-
# Defines a route that accepts
|
209
|
+
# Defines a route that accepts PATCH requests for the given path.
|
477
210
|
#
|
478
211
|
# @param path [String] the relative URL to be matched
|
479
|
-
#
|
480
|
-
# @param
|
481
|
-
# @
|
482
|
-
#
|
212
|
+
# @param to [#call] the Rack endpoint
|
213
|
+
# @param as [Symbol] a unique name for the route
|
214
|
+
# @param constraints [Hash] a set of constraints for path variables
|
483
215
|
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
484
216
|
#
|
485
|
-
# @return [Hanami::Routing::Route] this may vary according to the :route
|
486
|
-
# option passed to the constructor
|
487
|
-
#
|
488
|
-
# @see Hanami::Router#get
|
489
|
-
#
|
490
217
|
# @since 0.1.0
|
491
|
-
|
492
|
-
|
218
|
+
#
|
219
|
+
# @see #get
|
220
|
+
# @see #initialize
|
221
|
+
# @see #path
|
222
|
+
# @see #url
|
223
|
+
def patch(path, to: nil, as: nil, **constraints, &blk)
|
224
|
+
add_route("PATCH", path, to, as, constraints, &blk)
|
493
225
|
end
|
494
226
|
|
495
|
-
# Defines a route that accepts
|
227
|
+
# Defines a route that accepts PUT requests for the given path.
|
496
228
|
#
|
497
229
|
# @param path [String] the relative URL to be matched
|
498
|
-
#
|
499
|
-
# @param
|
500
|
-
# @
|
501
|
-
#
|
230
|
+
# @param to [#call] the Rack endpoint
|
231
|
+
# @param as [Symbol] a unique name for the route
|
232
|
+
# @param constraints [Hash] a set of constraints for path variables
|
502
233
|
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
503
234
|
#
|
504
|
-
# @return [Hanami::Routing::Route] this may vary according to the :route
|
505
|
-
# option passed to the constructor
|
506
|
-
#
|
507
|
-
# @see Hanami::Router#get
|
508
|
-
#
|
509
235
|
# @since 0.1.0
|
510
|
-
|
511
|
-
|
236
|
+
#
|
237
|
+
# @see #get
|
238
|
+
# @see #initialize
|
239
|
+
# @see #path
|
240
|
+
# @see #url
|
241
|
+
def put(path, to: nil, as: nil, **constraints, &blk)
|
242
|
+
add_route("PUT", path, to, as, constraints, &blk)
|
512
243
|
end
|
513
244
|
|
514
|
-
# Defines a route that accepts
|
245
|
+
# Defines a route that accepts DELETE requests for the given path.
|
515
246
|
#
|
516
247
|
# @param path [String] the relative URL to be matched
|
517
|
-
#
|
518
|
-
# @param
|
519
|
-
# @
|
520
|
-
#
|
248
|
+
# @param to [#call] the Rack endpoint
|
249
|
+
# @param as [Symbol] a unique name for the route
|
250
|
+
# @param constraints [Hash] a set of constraints for path variables
|
521
251
|
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
522
252
|
#
|
523
|
-
# @
|
524
|
-
# option passed to the constructor
|
525
|
-
#
|
526
|
-
# @see Hanami::Router#get
|
253
|
+
# @since 0.1.0
|
527
254
|
#
|
528
|
-
# @
|
529
|
-
|
530
|
-
|
255
|
+
# @see #get
|
256
|
+
# @see #initialize
|
257
|
+
# @see #path
|
258
|
+
# @see #url
|
259
|
+
def delete(path, to: nil, as: nil, **constraints, &blk)
|
260
|
+
add_route("DELETE", path, to, as, constraints, &blk)
|
531
261
|
end
|
532
262
|
|
533
|
-
# Defines a route that accepts
|
263
|
+
# Defines a route that accepts TRACE requests for the given path.
|
534
264
|
#
|
535
265
|
# @param path [String] the relative URL to be matched
|
536
|
-
#
|
537
|
-
# @param
|
538
|
-
# @
|
539
|
-
#
|
266
|
+
# @param to [#call] the Rack endpoint
|
267
|
+
# @param as [Symbol] a unique name for the route
|
268
|
+
# @param constraints [Hash] a set of constraints for path variables
|
540
269
|
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
541
270
|
#
|
542
|
-
# @
|
543
|
-
# option passed to the constructor
|
544
|
-
#
|
545
|
-
# @see Hanami::Router#get
|
271
|
+
# @since 0.1.0
|
546
272
|
#
|
547
|
-
# @
|
548
|
-
|
549
|
-
|
273
|
+
# @see #get
|
274
|
+
# @see #initialize
|
275
|
+
# @see #path
|
276
|
+
# @see #url
|
277
|
+
def trace(path, to: nil, as: nil, **constraints, &blk)
|
278
|
+
add_route("TRACE", path, to, as, constraints, &blk)
|
550
279
|
end
|
551
280
|
|
552
|
-
# Defines a route that accepts
|
281
|
+
# Defines a route that accepts OPTIONS requests for the given path.
|
553
282
|
#
|
554
283
|
# @param path [String] the relative URL to be matched
|
555
|
-
#
|
556
|
-
# @param
|
557
|
-
# @
|
558
|
-
#
|
284
|
+
# @param to [#call] the Rack endpoint
|
285
|
+
# @param as [Symbol] a unique name for the route
|
286
|
+
# @param constraints [Hash] a set of constraints for path variables
|
559
287
|
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
560
288
|
#
|
561
|
-
# @return [Hanami::Routing::Route] this may vary according to the :route
|
562
|
-
# option passed to the constructor
|
563
|
-
#
|
564
|
-
# @see Hanami::Router#get
|
565
|
-
#
|
566
289
|
# @since 0.1.0
|
567
|
-
|
568
|
-
|
290
|
+
#
|
291
|
+
# @see #get
|
292
|
+
# @see #initialize
|
293
|
+
# @see #path
|
294
|
+
# @see #url
|
295
|
+
def options(path, to: nil, as: nil, **constraints, &blk)
|
296
|
+
add_route("OPTIONS", path, to, as, constraints, &blk)
|
569
297
|
end
|
570
298
|
|
571
|
-
# Defines a
|
572
|
-
#
|
573
|
-
# @param options [Hash] the options to customize the route
|
574
|
-
# @option options [String,Proc,Class,Object#call] :to the endpoint
|
299
|
+
# Defines a route that accepts LINK requests for the given path.
|
575
300
|
#
|
301
|
+
# @param path [String] the relative URL to be matched
|
302
|
+
# @param to [#call] the Rack endpoint
|
303
|
+
# @param as [Symbol] a unique name for the route
|
304
|
+
# @param constraints [Hash] a set of constraints for path variables
|
576
305
|
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
577
306
|
#
|
578
|
-
# @
|
579
|
-
# option passed to the constructor
|
580
|
-
#
|
581
|
-
# @since 0.7.0
|
582
|
-
#
|
583
|
-
# @example Fixed matching string
|
584
|
-
# require 'hanami/router'
|
585
|
-
#
|
586
|
-
# router = Hanami::Router.new
|
587
|
-
# router.root to: ->(env) { [200, {}, ['Hello from Hanami!']] }
|
588
|
-
#
|
589
|
-
# @example Included names as `root` (for path and url helpers)
|
590
|
-
# require 'hanami/router'
|
591
|
-
#
|
592
|
-
# router = Hanami::Router.new(scheme: 'https', host: 'hanamirb.org')
|
593
|
-
# router.root to: ->(env) { [200, {}, ['Hello from Hanami!']] }
|
307
|
+
# @since 0.1.0
|
594
308
|
#
|
595
|
-
#
|
596
|
-
#
|
597
|
-
|
598
|
-
|
309
|
+
# @see #get
|
310
|
+
# @see #initialize
|
311
|
+
# @see #path
|
312
|
+
# @see #url
|
313
|
+
def link(path, to: nil, as: nil, **constraints, &blk)
|
314
|
+
add_route("LINK", path, to, as, constraints, &blk)
|
599
315
|
end
|
600
316
|
|
601
|
-
# Defines
|
317
|
+
# Defines a route that accepts UNLINK requests for the given path.
|
602
318
|
#
|
603
|
-
# @param path [String] the
|
604
|
-
# @param
|
605
|
-
# @
|
606
|
-
#
|
607
|
-
# @
|
608
|
-
# This may vary according to the `:route` option passed to the initializer
|
319
|
+
# @param path [String] the relative URL to be matched
|
320
|
+
# @param to [#call] the Rack endpoint
|
321
|
+
# @param as [Symbol] a unique name for the route
|
322
|
+
# @param constraints [Hash] a set of constraints for path variables
|
323
|
+
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
609
324
|
#
|
610
325
|
# @since 0.1.0
|
611
326
|
#
|
612
|
-
# @see
|
613
|
-
#
|
614
|
-
# @
|
615
|
-
#
|
616
|
-
|
617
|
-
|
618
|
-
# redirect '/legacy', to: '/new_endpoint'
|
619
|
-
# redirect '/legacy2', to: '/new_endpoint2', code: 302
|
620
|
-
# end
|
621
|
-
#
|
622
|
-
# @example
|
623
|
-
# require 'hanami/router'
|
624
|
-
#
|
625
|
-
# router = Hanami::Router.new
|
626
|
-
# router.redirect '/legacy', to: '/new_endpoint'
|
627
|
-
def redirect(path, to:, code: 301)
|
628
|
-
to = Routing::Redirect.new(@prefix.join(to).to_s, code)
|
629
|
-
add_route(GET, path, to)
|
327
|
+
# @see #get
|
328
|
+
# @see #initialize
|
329
|
+
# @see #path
|
330
|
+
# @see #url
|
331
|
+
def unlink(path, to: nil, as: nil, **constraints, &blk)
|
332
|
+
add_route("UNLINK", path, to, as, constraints, &blk)
|
630
333
|
end
|
631
334
|
|
632
|
-
# Defines a
|
633
|
-
# with the given relative path.
|
335
|
+
# Defines a route that redirects the incoming request to another path.
|
634
336
|
#
|
635
|
-
#
|
636
|
-
#
|
637
|
-
# @param
|
638
|
-
#
|
639
|
-
# @param blk [Proc] the block that defines the resources
|
640
|
-
#
|
641
|
-
# @return [void]
|
642
|
-
#
|
643
|
-
# @since 2.0.0
|
644
|
-
#
|
645
|
-
# @see Hanami::Router
|
646
|
-
#
|
647
|
-
# @example Basic example
|
648
|
-
# require "hanami/router"
|
649
|
-
#
|
650
|
-
# Hanami::Router.new do
|
651
|
-
# prefix "trees" do
|
652
|
-
# get "/sequoia", to: endpoint # => "/trees/sequoia"
|
653
|
-
# end
|
654
|
-
# end
|
337
|
+
# @param path [String] the relative URL to be matched
|
338
|
+
# @param to [#call] the Rack endpoint
|
339
|
+
# @param as [Symbol] a unique name for the route
|
340
|
+
# @param code [Integer] a HTTP status code to use for the redirect
|
655
341
|
#
|
656
|
-
# @
|
657
|
-
# require "hanami/router"
|
342
|
+
# @since 0.1.0
|
658
343
|
#
|
659
|
-
#
|
660
|
-
#
|
661
|
-
|
662
|
-
|
663
|
-
# end
|
664
|
-
# end
|
665
|
-
# end
|
666
|
-
def prefix(path, namespace: nil, configuration: nil, &blk)
|
667
|
-
Routing::Prefix.new(self, path, namespace, configuration, &blk)
|
344
|
+
# @see #get
|
345
|
+
# @see #initialize
|
346
|
+
def redirect(path, to: nil, as: nil, code: DEFAULT_REDIRECT_CODE)
|
347
|
+
get(path, to: _redirect(to, code), as: as)
|
668
348
|
end
|
669
349
|
|
670
|
-
# Defines a scope
|
671
|
-
#
|
672
|
-
# A scope is a combination of a path prefix and a Ruby namespace.
|
350
|
+
# Defines a routing scope. Routes defined in the context of a scope,
|
351
|
+
# inherit the given path as path prefix and as a named routes prefix.
|
673
352
|
#
|
674
|
-
# @param
|
675
|
-
# @param
|
676
|
-
# @param configuration [Hanami::Controller::Configuration] the action
|
677
|
-
# configuration
|
678
|
-
# @param blk [Proc] the routes definition block
|
353
|
+
# @param path [String] the scope path to be used as a path prefix
|
354
|
+
# @param blk [Proc] the routes definitions withing the scope
|
679
355
|
#
|
680
356
|
# @since 2.0.0
|
681
|
-
#
|
357
|
+
#
|
358
|
+
# @see #path
|
682
359
|
#
|
683
360
|
# @example
|
684
361
|
# require "hanami/router"
|
685
|
-
# require "hanami/controller"
|
686
|
-
#
|
687
|
-
# configuration = Hanami::Controller::Configuration.new
|
688
|
-
#
|
689
|
-
# Hanami::Router.new do
|
690
|
-
# scope "/admin", namespace: Admin::Controllers, configuration: configuration do
|
691
|
-
# root to: "home#index"
|
692
|
-
# end
|
693
|
-
# end
|
694
|
-
def scope(prefix, namespace:, configuration:, &blk)
|
695
|
-
Routing::Scope.new(self, prefix, namespace, configuration, &blk)
|
696
|
-
end
|
697
|
-
|
698
|
-
# Defines a set of named routes for a single RESTful resource.
|
699
|
-
# It has a built-in integration for Hanami::Controller.
|
700
|
-
#
|
701
|
-
# @param name [String] the name of the resource
|
702
|
-
# @param options [Hash] a set of options to customize the routes
|
703
|
-
# @option options [Array<Symbol>] :only a subset of the default routes
|
704
|
-
# that we want to generate
|
705
|
-
# @option options [Array<Symbol>] :except prevent the given routes to be
|
706
|
-
# generated
|
707
|
-
# @param blk [Proc] a block of code to generate additional routes
|
708
|
-
#
|
709
|
-
# @return [Hanami::Routing::Resource]
|
710
|
-
#
|
711
|
-
# @since 0.1.0
|
712
|
-
#
|
713
|
-
# @see Hanami::Routing::Resource
|
714
|
-
# @see Hanami::Routing::Resource::Action
|
715
|
-
# @see Hanami::Routing::Resource::Options
|
716
|
-
#
|
717
|
-
# @example Default usage
|
718
|
-
# require 'hanami/router'
|
719
|
-
#
|
720
|
-
# Hanami::Router.new do
|
721
|
-
# resource 'identity'
|
722
|
-
# end
|
723
|
-
#
|
724
|
-
# # It generates:
|
725
|
-
# #
|
726
|
-
# # +--------+----------------+-------------------+----------+----------------+
|
727
|
-
# # | Verb | Path | Action | Name | Named Route |
|
728
|
-
# # +--------+----------------+-------------------+----------+----------------+
|
729
|
-
# # | GET | /identity | Identity::Show | :show | :identity |
|
730
|
-
# # | GET | /identity/new | Identity::New | :new | :new_identity |
|
731
|
-
# # | POST | /identity | Identity::Create | :create | :identity |
|
732
|
-
# # | GET | /identity/edit | Identity::Edit | :edit | :edit_identity |
|
733
|
-
# # | PATCH | /identity | Identity::Update | :update | :identity |
|
734
|
-
# # | DELETE | /identity | Identity::Destroy | :destroy | :identity |
|
735
|
-
# # +--------+----------------+-------------------+----------+----------------+
|
736
|
-
#
|
737
|
-
#
|
738
|
-
#
|
739
|
-
# @example Limit the generated routes with :only
|
740
|
-
# require 'hanami/router'
|
741
|
-
#
|
742
|
-
# Hanami::Router.new do
|
743
|
-
# resource 'identity', only: [:show, :new, :create]
|
744
|
-
# end
|
745
|
-
#
|
746
|
-
# # It generates:
|
747
|
-
# #
|
748
|
-
# # +--------+----------------+------------------+----------+----------------+
|
749
|
-
# # | Verb | Path | Action | Name | Named Route |
|
750
|
-
# # +--------+----------------+------------------+----------+----------------+
|
751
|
-
# # | GET | /identity | Identity::Show | :show | :identity |
|
752
|
-
# # | GET | /identity/new | Identity::New | :new | :new_identity |
|
753
|
-
# # | POST | /identity | Identity::Create | :create | :identity |
|
754
|
-
# # +--------+----------------+------------------+----------+----------------+
|
755
|
-
#
|
756
|
-
#
|
757
|
-
#
|
758
|
-
# @example Limit the generated routes with :except
|
759
|
-
# require 'hanami/router'
|
760
|
-
#
|
761
|
-
# Hanami::Router.new do
|
762
|
-
# resource 'identity', except: [:edit, :update, :destroy]
|
763
|
-
# end
|
764
|
-
#
|
765
|
-
# # It generates:
|
766
|
-
# #
|
767
|
-
# # +--------+----------------+------------------+----------+----------------+
|
768
|
-
# # | Verb | Path | Action | Name | Named Route |
|
769
|
-
# # +--------+----------------+------------------+----------+----------------+
|
770
|
-
# # | GET | /identity | Identity::Show | :show | :identity |
|
771
|
-
# # | GET | /identity/new | Identity::New | :new | :new_identity |
|
772
|
-
# # | POST | /identity | Identity::Create | :create | :identity |
|
773
|
-
# # +--------+----------------+------------------+----------+----------------+
|
774
|
-
#
|
775
|
-
#
|
776
|
-
#
|
777
|
-
# @example Additional single routes
|
778
|
-
# require 'hanami/router'
|
779
|
-
#
|
780
|
-
# Hanami::Router.new do
|
781
|
-
# resource 'identity', only: [] do
|
782
|
-
# member do
|
783
|
-
# patch 'activate'
|
784
|
-
# end
|
785
|
-
# end
|
786
|
-
# end
|
787
|
-
#
|
788
|
-
# # It generates:
|
789
|
-
# #
|
790
|
-
# # +--------+--------------------+--------------------+------+--------------------+
|
791
|
-
# # | Verb | Path | Action | Name | Named Route |
|
792
|
-
# # +--------+--------------------+--------------------+------+--------------------+
|
793
|
-
# # | PATCH | /identity/activate | Identity::Activate | | :activate_identity |
|
794
|
-
# # +--------+--------------------+--------------------+------+--------------------+
|
795
|
-
#
|
796
|
-
#
|
797
362
|
#
|
798
|
-
#
|
799
|
-
#
|
800
|
-
#
|
801
|
-
# Hanami::Router.new do
|
802
|
-
# resource 'identity', only: [] do
|
803
|
-
# collection do
|
804
|
-
# get 'keys'
|
805
|
-
# end
|
363
|
+
# router = Hanami::Router.new do
|
364
|
+
# scope "v1" do
|
365
|
+
# get "/users", to: ->(*) { ... }, as: :users
|
806
366
|
# end
|
807
367
|
# end
|
808
368
|
#
|
809
|
-
# #
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
# # +------+----------------+----------------+------+----------------+
|
814
|
-
# # | GET | /identity/keys | Identity::Keys | | :keys_identity |
|
815
|
-
# # +------+----------------+----------------+------+----------------+
|
816
|
-
def resource(name, options = {}, &blk)
|
817
|
-
Routing::Resource.new(self, name, options.merge(separator: Routing::Endpoint::ACTION_SEPARATOR), &blk)
|
818
|
-
end
|
369
|
+
# router.path(:v1_users) # => "/v1/users"
|
370
|
+
def scope(path, &blk)
|
371
|
+
path_prefix = @path_prefix
|
372
|
+
name_prefix = @name_prefix
|
819
373
|
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
# generated
|
829
|
-
# @param blk [Proc] a block of code to generate additional routes
|
830
|
-
#
|
831
|
-
# @return [Hanami::Routing::Resources]
|
832
|
-
#
|
833
|
-
# @since 0.1.0
|
834
|
-
#
|
835
|
-
# @see Hanami::Routing::Resources
|
836
|
-
# @see Hanami::Routing::Resources::Action
|
837
|
-
# @see Hanami::Routing::Resource::Options
|
838
|
-
#
|
839
|
-
# @example Default usage
|
840
|
-
# require 'hanami/router'
|
841
|
-
#
|
842
|
-
# Hanami::Router.new do
|
843
|
-
# resources 'articles'
|
844
|
-
# end
|
845
|
-
#
|
846
|
-
# # It generates:
|
847
|
-
# #
|
848
|
-
# # +--------+--------------------+-------------------+----------+----------------+
|
849
|
-
# # | Verb | Path | Action | Name | Named Route |
|
850
|
-
# # +--------+--------------------+-------------------+----------+----------------+
|
851
|
-
# # | GET | /articles | Articles::Index | :index | :articles |
|
852
|
-
# # | GET | /articles/:id | Articles::Show | :show | :articles |
|
853
|
-
# # | GET | /articles/new | Articles::New | :new | :new_articles |
|
854
|
-
# # | POST | /articles | Articles::Create | :create | :articles |
|
855
|
-
# # | GET | /articles/:id/edit | Articles::Edit | :edit | :edit_articles |
|
856
|
-
# # | PATCH | /articles/:id | Articles::Update | :update | :articles |
|
857
|
-
# # | DELETE | /articles/:id | Articles::Destroy | :destroy | :articles |
|
858
|
-
# # +--------+--------------------+-------------------+----------+----------------+
|
859
|
-
#
|
860
|
-
#
|
861
|
-
#
|
862
|
-
# @example Limit the generated routes with :only
|
863
|
-
# require 'hanami/router'
|
864
|
-
#
|
865
|
-
# Hanami::Router.new do
|
866
|
-
# resources 'articles', only: [:index]
|
867
|
-
# end
|
868
|
-
#
|
869
|
-
# # It generates:
|
870
|
-
# #
|
871
|
-
# # +------+-----------+-----------------+--------+-------------+
|
872
|
-
# # | Verb | Path | Action | Name | Named Route |
|
873
|
-
# # +------+-----------+-----------------+--------+-------------+
|
874
|
-
# # | GET | /articles | Articles::Index | :index | :articles |
|
875
|
-
# # +------+-----------+-----------------+--------+-------------+
|
876
|
-
#
|
877
|
-
#
|
878
|
-
#
|
879
|
-
# @example Limit the generated routes with :except
|
880
|
-
# require 'hanami/router'
|
881
|
-
#
|
882
|
-
# Hanami::Router.new do
|
883
|
-
# resources 'articles', except: [:edit, :update]
|
884
|
-
# end
|
885
|
-
#
|
886
|
-
# # It generates:
|
887
|
-
# #
|
888
|
-
# # +--------+--------------------+-------------------+----------+----------------+
|
889
|
-
# # | Verb | Path | Action | Name | Named Route |
|
890
|
-
# # +--------+--------------------+-------------------+----------+----------------+
|
891
|
-
# # | GET | /articles | Articles::Index | :index | :articles |
|
892
|
-
# # | GET | /articles/:id | Articles::Show | :show | :articles |
|
893
|
-
# # | GET | /articles/new | Articles::New | :new | :new_articles |
|
894
|
-
# # | POST | /articles | Articles::Create | :create | :articles |
|
895
|
-
# # | DELETE | /articles/:id | Articles::Destroy | :destroy | :articles |
|
896
|
-
# # +--------+--------------------+-------------------+----------+----------------+
|
897
|
-
#
|
898
|
-
#
|
899
|
-
#
|
900
|
-
# @example Additional single routes
|
901
|
-
# require 'hanami/router'
|
902
|
-
#
|
903
|
-
# Hanami::Router.new do
|
904
|
-
# resources 'articles', only: [] do
|
905
|
-
# member do
|
906
|
-
# patch 'publish'
|
907
|
-
# end
|
908
|
-
# end
|
909
|
-
# end
|
910
|
-
#
|
911
|
-
# # It generates:
|
912
|
-
# #
|
913
|
-
# # +--------+-----------------------+-------------------+------+-------------------+
|
914
|
-
# # | Verb | Path | Action | Name | Named Route |
|
915
|
-
# # +--------+-----------------------+-------------------+------+-------------------+
|
916
|
-
# # | PATCH | /articles/:id/publish | Articles::Publish | | :publish_articles |
|
917
|
-
# # +--------+-----------------------+-------------------+------+-------------------+
|
918
|
-
#
|
919
|
-
#
|
920
|
-
#
|
921
|
-
# @example Additional collection routes
|
922
|
-
# require 'hanami/router'
|
923
|
-
#
|
924
|
-
# Hanami::Router.new do
|
925
|
-
# resources 'articles', only: [] do
|
926
|
-
# collection do
|
927
|
-
# get 'search'
|
928
|
-
# end
|
929
|
-
# end
|
930
|
-
# end
|
931
|
-
#
|
932
|
-
# # It generates:
|
933
|
-
# #
|
934
|
-
# # +------+------------------+------------------+------+------------------+
|
935
|
-
# # | Verb | Path | Action | Name | Named Route |
|
936
|
-
# # +------+------------------+------------------+------+------------------+
|
937
|
-
# # | GET | /articles/search | Articles::Search | | :search_articles |
|
938
|
-
# # +------+------------------+------------------+------+------------------+
|
939
|
-
def resources(name, options = {}, &blk)
|
940
|
-
Routing::Resources.new(self, name, options.merge(separator: Routing::Endpoint::ACTION_SEPARATOR), &blk)
|
374
|
+
begin
|
375
|
+
@path_prefix = @path_prefix.join(path.to_s)
|
376
|
+
@name_prefix = @name_prefix.join(path.to_s)
|
377
|
+
instance_eval(&blk)
|
378
|
+
ensure
|
379
|
+
@path_prefix = path_prefix
|
380
|
+
@name_prefix = name_prefix
|
381
|
+
end
|
941
382
|
end
|
942
383
|
|
943
384
|
# Mount a Rack application at the specified path.
|
944
385
|
# All the requests starting with the specified path, will be forwarded to
|
945
386
|
# the given application.
|
946
387
|
#
|
947
|
-
# All the other methods (eg
|
388
|
+
# All the other methods (eg `#get`) support callable objects, but they
|
948
389
|
# restrict the range of the acceptable HTTP verb. Mounting an application
|
949
390
|
# with #mount doesn't apply this kind of restriction at the router level,
|
950
391
|
# but let the application to decide.
|
951
392
|
#
|
952
393
|
# @param app [#call] a class or an object that responds to #call
|
953
|
-
# @param
|
954
|
-
# @
|
394
|
+
# @param at [String] the relative path where to mount the app
|
395
|
+
# @param constraints [Hash] a set of constraints for path variables
|
955
396
|
#
|
956
397
|
# @since 0.1.1
|
957
398
|
#
|
958
|
-
# @example
|
959
|
-
# require
|
399
|
+
# @example
|
400
|
+
# require "hanami/router"
|
960
401
|
#
|
961
402
|
# Hanami::Router.new do
|
962
|
-
# mount
|
403
|
+
# mount MyRackApp.new, at: "/foo"
|
963
404
|
# end
|
405
|
+
def mount(app, at:, **constraints)
|
406
|
+
path = prefixed_path(at)
|
407
|
+
prefix = Segment.fabricate(path, **constraints)
|
408
|
+
@mounted[prefix] = @resolver.call(path, app)
|
409
|
+
end
|
410
|
+
|
411
|
+
# Generate an relative URL for a specified named route.
|
412
|
+
# The additional arguments will be used to compose the relative URL - in
|
413
|
+
# case it has tokens to match - and for compose the query string.
|
964
414
|
#
|
965
|
-
#
|
966
|
-
# #
|
967
|
-
# # GET /api # => 200
|
968
|
-
# # GET /api/articles # => 200
|
969
|
-
# # POST /api/articles # => 200
|
970
|
-
# # GET /api/unknown # => 404
|
971
|
-
#
|
972
|
-
# @example Difference between #get and #mount
|
973
|
-
# require 'hanami/router'
|
415
|
+
# @param name [Symbol] the route name
|
974
416
|
#
|
975
|
-
#
|
976
|
-
# get '/rack1', to: RackOne.new
|
977
|
-
# mount RackTwo.new, at: '/rack2'
|
978
|
-
# end
|
417
|
+
# @return [String]
|
979
418
|
#
|
980
|
-
#
|
981
|
-
#
|
982
|
-
# # # /rack1 will only accept GET
|
983
|
-
# # GET /rack1 # => 200 (RackOne.new)
|
984
|
-
# # POST /rack1 # => 405
|
985
|
-
# #
|
986
|
-
# # # /rack2 accepts all the verbs and delegate the decision to RackTwo
|
987
|
-
# # GET /rack2 # => 200 (RackTwo.new)
|
988
|
-
# # POST /rack2 # => 200 (RackTwo.new)
|
989
|
-
#
|
990
|
-
# @example Types of mountable applications
|
991
|
-
# require 'hanami/router'
|
992
|
-
#
|
993
|
-
# class RackOne
|
994
|
-
# def self.call(env)
|
995
|
-
# end
|
996
|
-
# end
|
419
|
+
# @raise [Hanami::Routing::InvalidRouteException] when the router fails to
|
420
|
+
# recognize a route, because of the given arguments.
|
997
421
|
#
|
998
|
-
#
|
999
|
-
# def call(env)
|
1000
|
-
# end
|
1001
|
-
# end
|
422
|
+
# @since 0.1.0
|
1002
423
|
#
|
1003
|
-
#
|
1004
|
-
# def call(env)
|
1005
|
-
# end
|
1006
|
-
# end
|
424
|
+
# @see #url
|
1007
425
|
#
|
1008
|
-
#
|
1009
|
-
#
|
1010
|
-
# def call(env)
|
1011
|
-
# end
|
1012
|
-
# end
|
1013
|
-
# end
|
426
|
+
# @example
|
427
|
+
# require "hanami/router"
|
1014
428
|
#
|
1015
|
-
# Hanami::Router.new do
|
1016
|
-
#
|
1017
|
-
#
|
1018
|
-
# mount RackThree.new, at: '/rack3'
|
1019
|
-
# mount ->(env) {[200, {}, ['Rack Four']]}, at: '/rack4'
|
1020
|
-
# mount 'dashboard#index', at: '/dashboard'
|
429
|
+
# router = Hanami::Router.new(base_url: "https://hanamirb.org") do
|
430
|
+
# get "/login", to: ->(*) { ... }, as: :login
|
431
|
+
# get "/:name", to: ->(*) { ... }, as: :framework
|
1021
432
|
# end
|
1022
433
|
#
|
1023
|
-
#
|
1024
|
-
#
|
1025
|
-
#
|
1026
|
-
|
1027
|
-
|
1028
|
-
def mount(app, at:, host: nil)
|
1029
|
-
app = App.new(@prefix.join(at).to_s, Routing::Endpoint.find(app, @namespace), host: host)
|
1030
|
-
@routes.push(app)
|
434
|
+
# router.path(:login) # => "/login"
|
435
|
+
# router.path(:login, return_to: "/dashboard") # => "/login?return_to=%2Fdashboard"
|
436
|
+
# router.path(:framework, name: "router") # => "/router"
|
437
|
+
def path(name, variables = {})
|
438
|
+
@url_helpers.path(name, variables)
|
1031
439
|
end
|
1032
440
|
|
1033
|
-
#
|
441
|
+
# Generate an absolute URL for a specified named route.
|
442
|
+
# The additional arguments will be used to compose the relative URL - in
|
443
|
+
# case it has tokens to match - and for compose the query string.
|
1034
444
|
#
|
1035
|
-
# @param
|
445
|
+
# @param name [Symbol] the route name
|
1036
446
|
#
|
1037
|
-
# @return [
|
447
|
+
# @return [String]
|
448
|
+
#
|
449
|
+
# @raise [Hanami::Routing::InvalidRouteException] when the router fails to
|
450
|
+
# recognize a route, because of the given arguments.
|
1038
451
|
#
|
1039
452
|
# @since 0.1.0
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
453
|
+
#
|
454
|
+
# @see #path
|
455
|
+
#
|
456
|
+
# @example
|
457
|
+
# require "hanami/router"
|
458
|
+
#
|
459
|
+
# router = Hanami::Router.new(base_url: "https://hanamirb.org") do
|
460
|
+
# get "/login", to: ->(*) { ... }, as: :login
|
461
|
+
# get "/:name", to: ->(*) { ... }, as: :framework
|
462
|
+
# end
|
463
|
+
#
|
464
|
+
# router.url(:login) # => "https://hanamirb.org/login"
|
465
|
+
# router.url(:login, return_to: "/dashboard") # => "https://hanamirb.org/login?return_to=%2Fdashboard"
|
466
|
+
# router.url(:framework, name: "router") # => "https://hanamirb.org/router"
|
467
|
+
def url(name, variables = {})
|
468
|
+
@url_helpers.url(name, variables)
|
1050
469
|
end
|
1051
470
|
|
1052
471
|
# Recognize the given env, path, or name and return a route for testing
|
@@ -1067,34 +486,34 @@ module Hanami
|
|
1067
486
|
# @see Hanami::Routing::RecognizedRoute
|
1068
487
|
#
|
1069
488
|
# @example Successful Path Recognition
|
1070
|
-
# require
|
489
|
+
# require "hanami/router"
|
1071
490
|
#
|
1072
491
|
# router = Hanami::Router.new do
|
1073
|
-
# get
|
492
|
+
# get "/books/:id", to: ->(*) { ... }, as: :book
|
1074
493
|
# end
|
1075
494
|
#
|
1076
|
-
# route = router.recognize(
|
495
|
+
# route = router.recognize("/books/23")
|
1077
496
|
# route.verb # => "GET" (default)
|
1078
497
|
# route.routable? # => true
|
1079
498
|
# route.params # => {:id=>"23"}
|
1080
499
|
#
|
1081
500
|
# @example Successful Rack Env Recognition
|
1082
|
-
# require
|
501
|
+
# require "hanami/router"
|
1083
502
|
#
|
1084
503
|
# router = Hanami::Router.new do
|
1085
|
-
# get
|
504
|
+
# get "/books/:id", to: ->(*) { ... }, as: :book
|
1086
505
|
# end
|
1087
506
|
#
|
1088
|
-
# route = router.recognize(Rack::MockRequest.env_for(
|
507
|
+
# route = router.recognize(Rack::MockRequest.env_for("/books/23"))
|
1089
508
|
# route.verb # => "GET" (default)
|
1090
509
|
# route.routable? # => true
|
1091
510
|
# route.params # => {:id=>"23"}
|
1092
511
|
#
|
1093
512
|
# @example Successful Named Route Recognition
|
1094
|
-
# require
|
513
|
+
# require "hanami/router"
|
1095
514
|
#
|
1096
515
|
# router = Hanami::Router.new do
|
1097
|
-
# get
|
516
|
+
# get "/books/:id", to: ->(*) { ... }, as: :book
|
1098
517
|
# end
|
1099
518
|
#
|
1100
519
|
# route = router.recognize(:book, id: 23)
|
@@ -1103,43 +522,43 @@ module Hanami
|
|
1103
522
|
# route.params # => {:id=>"23"}
|
1104
523
|
#
|
1105
524
|
# @example Failing Recognition For Unknown Path
|
1106
|
-
# require
|
525
|
+
# require "hanami/router"
|
1107
526
|
#
|
1108
527
|
# router = Hanami::Router.new do
|
1109
|
-
# get
|
528
|
+
# get "/books/:id", to: ->(*) { ... }, as: :book
|
1110
529
|
# end
|
1111
530
|
#
|
1112
|
-
# route = router.recognize(
|
531
|
+
# route = router.recognize("/books")
|
1113
532
|
# route.verb # => "GET" (default)
|
1114
533
|
# route.routable? # => false
|
1115
534
|
#
|
1116
535
|
# @example Failing Recognition For Path With Wrong HTTP Verb
|
1117
|
-
# require
|
536
|
+
# require "hanami/router"
|
1118
537
|
#
|
1119
538
|
# router = Hanami::Router.new do
|
1120
|
-
# get
|
539
|
+
# get "/books/:id", to: ->(*) { ... }, as: :book
|
1121
540
|
# end
|
1122
541
|
#
|
1123
|
-
# route = router.recognize(
|
542
|
+
# route = router.recognize("/books/23", method: :post)
|
1124
543
|
# route.verb # => "POST"
|
1125
544
|
# route.routable? # => false
|
1126
545
|
#
|
1127
546
|
# @example Failing Recognition For Rack Env With Wrong HTTP Verb
|
1128
|
-
# require
|
547
|
+
# require "hanami/router"
|
1129
548
|
#
|
1130
549
|
# router = Hanami::Router.new do
|
1131
|
-
# get
|
550
|
+
# get "/books/:id", to: ->(*) { ... }, as: :book
|
1132
551
|
# end
|
1133
552
|
#
|
1134
|
-
# route = router.recognize(Rack::MockRequest.env_for(
|
553
|
+
# route = router.recognize(Rack::MockRequest.env_for("/books/23", method: :post))
|
1135
554
|
# route.verb # => "POST"
|
1136
555
|
# route.routable? # => false
|
1137
556
|
#
|
1138
557
|
# @example Failing Recognition Named Route With Wrong Params
|
1139
|
-
# require
|
558
|
+
# require "hanami/router"
|
1140
559
|
#
|
1141
560
|
# router = Hanami::Router.new do
|
1142
|
-
# get
|
561
|
+
# get "/books/:id", to: ->(*) { ... }, as: :book
|
1143
562
|
# end
|
1144
563
|
#
|
1145
564
|
# route = router.recognize(:book)
|
@@ -1147,104 +566,78 @@ module Hanami
|
|
1147
566
|
# route.routable? # => false
|
1148
567
|
#
|
1149
568
|
# @example Failing Recognition Named Route With Wrong HTTP Verb
|
1150
|
-
# require
|
569
|
+
# require "hanami/router"
|
1151
570
|
#
|
1152
571
|
# router = Hanami::Router.new do
|
1153
|
-
# get
|
572
|
+
# get "/books/:id", to: ->(*) { ... }, as: :book
|
1154
573
|
# end
|
1155
574
|
#
|
1156
575
|
# route = router.recognize(:book, {method: :post}, {id: 1})
|
1157
576
|
# route.verb # => "POST"
|
1158
577
|
# route.routable? # => false
|
1159
578
|
# route.params # => {:id=>"1"}
|
1160
|
-
def recognize(env,
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
579
|
+
def recognize(env, params = {}, options = {})
|
580
|
+
require "hanami/router/recognized_route"
|
581
|
+
env = env_for(env, params, options)
|
582
|
+
endpoint, params = lookup(env)
|
1164
583
|
|
1165
|
-
|
584
|
+
RecognizedRoute.new(
|
585
|
+
endpoint, _params(env, params)
|
586
|
+
)
|
1166
587
|
end
|
1167
588
|
|
1168
|
-
#
|
1169
|
-
#
|
1170
|
-
|
1171
|
-
|
1172
|
-
# @param route [Symbol] the route name
|
1173
|
-
#
|
1174
|
-
# @return [String]
|
1175
|
-
#
|
1176
|
-
# @raise [Hanami::Routing::InvalidRouteException] when the router fails to
|
1177
|
-
# recognize a route, because of the given arguments.
|
1178
|
-
#
|
1179
|
-
# @since 0.1.0
|
1180
|
-
#
|
1181
|
-
# @example
|
1182
|
-
# require 'hanami/router'
|
1183
|
-
#
|
1184
|
-
# router = Hanami::Router.new(scheme: 'https', host: 'hanamirb.org')
|
1185
|
-
# router.get '/login', to: 'sessions#new', as: :login
|
1186
|
-
# router.get '/:name', to: 'frameworks#show', as: :framework
|
1187
|
-
#
|
1188
|
-
# router.path(:login) # => "/login"
|
1189
|
-
# router.path(:login, return_to: '/dashboard') # => "/login?return_to=%2Fdashboard"
|
1190
|
-
# router.path(:framework, name: 'router') # => "/router"
|
1191
|
-
def path(route, args = {})
|
1192
|
-
@named.fetch(route).path(args)
|
1193
|
-
rescue KeyError
|
1194
|
-
raise Hanami::Routing::InvalidRouteException.new("No route could be generated for #{route.inspect} - please check given arguments")
|
589
|
+
# @since 2.0.0
|
590
|
+
# @api private
|
591
|
+
def fixed(env)
|
592
|
+
@fixed.dig(env["REQUEST_METHOD"], env["PATH_INFO"])
|
1195
593
|
end
|
1196
594
|
|
1197
|
-
#
|
1198
|
-
#
|
1199
|
-
|
1200
|
-
|
1201
|
-
# @param route [Symbol] the route name
|
1202
|
-
#
|
1203
|
-
# @return [String]
|
1204
|
-
#
|
1205
|
-
# @raise [Hanami::Routing::InvalidRouteException] when the router fails to
|
1206
|
-
# recognize a route, because of the given arguments.
|
1207
|
-
#
|
1208
|
-
# @since 0.1.0
|
1209
|
-
#
|
1210
|
-
# @example
|
1211
|
-
# require 'hanami/router'
|
1212
|
-
#
|
1213
|
-
# router = Hanami::Router.new(scheme: 'https', host: 'hanamirb.org')
|
1214
|
-
# router.get '/login', to: 'sessions#new', as: :login
|
1215
|
-
# router.get '/:name', to: 'frameworks#show', as: :framework
|
1216
|
-
#
|
1217
|
-
# router.url(:login) # => "https://hanamirb.org/login"
|
1218
|
-
# router.url(:login, return_to: '/dashboard') # => "https://hanamirb.org/login?return_to=%2Fdashboard"
|
1219
|
-
# router.url(:framework, name: 'router') # => "https://hanamirb.org/router"
|
1220
|
-
def url(route, args = {})
|
1221
|
-
@base + path(route, args)
|
595
|
+
# @since 2.0.0
|
596
|
+
# @api private
|
597
|
+
def variable(env)
|
598
|
+
@variable[env["REQUEST_METHOD"]]&.find(env["PATH_INFO"])
|
1222
599
|
end
|
1223
600
|
|
1224
|
-
#
|
1225
|
-
#
|
1226
|
-
|
1227
|
-
|
1228
|
-
|
1229
|
-
|
1230
|
-
|
1231
|
-
|
1232
|
-
|
1233
|
-
|
1234
|
-
|
1235
|
-
|
1236
|
-
#
|
1237
|
-
#
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
|
1247
|
-
|
601
|
+
# @since 2.0.0
|
602
|
+
# @api private
|
603
|
+
def globbed(env)
|
604
|
+
@globbed[env["REQUEST_METHOD"]]&.each do |path, to|
|
605
|
+
if (match = path.match(env["PATH_INFO"]))
|
606
|
+
return [to, match.named_captures]
|
607
|
+
end
|
608
|
+
end
|
609
|
+
|
610
|
+
nil
|
611
|
+
end
|
612
|
+
|
613
|
+
# @since 2.0.0
|
614
|
+
# @api private
|
615
|
+
def mounted(env)
|
616
|
+
@mounted.each do |prefix, app|
|
617
|
+
next unless (match = prefix.peek_match(env["PATH_INFO"]))
|
618
|
+
|
619
|
+
# TODO: ensure compatibility with existing env["SCRIPT_NAME"]
|
620
|
+
# TODO: cleanup this code
|
621
|
+
env["SCRIPT_NAME"] = env["SCRIPT_NAME"].to_s + prefix.to_s
|
622
|
+
env["PATH_INFO"] = env["PATH_INFO"].sub(prefix.to_s, "")
|
623
|
+
env["PATH_INFO"] = "/" if env["PATH_INFO"] == ""
|
624
|
+
|
625
|
+
return [app, match.named_captures]
|
626
|
+
end
|
627
|
+
|
628
|
+
nil
|
629
|
+
end
|
630
|
+
|
631
|
+
# @since 2.0.0
|
632
|
+
# @api private
|
633
|
+
def not_allowed(env)
|
634
|
+
(_not_allowed_fixed(env) || _not_allowed_variable(env)) and return NOT_ALLOWED
|
635
|
+
end
|
636
|
+
|
637
|
+
# @since 2.0.0
|
638
|
+
# @api private
|
639
|
+
def not_found
|
640
|
+
NOT_FOUND
|
1248
641
|
end
|
1249
642
|
|
1250
643
|
protected
|
@@ -1262,16 +655,18 @@ module Hanami
|
|
1262
655
|
#
|
1263
656
|
# @see Hanami::Router#recognize
|
1264
657
|
# @see http://www.rubydoc.info/github/rack/rack/Rack%2FMockRequest.env_for
|
1265
|
-
def env_for(env,
|
658
|
+
def env_for(env, params = {}, options = {}) # rubocop:disable Metrics/MethodLength
|
659
|
+
require "rack/mock"
|
660
|
+
|
1266
661
|
case env
|
1267
|
-
when String
|
1268
|
-
Rack::MockRequest.env_for(env, options)
|
1269
|
-
when Symbol
|
662
|
+
when ::String
|
663
|
+
::Rack::MockRequest.env_for(env, options)
|
664
|
+
when ::Symbol
|
1270
665
|
begin
|
1271
|
-
url = path(env, params
|
1272
|
-
return env_for(url, options)
|
1273
|
-
rescue Hanami::
|
1274
|
-
|
666
|
+
url = path(env, params)
|
667
|
+
return env_for(url, params, options) # rubocop:disable Style/RedundantReturn
|
668
|
+
rescue Hanami::Router::InvalidRouteException
|
669
|
+
EMPTY_RACK_ENV.dup
|
1275
670
|
end
|
1276
671
|
else
|
1277
672
|
env
|
@@ -1280,86 +675,176 @@ module Hanami
|
|
1280
675
|
|
1281
676
|
private
|
1282
677
|
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
678
|
+
# @since 2.0.0
|
679
|
+
# @api private
|
680
|
+
DEFAULT_BASE_URL = "http://localhost"
|
681
|
+
|
682
|
+
# @since 2.0.0
|
683
|
+
# @api private
|
684
|
+
DEFAULT_PREFIX = "/"
|
685
|
+
|
686
|
+
# @since 2.0.0
|
687
|
+
# @api private
|
688
|
+
DEFAULT_RESOLVER = ->(_, to) { to }
|
689
|
+
|
690
|
+
# @since 2.0.0
|
691
|
+
# @api private
|
692
|
+
DEFAULT_REDIRECT_CODE = 301
|
1287
693
|
|
694
|
+
# @since 2.0.0
|
695
|
+
# @api private
|
696
|
+
NOT_FOUND = [404, { "Content-Length" => "9" }, ["Not Found"]].freeze
|
697
|
+
|
698
|
+
# @since 2.0.0
|
699
|
+
# @api private
|
700
|
+
NOT_ALLOWED = [405, { "Content-Length" => "11" }, ["Not Allowed"]].freeze
|
701
|
+
|
702
|
+
# @since 2.0.0
|
703
|
+
# @api private
|
1288
704
|
PARAMS = "router.params"
|
1289
705
|
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
PUT = "PUT"
|
1294
|
-
PATCH = "PATCH"
|
1295
|
-
DELETE = "DELETE"
|
1296
|
-
TRACE = "TRACE"
|
1297
|
-
OPTIONS = "OPTIONS"
|
1298
|
-
LINK = "LINK"
|
1299
|
-
UNLINK = "UNLINK"
|
706
|
+
# @since 2.0.0
|
707
|
+
# @api private
|
708
|
+
EMPTY_PARAMS = {}.freeze
|
1300
709
|
|
1301
|
-
|
1302
|
-
|
1303
|
-
|
710
|
+
# @since 2.0.0
|
711
|
+
# @api private
|
712
|
+
EMPTY_RACK_ENV = {}.freeze
|
1304
713
|
|
1305
|
-
|
714
|
+
# @since 2.0.0
|
715
|
+
# @api private
|
716
|
+
def lookup(env)
|
717
|
+
endpoint = fixed(env)
|
718
|
+
return [endpoint, EMPTY_PARAMS] if endpoint
|
1306
719
|
|
1307
|
-
|
720
|
+
variable(env) || globbed(env) || mounted(env)
|
721
|
+
end
|
1308
722
|
|
1309
|
-
# Application
|
1310
|
-
#
|
1311
723
|
# @since 2.0.0
|
1312
724
|
# @api private
|
1313
|
-
|
1314
|
-
|
1315
|
-
|
1316
|
-
|
1317
|
-
|
1318
|
-
|
1319
|
-
|
725
|
+
def add_route(http_method, path, to, as, constraints, &blk)
|
726
|
+
path = prefixed_path(path)
|
727
|
+
to = resolve_endpoint(path, to, blk)
|
728
|
+
|
729
|
+
if globbed?(path)
|
730
|
+
add_globbed_route(http_method, path, to, constraints)
|
731
|
+
elsif variable?(path)
|
732
|
+
add_variable_route(http_method, path, to, constraints)
|
733
|
+
else
|
734
|
+
add_fixed_route(http_method, path, to)
|
1320
735
|
end
|
1321
736
|
|
1322
|
-
|
1323
|
-
|
1324
|
-
end
|
737
|
+
add_named_route(path, as, constraints) if as
|
738
|
+
end
|
1325
739
|
|
1326
|
-
|
1327
|
-
|
1328
|
-
|
740
|
+
# @since 2.0.0
|
741
|
+
# @api private
|
742
|
+
def resolve_endpoint(path, to, blk)
|
743
|
+
(to || blk) or raise MissingEndpointError.new(path)
|
744
|
+
to = Block.new(@block_context, blk) if to.nil?
|
1329
745
|
|
1330
|
-
|
1331
|
-
|
746
|
+
@resolver.call(path, to)
|
747
|
+
end
|
1332
748
|
|
1333
|
-
|
1334
|
-
|
1335
|
-
|
749
|
+
# @since 2.0.0
|
750
|
+
# @api private
|
751
|
+
def add_globbed_route(http_method, path, to, constraints)
|
752
|
+
@globbed[http_method] ||= []
|
753
|
+
@globbed[http_method] << [Segment.fabricate(path, **constraints), to]
|
754
|
+
end
|
755
|
+
|
756
|
+
# @since 2.0.0
|
757
|
+
# @api private
|
758
|
+
def add_variable_route(http_method, path, to, constraints)
|
759
|
+
@variable[http_method] ||= Trie.new
|
760
|
+
@variable[http_method].add(path, to, constraints)
|
761
|
+
end
|
1336
762
|
|
1337
|
-
|
1338
|
-
|
1339
|
-
|
763
|
+
# @since 2.0.0
|
764
|
+
# @api private
|
765
|
+
def add_fixed_route(http_method, path, to)
|
766
|
+
@fixed[http_method] ||= {}
|
767
|
+
@fixed[http_method][path] = to
|
768
|
+
end
|
1340
769
|
|
1341
|
-
|
770
|
+
# @since 2.0.0
|
771
|
+
# @api private
|
772
|
+
def add_named_route(path, as, constraints)
|
773
|
+
@url_helpers.add(prefixed_name(as), Segment.fabricate(path, **constraints))
|
774
|
+
end
|
775
|
+
|
776
|
+
# @since 2.0.0
|
777
|
+
# @api private
|
778
|
+
def variable?(path)
|
779
|
+
/:/.match?(path)
|
780
|
+
end
|
781
|
+
|
782
|
+
# @since 2.0.0
|
783
|
+
# @api private
|
784
|
+
def globbed?(path)
|
785
|
+
/\*/.match?(path)
|
786
|
+
end
|
787
|
+
|
788
|
+
# @since 2.0.0
|
789
|
+
# @api private
|
790
|
+
def prefixed_path(path)
|
791
|
+
@path_prefix.join(path).to_s
|
792
|
+
end
|
793
|
+
|
794
|
+
# @since 2.0.0
|
795
|
+
# @api private
|
796
|
+
def prefixed_name(name)
|
797
|
+
@name_prefix.relative_join(name, "_").to_sym
|
798
|
+
end
|
799
|
+
|
800
|
+
# @since 2.0.0
|
801
|
+
# @api private
|
802
|
+
def _redirect(to, code)
|
803
|
+
body = Rack::Utils::HTTP_STATUS_CODES.fetch(code) do
|
804
|
+
raise UnknownHTTPStatusCodeError.new(code)
|
1342
805
|
end
|
806
|
+
|
807
|
+
destination = prefixed_path(to)
|
808
|
+
Redirect.new(destination, ->(*) { [code, { "Location" => destination }, [body]] })
|
809
|
+
end
|
810
|
+
|
811
|
+
# @since 2.0.0
|
812
|
+
# @api private
|
813
|
+
def _params(env, params)
|
814
|
+
params ||= {}
|
815
|
+
env[PARAMS] ||= {}
|
816
|
+
env[PARAMS].merge!(Rack::Utils.parse_nested_query(env["QUERY_STRING"]))
|
817
|
+
env[PARAMS].merge!(params)
|
818
|
+
env[PARAMS] = Params.deep_symbolize(env[PARAMS])
|
819
|
+
env
|
1343
820
|
end
|
1344
821
|
|
1345
|
-
|
1346
|
-
|
1347
|
-
|
822
|
+
# @since 2.0.0
|
823
|
+
# @api private
|
824
|
+
def _not_allowed_fixed(env)
|
825
|
+
found = false
|
1348
826
|
|
1349
|
-
|
1350
|
-
|
1351
|
-
route = Routing::Route.new(verb_for(verb), @prefix.join(path).to_s, endpoint, constraints)
|
827
|
+
@fixed.each_value do |routes|
|
828
|
+
break if found
|
1352
829
|
|
1353
|
-
|
1354
|
-
|
830
|
+
found = routes.key?(env["PATH_INFO"])
|
831
|
+
end
|
832
|
+
|
833
|
+
found
|
1355
834
|
end
|
1356
835
|
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
836
|
+
# @since 2.0.0
|
837
|
+
# @api private
|
838
|
+
def _not_allowed_variable(env)
|
839
|
+
found = false
|
840
|
+
|
841
|
+
@variable.each_value do |routes|
|
842
|
+
break if found
|
843
|
+
|
844
|
+
found = routes.find(env["PATH_INFO"])
|
1362
845
|
end
|
846
|
+
|
847
|
+
found
|
1363
848
|
end
|
1364
849
|
end
|
1365
850
|
end
|