hanami-router 0.0.0 → 0.6.0
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 +101 -0
- data/LICENSE.md +22 -0
- data/README.md +667 -9
- data/hanami-router.gemspec +18 -12
- data/lib/hanami-router.rb +1 -0
- data/lib/hanami/router.rb +1160 -3
- data/lib/hanami/router/version.rb +3 -2
- data/lib/hanami/routing/endpoint.rb +151 -0
- data/lib/hanami/routing/endpoint_resolver.rb +225 -0
- data/lib/hanami/routing/error.rb +7 -0
- data/lib/hanami/routing/force_ssl.rb +209 -0
- data/lib/hanami/routing/http_router.rb +187 -0
- data/lib/hanami/routing/namespace.rb +92 -0
- data/lib/hanami/routing/parsers.rb +71 -0
- data/lib/hanami/routing/parsing/json_parser.rb +28 -0
- data/lib/hanami/routing/parsing/parser.rb +58 -0
- data/lib/hanami/routing/recognized_route.rb +153 -0
- data/lib/hanami/routing/resource.rb +116 -0
- data/lib/hanami/routing/resource/action.rb +387 -0
- data/lib/hanami/routing/resource/nested.rb +39 -0
- data/lib/hanami/routing/resource/options.rb +74 -0
- data/lib/hanami/routing/resources.rb +48 -0
- data/lib/hanami/routing/resources/action.rb +150 -0
- data/lib/hanami/routing/route.rb +62 -0
- data/lib/hanami/routing/routes_inspector.rb +215 -0
- metadata +94 -16
- data/.gitignore +0 -9
- data/Gemfile +0 -4
- data/Rakefile +0 -2
- data/bin/console +0 -14
- data/bin/setup +0 -8
data/hanami-router.gemspec
CHANGED
@@ -4,20 +4,26 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'hanami/router/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'hanami-router'
|
8
8
|
spec.version = Hanami::Router::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
9
|
+
spec.authors = ['Luca Guidi', 'Trung Lê', 'Alfonso Uceda']
|
10
|
+
spec.email = ['me@lucaguidi.com', 'trung.le@ruby-journal.com', 'uceda73@gmail.com']
|
11
|
+
spec.description = %q{Rack compatible HTTP router for Ruby}
|
12
|
+
spec.summary = %q{Rack compatible HTTP router for Ruby and Hanami}
|
13
|
+
spec.homepage = 'http://hanamirb.org'
|
14
|
+
spec.license = 'MIT'
|
11
15
|
|
12
|
-
spec.
|
13
|
-
spec.
|
14
|
-
spec.
|
16
|
+
spec.files = `git ls-files -- lib/* CHANGELOG.md LICENSE.md README.md hanami-router.gemspec`.split($/)
|
17
|
+
spec.executables = []
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
spec.required_ruby_version = '>= 2.0.0'
|
15
21
|
|
16
|
-
spec.
|
17
|
-
spec.
|
18
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
-
spec.require_paths = ["lib"]
|
22
|
+
spec.add_dependency 'http_router', '~> 0.11'
|
23
|
+
spec.add_dependency 'hanami-utils', '~> 0.7'
|
20
24
|
|
21
|
-
spec.add_development_dependency
|
22
|
-
spec.add_development_dependency
|
25
|
+
spec.add_development_dependency 'bundler', '~> 1.5'
|
26
|
+
spec.add_development_dependency 'minitest', '~> 5'
|
27
|
+
spec.add_development_dependency 'rake', '~> 10'
|
28
|
+
spec.add_development_dependency 'rack-test', '~> 0.6'
|
23
29
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'hanami/router'
|
data/lib/hanami/router.rb
CHANGED
@@ -1,7 +1,1164 @@
|
|
1
|
-
require
|
1
|
+
require 'rack/request'
|
2
|
+
require 'hanami/routing/http_router'
|
3
|
+
require 'hanami/routing/namespace'
|
4
|
+
require 'hanami/routing/resource'
|
5
|
+
require 'hanami/routing/resources'
|
6
|
+
require 'hanami/routing/error'
|
2
7
|
|
3
8
|
module Hanami
|
4
|
-
|
5
|
-
|
9
|
+
# Rack compatible, lightweight and fast HTTP Router.
|
10
|
+
#
|
11
|
+
# @since 0.1.0
|
12
|
+
#
|
13
|
+
# @example It offers an intuitive DSL, that supports most of the HTTP verbs:
|
14
|
+
# require 'hanami/router'
|
15
|
+
#
|
16
|
+
# endpoint = ->(env) { [200, {}, ['Welcome to Hanami::Router!']] }
|
17
|
+
# router = Hanami::Router.new do
|
18
|
+
# get '/', to: endpoint # => get and head requests
|
19
|
+
# post '/', to: endpoint
|
20
|
+
# put '/', to: endpoint
|
21
|
+
# patch '/', to: endpoint
|
22
|
+
# delete '/', to: endpoint
|
23
|
+
# options '/', to: endpoint
|
24
|
+
# trace '/', to: endpoint
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
#
|
28
|
+
#
|
29
|
+
# @example Specify an endpoint with `:to` (Rack compatible object)
|
30
|
+
# require 'hanami/router'
|
31
|
+
#
|
32
|
+
# endpoint = ->(env) { [200, {}, ['Welcome to Hanami::Router!']] }
|
33
|
+
# router = Hanami::Router.new do
|
34
|
+
# get '/', to: endpoint
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# # :to is mandatory for the default resolver (`Hanami::Routing::EndpointResolver.new`),
|
38
|
+
# # This behavior can be changed by passing a custom resolver to `Hanami::Router#initialize`
|
39
|
+
#
|
40
|
+
#
|
41
|
+
#
|
42
|
+
# @example Specify an endpoint with `:to` (controller and action string)
|
43
|
+
# require 'hanami/router'
|
44
|
+
#
|
45
|
+
# router = Hanami::Router.new do
|
46
|
+
# get '/', to: 'articles#show' # => Articles::Show
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# # This is a builtin feature for a Hanami::Controller convention.
|
50
|
+
#
|
51
|
+
#
|
52
|
+
#
|
53
|
+
# @example Specify a named route with `:as`
|
54
|
+
# require 'hanami/router'
|
55
|
+
#
|
56
|
+
# endpoint = ->(env) { [200, {}, ['Welcome to Hanami::Router!']] }
|
57
|
+
# router = Hanami::Router.new(scheme: 'https', host: 'hanamirb.org') do
|
58
|
+
# get '/', to: endpoint, as: :root
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# router.path(:root) # => '/'
|
62
|
+
# router.url(:root) # => 'https://hanamirb.org/'
|
63
|
+
#
|
64
|
+
# # This isn't mandatory for the default route class (`Hanami::Routing::Route`),
|
65
|
+
# # This behavior can be changed by passing a custom route to `Hanami::Router#initialize`
|
66
|
+
#
|
67
|
+
# @example Mount an application
|
68
|
+
# require 'hanami/router'
|
69
|
+
#
|
70
|
+
# router = Hanami::Router.new do
|
71
|
+
# mount Api::App, at: '/api'
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# # All the requests starting with "/api" will be forwarded to Api::App
|
75
|
+
class Router
|
76
|
+
# This error is raised when <tt>#call</tt> is invoked on a non-routable
|
77
|
+
# recognized route.
|
78
|
+
#
|
79
|
+
# @since 0.5.0
|
80
|
+
#
|
81
|
+
# @see Hanami::Router#recognize
|
82
|
+
# @see Hanami::Routing::RecognizedRoute
|
83
|
+
# @see Hanami::Routing::RecognizedRoute#call
|
84
|
+
# @see Hanami::Routing::RecognizedRoute#routable?
|
85
|
+
class NotRoutableEndpointError < Hanami::Routing::Error
|
86
|
+
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
|
87
|
+
PATH_INFO = 'PATH_INFO'.freeze
|
88
|
+
|
89
|
+
def initialize(env)
|
90
|
+
super %(Cannot find routable endpoint for #{ env[REQUEST_METHOD] } "#{ env[PATH_INFO] }")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns the given block as it is.
|
95
|
+
#
|
96
|
+
# When Hanami::Router is used as a standalone gem and the routes are defined
|
97
|
+
# into a configuration file, some systems could raise an exception.
|
98
|
+
#
|
99
|
+
# Imagine the following file into a Ruby on Rails application:
|
100
|
+
#
|
101
|
+
# get '/', to: 'api#index'
|
102
|
+
#
|
103
|
+
# Because Ruby on Rails in production mode use to eager load code and the
|
104
|
+
# routes file uses top level method calls, it crashes the application.
|
105
|
+
#
|
106
|
+
# If we wrap these routes with <tt>Hanami::Router.define</tt>, the block
|
107
|
+
# doesn't get yielded but just returned to the caller as it is.
|
108
|
+
#
|
109
|
+
# Usually the receiver of this block is <tt>Hanami::Router#initialize</tt>,
|
110
|
+
# which finally evaluates the block.
|
111
|
+
#
|
112
|
+
# @param blk [Proc] a set of route definitions
|
113
|
+
#
|
114
|
+
# @return [Proc] the given block
|
115
|
+
#
|
116
|
+
# @since 0.5.0
|
117
|
+
#
|
118
|
+
# @example
|
119
|
+
# # apps/web/config/routes.rb
|
120
|
+
# Hanami::Router.define do
|
121
|
+
# get '/', to: 'home#index'
|
122
|
+
# end
|
123
|
+
def self.define(&blk)
|
124
|
+
blk
|
125
|
+
end
|
126
|
+
|
127
|
+
# Initialize the router.
|
128
|
+
#
|
129
|
+
# @param options [Hash] the options to initialize the router
|
130
|
+
#
|
131
|
+
# @option options [String] :scheme The HTTP scheme (defaults to `"http"`)
|
132
|
+
# @option options [String] :host The URL host (defaults to `"localhost"`)
|
133
|
+
# @option options [String] :port The URL port (defaults to `"80"`)
|
134
|
+
# @option options [Object, #resolve, #find, #action_separator] :resolver
|
135
|
+
# the route resolver (defaults to `Hanami::Routing::EndpointResolver.new`)
|
136
|
+
# @option options [Object, #generate] :route the route class
|
137
|
+
# (defaults to `Hanami::Routing::Route`)
|
138
|
+
# @option options [String] :action_separator the separator between controller
|
139
|
+
# and action name (eg. 'dashboard#show', where '#' is the :action_separator)
|
140
|
+
# @option options [Array<Symbol,String,Object #mime_types, parse>] :parsers
|
141
|
+
# the body parsers for mime types
|
142
|
+
#
|
143
|
+
# @param blk [Proc] the optional block to define the routes
|
144
|
+
#
|
145
|
+
# @return [Hanami::Router] self
|
146
|
+
#
|
147
|
+
# @since 0.1.0
|
148
|
+
#
|
149
|
+
# @example Basic example
|
150
|
+
# require 'hanami/router'
|
151
|
+
#
|
152
|
+
# endpoint = ->(env) { [200, {}, ['Welcome to Hanami::Router!']] }
|
153
|
+
#
|
154
|
+
# router = Hanami::Router.new
|
155
|
+
# router.get '/', to: endpoint
|
156
|
+
#
|
157
|
+
# # or
|
158
|
+
#
|
159
|
+
# router = Hanami::Router.new do
|
160
|
+
# get '/', to: endpoint
|
161
|
+
# end
|
162
|
+
#
|
163
|
+
# @example Body parsers
|
164
|
+
# require 'json'
|
165
|
+
# require 'hanami/router'
|
166
|
+
#
|
167
|
+
# # It parses JSON body and makes the attributes available to the params
|
168
|
+
#
|
169
|
+
# endpoint = ->(env) { [200, {},[env['router.params'].inspect]] }
|
170
|
+
#
|
171
|
+
# router = Hanami::Router.new(parsers: [:json]) do
|
172
|
+
# patch '/books/:id', to: endpoint
|
173
|
+
# end
|
174
|
+
#
|
175
|
+
# # From the shell
|
176
|
+
#
|
177
|
+
# curl http://localhost:2300/books/1 \
|
178
|
+
# -H "Content-Type: application/json" \
|
179
|
+
# -H "Accept: application/json" \
|
180
|
+
# -d '{"published":"true"}' \
|
181
|
+
# -X PATCH
|
182
|
+
#
|
183
|
+
# # It returns
|
184
|
+
#
|
185
|
+
# [200, {}, ["{:published=>\"true\",:id=>\"1\"}"]]
|
186
|
+
#
|
187
|
+
# @example Custom body parser
|
188
|
+
# require 'hanami/router'
|
189
|
+
#
|
190
|
+
# class XmlParser
|
191
|
+
# def mime_types
|
192
|
+
# ['application/xml', 'text/xml']
|
193
|
+
# end
|
194
|
+
#
|
195
|
+
# # Parse body and return a Hash
|
196
|
+
# def parse(body)
|
197
|
+
# # ...
|
198
|
+
# end
|
199
|
+
# end
|
200
|
+
#
|
201
|
+
# # It parses XML body and makes the attributes available to the params
|
202
|
+
#
|
203
|
+
# endpoint = ->(env) { [200, {},[env['router.params'].inspect]] }
|
204
|
+
#
|
205
|
+
# router = Hanami::Router.new(parsers: [XmlParser.new]) do
|
206
|
+
# patch '/authors/:id', to: endpoint
|
207
|
+
# end
|
208
|
+
#
|
209
|
+
# # From the shell
|
210
|
+
#
|
211
|
+
# curl http://localhost:2300/authors/1 \
|
212
|
+
# -H "Content-Type: application/xml" \
|
213
|
+
# -H "Accept: application/xml" \
|
214
|
+
# -d '<name>LG</name>' \
|
215
|
+
# -X PATCH
|
216
|
+
#
|
217
|
+
# # It returns
|
218
|
+
#
|
219
|
+
# [200, {}, ["{:name=>\"LG\",:id=>\"1\"}"]]
|
220
|
+
def initialize(options = {}, &blk)
|
221
|
+
@router = Routing::HttpRouter.new(options)
|
222
|
+
define(&blk)
|
223
|
+
end
|
224
|
+
|
225
|
+
# Returns self
|
226
|
+
#
|
227
|
+
# This is a duck-typing trick for compatibility with `Hanami::Application`.
|
228
|
+
# It's used by `Hanami::Routing::RoutesInspector` to inspect both apps and
|
229
|
+
# routers.
|
230
|
+
#
|
231
|
+
# @return [self]
|
232
|
+
#
|
233
|
+
# @since 0.2.0
|
234
|
+
# @api private
|
235
|
+
def routes
|
236
|
+
self
|
237
|
+
end
|
238
|
+
|
239
|
+
# To support defining routes in the `define` wrapper.
|
240
|
+
#
|
241
|
+
# @param blk [Proc] the block to define the routes
|
242
|
+
#
|
243
|
+
# @return [Hanami::Routing::Route]
|
244
|
+
#
|
245
|
+
# @since 0.2.0
|
246
|
+
#
|
247
|
+
# @example In Hanami framework
|
248
|
+
# class Application < Hanami::Application
|
249
|
+
# configure do
|
250
|
+
# routes 'config/routes'
|
251
|
+
# end
|
252
|
+
# end
|
253
|
+
#
|
254
|
+
# # In `config/routes`
|
255
|
+
#
|
256
|
+
# define do
|
257
|
+
# get # ...
|
258
|
+
# end
|
259
|
+
def define(&blk)
|
260
|
+
instance_eval(&blk) if block_given?
|
261
|
+
end
|
262
|
+
|
263
|
+
# Check if there are defined routes
|
264
|
+
#
|
265
|
+
# @return [TrueClass,FalseClass] the result of the check
|
266
|
+
#
|
267
|
+
# @since 0.2.0
|
268
|
+
# @api private
|
269
|
+
#
|
270
|
+
# @example
|
271
|
+
#
|
272
|
+
# router = Hanami::Router.new
|
273
|
+
# router.defined? # => false
|
274
|
+
#
|
275
|
+
# router = Hanami::Router.new { get '/', to: ->(env) { } }
|
276
|
+
# router.defined? # => true
|
277
|
+
def defined?
|
278
|
+
@router.routes.any?
|
279
|
+
end
|
280
|
+
|
281
|
+
# Defines a route that accepts a GET request for the given path.
|
282
|
+
#
|
283
|
+
# @param path [String] the relative URL to be matched
|
284
|
+
#
|
285
|
+
# @param options [Hash] the options to customize the route
|
286
|
+
# @option options [String,Proc,Class,Object#call] :to the endpoint
|
287
|
+
#
|
288
|
+
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
289
|
+
#
|
290
|
+
# @return [Hanami::Routing::Route] this may vary according to the :route
|
291
|
+
# option passed to the constructor
|
292
|
+
#
|
293
|
+
# @since 0.1.0
|
294
|
+
#
|
295
|
+
# @example Fixed matching string
|
296
|
+
# require 'hanami/router'
|
297
|
+
#
|
298
|
+
# router = Hanami::Router.new
|
299
|
+
# router.get '/hanami', to: ->(env) { [200, {}, ['Hello from Hanami!']] }
|
300
|
+
#
|
301
|
+
# @example String matching with variables
|
302
|
+
# require 'hanami/router'
|
303
|
+
#
|
304
|
+
# router = Hanami::Router.new
|
305
|
+
# router.get '/flowers/:id',
|
306
|
+
# to: ->(env) {
|
307
|
+
# [
|
308
|
+
# 200,
|
309
|
+
# {},
|
310
|
+
# ["Hello from Flower no. #{ env['router.params'][:id] }!"]
|
311
|
+
# ]
|
312
|
+
# }
|
313
|
+
#
|
314
|
+
# @example Variables Constraints
|
315
|
+
# require 'hanami/router'
|
316
|
+
#
|
317
|
+
# router = Hanami::Router.new
|
318
|
+
# router.get '/flowers/:id',
|
319
|
+
# id: /\d+/,
|
320
|
+
# to: ->(env) { [200, {}, [":id must be a number!"]] }
|
321
|
+
#
|
322
|
+
# @example String matching with globbling
|
323
|
+
# require 'hanami/router'
|
324
|
+
#
|
325
|
+
# router = Hanami::Router.new
|
326
|
+
# router.get '/*',
|
327
|
+
# to: ->(env) {
|
328
|
+
# [
|
329
|
+
# 200,
|
330
|
+
# {},
|
331
|
+
# ["This is catch all: #{ env['router.params'].inspect }!"]
|
332
|
+
# ]
|
333
|
+
# }
|
334
|
+
#
|
335
|
+
# @example String matching with optional tokens
|
336
|
+
# require 'hanami/router'
|
337
|
+
#
|
338
|
+
# router = Hanami::Router.new
|
339
|
+
# router.get '/hanami(.:format)',
|
340
|
+
# to: ->(env) {
|
341
|
+
# [200, {}, ["You've requested #{ env['router.params'][:format] }!"]]
|
342
|
+
# }
|
343
|
+
#
|
344
|
+
# @example Named routes
|
345
|
+
# require 'hanami/router'
|
346
|
+
#
|
347
|
+
# router = Hanami::Router.new(scheme: 'https', host: 'hanamirb.org')
|
348
|
+
# router.get '/hanami',
|
349
|
+
# to: ->(env) { [200, {}, ['Hello from Hanami!']] },
|
350
|
+
# as: :hanami
|
351
|
+
#
|
352
|
+
# router.path(:hanami) # => "/hanami"
|
353
|
+
# router.url(:hanami) # => "https://hanamirb.org/hanami"
|
354
|
+
#
|
355
|
+
# @example Duck typed endpoints (Rack compatible objects)
|
356
|
+
# require 'hanami/router'
|
357
|
+
#
|
358
|
+
# router = Hanami::Router.new
|
359
|
+
#
|
360
|
+
# router.get '/hanami', to: ->(env) { [200, {}, ['Hello from Hanami!']] }
|
361
|
+
# router.get '/middleware', to: Middleware
|
362
|
+
# router.get '/rack-app', to: RackApp.new
|
363
|
+
# router.get '/method', to: ActionControllerSubclass.action(:new)
|
364
|
+
#
|
365
|
+
# # Everything that responds to #call is invoked as it is
|
366
|
+
#
|
367
|
+
# @example Duck typed endpoints (strings)
|
368
|
+
# require 'hanami/router'
|
369
|
+
#
|
370
|
+
# class RackApp
|
371
|
+
# def call(env)
|
372
|
+
# # ...
|
373
|
+
# end
|
374
|
+
# end
|
375
|
+
#
|
376
|
+
# router = Hanami::Router.new
|
377
|
+
# router.get '/hanami', to: 'rack_app' # it will map to RackApp.new
|
378
|
+
#
|
379
|
+
# @example Duck typed endpoints (string: controller + action)
|
380
|
+
# require 'hanami/router'
|
381
|
+
#
|
382
|
+
# module Flowers
|
383
|
+
# class Index
|
384
|
+
# def call(env)
|
385
|
+
# # ...
|
386
|
+
# end
|
387
|
+
# end
|
388
|
+
# end
|
389
|
+
#
|
390
|
+
# router = Hanami::Router.new
|
391
|
+
# router.get '/flowers', to: 'flowers#index'
|
392
|
+
#
|
393
|
+
# # It will map to Flowers::Index.new, which is the
|
394
|
+
# # Hanami::Controller convention.
|
395
|
+
def get(path, options = {}, &blk)
|
396
|
+
@router.get(path, options, &blk)
|
397
|
+
end
|
398
|
+
|
399
|
+
# Defines a route that accepts a POST request for the given path.
|
400
|
+
#
|
401
|
+
# @param path [String] the relative URL to be matched
|
402
|
+
#
|
403
|
+
# @param options [Hash] the options to customize the route
|
404
|
+
# @option options [String,Proc,Class,Object#call] :to the endpoint
|
405
|
+
#
|
406
|
+
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
407
|
+
#
|
408
|
+
# @return [Hanami::Routing::Route] this may vary according to the :route
|
409
|
+
# option passed to the constructor
|
410
|
+
#
|
411
|
+
# @see Hanami::Router#get
|
412
|
+
#
|
413
|
+
# @since 0.1.0
|
414
|
+
def post(path, options = {}, &blk)
|
415
|
+
@router.post(path, options, &blk)
|
416
|
+
end
|
417
|
+
|
418
|
+
# Defines a route that accepts a PUT request for the given path.
|
419
|
+
#
|
420
|
+
# @param path [String] the relative URL to be matched
|
421
|
+
#
|
422
|
+
# @param options [Hash] the options to customize the route
|
423
|
+
# @option options [String,Proc,Class,Object#call] :to the endpoint
|
424
|
+
#
|
425
|
+
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
426
|
+
#
|
427
|
+
# @return [Hanami::Routing::Route] this may vary according to the :route
|
428
|
+
# option passed to the constructor
|
429
|
+
#
|
430
|
+
# @see Hanami::Router#get
|
431
|
+
#
|
432
|
+
# @since 0.1.0
|
433
|
+
def put(path, options = {}, &blk)
|
434
|
+
@router.put(path, options, &blk)
|
435
|
+
end
|
436
|
+
|
437
|
+
# Defines a route that accepts a PATCH request for the given path.
|
438
|
+
#
|
439
|
+
# @param path [String] the relative URL to be matched
|
440
|
+
#
|
441
|
+
# @param options [Hash] the options to customize the route
|
442
|
+
# @option options [String,Proc,Class,Object#call] :to the endpoint
|
443
|
+
#
|
444
|
+
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
445
|
+
#
|
446
|
+
# @return [Hanami::Routing::Route] this may vary according to the :route
|
447
|
+
# option passed to the constructor
|
448
|
+
#
|
449
|
+
# @see Hanami::Router#get
|
450
|
+
#
|
451
|
+
# @since 0.1.0
|
452
|
+
def patch(path, options = {}, &blk)
|
453
|
+
@router.patch(path, options, &blk)
|
454
|
+
end
|
455
|
+
|
456
|
+
# Defines a route that accepts a DELETE request for the given path.
|
457
|
+
#
|
458
|
+
# @param path [String] the relative URL to be matched
|
459
|
+
#
|
460
|
+
# @param options [Hash] the options to customize the route
|
461
|
+
# @option options [String,Proc,Class,Object#call] :to the endpoint
|
462
|
+
#
|
463
|
+
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
464
|
+
#
|
465
|
+
# @return [Hanami::Routing::Route] this may vary according to the :route
|
466
|
+
# option passed to the constructor
|
467
|
+
#
|
468
|
+
# @see Hanami::Router#get
|
469
|
+
#
|
470
|
+
# @since 0.1.0
|
471
|
+
def delete(path, options = {}, &blk)
|
472
|
+
@router.delete(path, options, &blk)
|
473
|
+
end
|
474
|
+
|
475
|
+
# Defines a route that accepts a TRACE request for the given path.
|
476
|
+
#
|
477
|
+
# @param path [String] the relative URL to be matched
|
478
|
+
#
|
479
|
+
# @param options [Hash] the options to customize the route
|
480
|
+
# @option options [String,Proc,Class,Object#call] :to the endpoint
|
481
|
+
#
|
482
|
+
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
483
|
+
#
|
484
|
+
# @return [Hanami::Routing::Route] this may vary according to the :route
|
485
|
+
# option passed to the constructor
|
486
|
+
#
|
487
|
+
# @see Hanami::Router#get
|
488
|
+
#
|
489
|
+
# @since 0.1.0
|
490
|
+
def trace(path, options = {}, &blk)
|
491
|
+
@router.trace(path, options, &blk)
|
492
|
+
end
|
493
|
+
|
494
|
+
# Defines a route that accepts a OPTIONS request for the given path.
|
495
|
+
#
|
496
|
+
# @param path [String] the relative URL to be matched
|
497
|
+
#
|
498
|
+
# @param options [Hash] the options to customize the route
|
499
|
+
# @option options [String,Proc,Class,Object#call] :to the endpoint
|
500
|
+
#
|
501
|
+
# @param blk [Proc] the anonymous proc to be used as endpoint for the route
|
502
|
+
#
|
503
|
+
# @return [Hanami::Routing::Route] this may vary according to the :route
|
504
|
+
# option passed to the constructor
|
505
|
+
#
|
506
|
+
# @see Hanami::Router#get
|
507
|
+
#
|
508
|
+
# @since 0.1.0
|
509
|
+
def options(path, options = {}, &blk)
|
510
|
+
@router.options(path, options, &blk)
|
511
|
+
end
|
512
|
+
|
513
|
+
# Defines an HTTP redirect
|
514
|
+
#
|
515
|
+
# @param path [String] the path that needs to be redirected
|
516
|
+
# @param options [Hash] the options to customize the redirect behavior
|
517
|
+
# @option options [Fixnum] the HTTP status to return (defaults to `301`)
|
518
|
+
#
|
519
|
+
# @return [Hanami::Routing::Route] the generated route.
|
520
|
+
# This may vary according to the `:route` option passed to the initializer
|
521
|
+
#
|
522
|
+
# @since 0.1.0
|
523
|
+
#
|
524
|
+
# @see Hanami::Router
|
525
|
+
#
|
526
|
+
# @example
|
527
|
+
# require 'hanami/router'
|
528
|
+
#
|
529
|
+
# Hanami::Router.new do
|
530
|
+
# redirect '/legacy', to: '/new_endpoint'
|
531
|
+
# redirect '/legacy2', to: '/new_endpoint2', code: 302
|
532
|
+
# end
|
533
|
+
#
|
534
|
+
# @example
|
535
|
+
# require 'hanami/router'
|
536
|
+
#
|
537
|
+
# router = Hanami::Router.new
|
538
|
+
# router.redirect '/legacy', to: '/new_endpoint'
|
539
|
+
def redirect(path, options = {}, &endpoint)
|
540
|
+
get(path).redirect @router.find(options), options[:code] || 301
|
541
|
+
end
|
542
|
+
|
543
|
+
# Defines a Ruby block: all the routes defined within it will be namespaced
|
544
|
+
# with the given relative path.
|
545
|
+
#
|
546
|
+
# Namespaces blocks can be nested multiple times.
|
547
|
+
#
|
548
|
+
# @param namespace [String] the relative path where the nested routes will
|
549
|
+
# be mounted
|
550
|
+
# @param blk [Proc] the block that defines the resources
|
551
|
+
#
|
552
|
+
# @return [Hanami::Routing::Namespace] the generated namespace.
|
553
|
+
#
|
554
|
+
# @since 0.1.0
|
555
|
+
#
|
556
|
+
# @see Hanami::Router
|
557
|
+
#
|
558
|
+
# @example Basic example
|
559
|
+
# require 'hanami/router'
|
560
|
+
#
|
561
|
+
# Hanami::Router.new do
|
562
|
+
# namespace 'trees' do
|
563
|
+
# get '/sequoia', to: endpoint # => '/trees/sequoia'
|
564
|
+
# end
|
565
|
+
# end
|
566
|
+
#
|
567
|
+
# @example Nested namespaces
|
568
|
+
# require 'hanami/router'
|
569
|
+
#
|
570
|
+
# Hanami::Router.new do
|
571
|
+
# namespace 'animals' do
|
572
|
+
# namespace 'mammals' do
|
573
|
+
# get '/cats', to: endpoint # => '/animals/mammals/cats'
|
574
|
+
# end
|
575
|
+
# end
|
576
|
+
# end
|
577
|
+
#
|
578
|
+
# @example
|
579
|
+
# require 'hanami/router'
|
580
|
+
#
|
581
|
+
# router = Hanami::Router.new
|
582
|
+
# router.namespace 'trees' do
|
583
|
+
# get '/sequoia', to: endpoint # => '/trees/sequoia'
|
584
|
+
# end
|
585
|
+
def namespace(namespace, &blk)
|
586
|
+
Routing::Namespace.new(self, namespace, &blk)
|
587
|
+
end
|
588
|
+
|
589
|
+
# Defines a set of named routes for a single RESTful resource.
|
590
|
+
# It has a built-in integration for Hanami::Controller.
|
591
|
+
#
|
592
|
+
# @param name [String] the name of the resource
|
593
|
+
# @param options [Hash] a set of options to customize the routes
|
594
|
+
# @option options [Array<Symbol>] :only a subset of the default routes
|
595
|
+
# that we want to generate
|
596
|
+
# @option options [Array<Symbol>] :except prevent the given routes to be
|
597
|
+
# generated
|
598
|
+
# @param blk [Proc] a block of code to generate additional routes
|
599
|
+
#
|
600
|
+
# @return [Hanami::Routing::Resource]
|
601
|
+
#
|
602
|
+
# @since 0.1.0
|
603
|
+
#
|
604
|
+
# @see Hanami::Routing::Resource
|
605
|
+
# @see Hanami::Routing::Resource::Action
|
606
|
+
# @see Hanami::Routing::Resource::Options
|
607
|
+
#
|
608
|
+
# @example Default usage
|
609
|
+
# require 'hanami/router'
|
610
|
+
#
|
611
|
+
# Hanami::Router.new do
|
612
|
+
# resource 'identity'
|
613
|
+
# end
|
614
|
+
#
|
615
|
+
# # It generates:
|
616
|
+
# #
|
617
|
+
# # +--------+----------------+-------------------+----------+----------------+
|
618
|
+
# # | Verb | Path | Action | Name | Named Route |
|
619
|
+
# # +--------+----------------+-------------------+----------+----------------+
|
620
|
+
# # | GET | /identity | Identity::Show | :show | :identity |
|
621
|
+
# # | GET | /identity/new | Identity::New | :new | :new_identity |
|
622
|
+
# # | POST | /identity | Identity::Create | :create | :identity |
|
623
|
+
# # | GET | /identity/edit | Identity::Edit | :edit | :edit_identity |
|
624
|
+
# # | PATCH | /identity | Identity::Update | :update | :identity |
|
625
|
+
# # | DELETE | /identity | Identity::Destroy | :destroy | :identity |
|
626
|
+
# # +--------+----------------+-------------------+----------+----------------+
|
627
|
+
#
|
628
|
+
#
|
629
|
+
#
|
630
|
+
# @example Limit the generated routes with :only
|
631
|
+
# require 'hanami/router'
|
632
|
+
#
|
633
|
+
# Hanami::Router.new do
|
634
|
+
# resource 'identity', only: [:show, :new, :create]
|
635
|
+
# end
|
636
|
+
#
|
637
|
+
# # It generates:
|
638
|
+
# #
|
639
|
+
# # +--------+----------------+------------------+----------+----------------+
|
640
|
+
# # | Verb | Path | Action | Name | Named Route |
|
641
|
+
# # +--------+----------------+------------------+----------+----------------+
|
642
|
+
# # | GET | /identity | Identity::Show | :show | :identity |
|
643
|
+
# # | GET | /identity/new | Identity::New | :new | :new_identity |
|
644
|
+
# # | POST | /identity | Identity::Create | :create | :identity |
|
645
|
+
# # +--------+----------------+------------------+----------+----------------+
|
646
|
+
#
|
647
|
+
#
|
648
|
+
#
|
649
|
+
# @example Limit the generated routes with :except
|
650
|
+
# require 'hanami/router'
|
651
|
+
#
|
652
|
+
# Hanami::Router.new do
|
653
|
+
# resource 'identity', except: [:edit, :update, :destroy]
|
654
|
+
# end
|
655
|
+
#
|
656
|
+
# # It generates:
|
657
|
+
# #
|
658
|
+
# # +--------+----------------+------------------+----------+----------------+
|
659
|
+
# # | Verb | Path | Action | Name | Named Route |
|
660
|
+
# # +--------+----------------+------------------+----------+----------------+
|
661
|
+
# # | GET | /identity | Identity::Show | :show | :identity |
|
662
|
+
# # | GET | /identity/new | Identity::New | :new | :new_identity |
|
663
|
+
# # | POST | /identity | Identity::Create | :create | :identity |
|
664
|
+
# # +--------+----------------+------------------+----------+----------------+
|
665
|
+
#
|
666
|
+
#
|
667
|
+
#
|
668
|
+
# @example Additional single routes
|
669
|
+
# require 'hanami/router'
|
670
|
+
#
|
671
|
+
# Hanami::Router.new do
|
672
|
+
# resource 'identity', only: [] do
|
673
|
+
# member do
|
674
|
+
# patch 'activate'
|
675
|
+
# end
|
676
|
+
# end
|
677
|
+
# end
|
678
|
+
#
|
679
|
+
# # It generates:
|
680
|
+
# #
|
681
|
+
# # +--------+--------------------+--------------------+------+--------------------+
|
682
|
+
# # | Verb | Path | Action | Name | Named Route |
|
683
|
+
# # +--------+--------------------+--------------------+------+--------------------+
|
684
|
+
# # | PATCH | /identity/activate | Identity::Activate | | :activate_identity |
|
685
|
+
# # +--------+--------------------+--------------------+------+--------------------+
|
686
|
+
#
|
687
|
+
#
|
688
|
+
#
|
689
|
+
# @example Additional collection routes
|
690
|
+
# require 'hanami/router'
|
691
|
+
#
|
692
|
+
# Hanami::Router.new do
|
693
|
+
# resource 'identity', only: [] do
|
694
|
+
# collection do
|
695
|
+
# get 'keys'
|
696
|
+
# end
|
697
|
+
# end
|
698
|
+
# end
|
699
|
+
#
|
700
|
+
# # It generates:
|
701
|
+
# #
|
702
|
+
# # +------+----------------+----------------+------+----------------+
|
703
|
+
# # | Verb | Path | Action | Name | Named Route |
|
704
|
+
# # +------+----------------+----------------+------+----------------+
|
705
|
+
# # | GET | /identity/keys | Identity::Keys | | :keys_identity |
|
706
|
+
# # +------+----------------+----------------+------+----------------+
|
707
|
+
def resource(name, options = {}, &blk)
|
708
|
+
Routing::Resource.new(self, name, options.merge(separator: @router.action_separator), &blk)
|
709
|
+
end
|
710
|
+
|
711
|
+
# Defines a set of named routes for a plural RESTful resource.
|
712
|
+
# It has a built-in integration for Hanami::Controller.
|
713
|
+
#
|
714
|
+
# @param name [String] the name of the resource
|
715
|
+
# @param options [Hash] a set of options to customize the routes
|
716
|
+
# @option options [Array<Symbol>] :only a subset of the default routes
|
717
|
+
# that we want to generate
|
718
|
+
# @option options [Array<Symbol>] :except prevent the given routes to be
|
719
|
+
# generated
|
720
|
+
# @param blk [Proc] a block of code to generate additional routes
|
721
|
+
#
|
722
|
+
# @return [Hanami::Routing::Resources]
|
723
|
+
#
|
724
|
+
# @since 0.1.0
|
725
|
+
#
|
726
|
+
# @see Hanami::Routing::Resources
|
727
|
+
# @see Hanami::Routing::Resources::Action
|
728
|
+
# @see Hanami::Routing::Resource::Options
|
729
|
+
#
|
730
|
+
# @example Default usage
|
731
|
+
# require 'hanami/router'
|
732
|
+
#
|
733
|
+
# Hanami::Router.new do
|
734
|
+
# resources 'articles'
|
735
|
+
# end
|
736
|
+
#
|
737
|
+
# # It generates:
|
738
|
+
# #
|
739
|
+
# # +--------+--------------------+-------------------+----------+----------------+
|
740
|
+
# # | Verb | Path | Action | Name | Named Route |
|
741
|
+
# # +--------+--------------------+-------------------+----------+----------------+
|
742
|
+
# # | GET | /articles | Articles::Index | :index | :articles |
|
743
|
+
# # | GET | /articles/:id | Articles::Show | :show | :articles |
|
744
|
+
# # | GET | /articles/new | Articles::New | :new | :new_articles |
|
745
|
+
# # | POST | /articles | Articles::Create | :create | :articles |
|
746
|
+
# # | GET | /articles/:id/edit | Articles::Edit | :edit | :edit_articles |
|
747
|
+
# # | PATCH | /articles/:id | Articles::Update | :update | :articles |
|
748
|
+
# # | DELETE | /articles/:id | Articles::Destroy | :destroy | :articles |
|
749
|
+
# # +--------+--------------------+-------------------+----------+----------------+
|
750
|
+
#
|
751
|
+
#
|
752
|
+
#
|
753
|
+
# @example Limit the generated routes with :only
|
754
|
+
# require 'hanami/router'
|
755
|
+
#
|
756
|
+
# Hanami::Router.new do
|
757
|
+
# resources 'articles', only: [:index]
|
758
|
+
# end
|
759
|
+
#
|
760
|
+
# # It generates:
|
761
|
+
# #
|
762
|
+
# # +------+-----------+-----------------+--------+-------------+
|
763
|
+
# # | Verb | Path | Action | Name | Named Route |
|
764
|
+
# # +------+-----------+-----------------+--------+-------------+
|
765
|
+
# # | GET | /articles | Articles::Index | :index | :articles |
|
766
|
+
# # +------+-----------+-----------------+--------+-------------+
|
767
|
+
#
|
768
|
+
#
|
769
|
+
#
|
770
|
+
# @example Limit the generated routes with :except
|
771
|
+
# require 'hanami/router'
|
772
|
+
#
|
773
|
+
# Hanami::Router.new do
|
774
|
+
# resources 'articles', except: [:edit, :update]
|
775
|
+
# end
|
776
|
+
#
|
777
|
+
# # It generates:
|
778
|
+
# #
|
779
|
+
# # +--------+--------------------+-------------------+----------+----------------+
|
780
|
+
# # | Verb | Path | Action | Name | Named Route |
|
781
|
+
# # +--------+--------------------+-------------------+----------+----------------+
|
782
|
+
# # | GET | /articles | Articles::Index | :index | :articles |
|
783
|
+
# # | GET | /articles/:id | Articles::Show | :show | :articles |
|
784
|
+
# # | GET | /articles/new | Articles::New | :new | :new_articles |
|
785
|
+
# # | POST | /articles | Articles::Create | :create | :articles |
|
786
|
+
# # | DELETE | /articles/:id | Articles::Destroy | :destroy | :articles |
|
787
|
+
# # +--------+--------------------+-------------------+----------+----------------+
|
788
|
+
#
|
789
|
+
#
|
790
|
+
#
|
791
|
+
# @example Additional single routes
|
792
|
+
# require 'hanami/router'
|
793
|
+
#
|
794
|
+
# Hanami::Router.new do
|
795
|
+
# resources 'articles', only: [] do
|
796
|
+
# member do
|
797
|
+
# patch 'publish'
|
798
|
+
# end
|
799
|
+
# end
|
800
|
+
# end
|
801
|
+
#
|
802
|
+
# # It generates:
|
803
|
+
# #
|
804
|
+
# # +--------+-----------------------+-------------------+------+-------------------+
|
805
|
+
# # | Verb | Path | Action | Name | Named Route |
|
806
|
+
# # +--------+-----------------------+-------------------+------+-------------------+
|
807
|
+
# # | PATCH | /articles/:id/publish | Articles::Publish | | :publish_articles |
|
808
|
+
# # +--------+-----------------------+-------------------+------+-------------------+
|
809
|
+
#
|
810
|
+
#
|
811
|
+
#
|
812
|
+
# @example Additional collection routes
|
813
|
+
# require 'hanami/router'
|
814
|
+
#
|
815
|
+
# Hanami::Router.new do
|
816
|
+
# resources 'articles', only: [] do
|
817
|
+
# collection do
|
818
|
+
# get 'search'
|
819
|
+
# end
|
820
|
+
# end
|
821
|
+
# end
|
822
|
+
#
|
823
|
+
# # It generates:
|
824
|
+
# #
|
825
|
+
# # +------+------------------+------------------+------+------------------+
|
826
|
+
# # | Verb | Path | Action | Name | Named Route |
|
827
|
+
# # +------+------------------+------------------+------+------------------+
|
828
|
+
# # | GET | /articles/search | Articles::Search | | :search_articles |
|
829
|
+
# # +------+------------------+------------------+------+------------------+
|
830
|
+
def resources(name, options = {}, &blk)
|
831
|
+
Routing::Resources.new(self, name, options.merge(separator: @router.action_separator), &blk)
|
832
|
+
end
|
833
|
+
|
834
|
+
# Mount a Rack application at the specified path.
|
835
|
+
# All the requests starting with the specified path, will be forwarded to
|
836
|
+
# the given application.
|
837
|
+
#
|
838
|
+
# All the other methods (eg #get) support callable objects, but they
|
839
|
+
# restrict the range of the acceptable HTTP verb. Mounting an application
|
840
|
+
# with #mount doesn't apply this kind of restriction at the router level,
|
841
|
+
# but let the application to decide.
|
842
|
+
#
|
843
|
+
# @param app [#call] a class or an object that responds to #call
|
844
|
+
# @param options [Hash] the options to customize the mount
|
845
|
+
# @option options [:at] the relative path where to mount the app
|
846
|
+
#
|
847
|
+
# @since 0.1.1
|
848
|
+
#
|
849
|
+
# @example Basic usage
|
850
|
+
# require 'hanami/router'
|
851
|
+
#
|
852
|
+
# Hanami::Router.new do
|
853
|
+
# mount Api::App.new, at: '/api'
|
854
|
+
# end
|
855
|
+
#
|
856
|
+
# # Requests:
|
857
|
+
# #
|
858
|
+
# # GET /api # => 200
|
859
|
+
# # GET /api/articles # => 200
|
860
|
+
# # POST /api/articles # => 200
|
861
|
+
# # GET /api/unknown # => 404
|
862
|
+
#
|
863
|
+
# @example Difference between #get and #mount
|
864
|
+
# require 'hanami/router'
|
865
|
+
#
|
866
|
+
# Hanami::Router.new do
|
867
|
+
# get '/rack1', to: RackOne.new
|
868
|
+
# mount RackTwo.new, at: '/rack2'
|
869
|
+
# end
|
870
|
+
#
|
871
|
+
# # Requests:
|
872
|
+
# #
|
873
|
+
# # # /rack1 will only accept GET
|
874
|
+
# # GET /rack1 # => 200 (RackOne.new)
|
875
|
+
# # POST /rack1 # => 405
|
876
|
+
# #
|
877
|
+
# # # /rack2 accepts all the verbs and delegate the decision to RackTwo
|
878
|
+
# # GET /rack2 # => 200 (RackTwo.new)
|
879
|
+
# # POST /rack2 # => 200 (RackTwo.new)
|
880
|
+
#
|
881
|
+
# @example Types of mountable applications
|
882
|
+
# require 'hanami/router'
|
883
|
+
#
|
884
|
+
# class RackOne
|
885
|
+
# def self.call(env)
|
886
|
+
# end
|
887
|
+
# end
|
888
|
+
#
|
889
|
+
# class RackTwo
|
890
|
+
# def call(env)
|
891
|
+
# end
|
892
|
+
# end
|
893
|
+
#
|
894
|
+
# class RackThree
|
895
|
+
# def call(env)
|
896
|
+
# end
|
897
|
+
# end
|
898
|
+
#
|
899
|
+
# module Dashboard
|
900
|
+
# class Index
|
901
|
+
# def call(env)
|
902
|
+
# end
|
903
|
+
# end
|
904
|
+
# end
|
905
|
+
#
|
906
|
+
# Hanami::Router.new do
|
907
|
+
# mount RackOne, at: '/rack1'
|
908
|
+
# mount RackTwo, at: '/rack2'
|
909
|
+
# mount RackThree.new, at: '/rack3'
|
910
|
+
# mount ->(env) {[200, {}, ['Rack Four']]}, at: '/rack4'
|
911
|
+
# mount 'dashboard#index', at: '/dashboard'
|
912
|
+
# end
|
913
|
+
#
|
914
|
+
# # 1. RackOne is used as it is (class), because it respond to .call
|
915
|
+
# # 2. RackTwo is initialized, because it respond to #call
|
916
|
+
# # 3. RackThree is used as it is (object), because it respond to #call
|
917
|
+
# # 4. That Proc is used as it is, because it respond to #call
|
918
|
+
# # 5. That string is resolved as Dashboard::Index (Hanami::Controller)
|
919
|
+
def mount(app, options)
|
920
|
+
@router.mount(app, options)
|
921
|
+
end
|
922
|
+
|
923
|
+
# Resolve the given Rack env to a registered endpoint and invoke it.
|
924
|
+
#
|
925
|
+
# @param env [Hash] a Rack env instance
|
926
|
+
#
|
927
|
+
# @return [Rack::Response, Array]
|
928
|
+
#
|
929
|
+
# @since 0.1.0
|
930
|
+
def call(env)
|
931
|
+
@router.call(env)
|
932
|
+
end
|
933
|
+
|
934
|
+
# Recognize the given env, path, or name and return a route for testing
|
935
|
+
# inspection.
|
936
|
+
#
|
937
|
+
# If the route cannot be recognized, it still returns an object for testing
|
938
|
+
# inspection.
|
939
|
+
#
|
940
|
+
# @param env [Hash, String, Symbol] Rack env, path or route name
|
941
|
+
# @param options [Hash] a set of options for Rack env or route params
|
942
|
+
# @param params [Hash] a set of params
|
943
|
+
#
|
944
|
+
# @return [Hanami::Routing::RecognizedRoute] the recognized route
|
945
|
+
#
|
946
|
+
# @since 0.5.0
|
947
|
+
#
|
948
|
+
# @see Hanami::Router#env_for
|
949
|
+
# @see Hanami::Routing::RecognizedRoute
|
950
|
+
#
|
951
|
+
# @example Successful Path Recognition
|
952
|
+
# require 'hanami/router'
|
953
|
+
#
|
954
|
+
# router = Hanami::Router.new do
|
955
|
+
# get '/books/:id', to: 'books#show', as: :book
|
956
|
+
# end
|
957
|
+
#
|
958
|
+
# route = router.recognize('/books/23')
|
959
|
+
# route.verb # => "GET" (default)
|
960
|
+
# route.routable? # => true
|
961
|
+
# route.params # => {:id=>"23"}
|
962
|
+
#
|
963
|
+
# @example Successful Rack Env Recognition
|
964
|
+
# require 'hanami/router'
|
965
|
+
#
|
966
|
+
# router = Hanami::Router.new do
|
967
|
+
# get '/books/:id', to: 'books#show', as: :book
|
968
|
+
# end
|
969
|
+
#
|
970
|
+
# route = router.recognize(Rack::MockRequest.env_for('/books/23'))
|
971
|
+
# route.verb # => "GET" (default)
|
972
|
+
# route.routable? # => true
|
973
|
+
# route.params # => {:id=>"23"}
|
974
|
+
#
|
975
|
+
# @example Successful Named Route Recognition
|
976
|
+
# require 'hanami/router'
|
977
|
+
#
|
978
|
+
# router = Hanami::Router.new do
|
979
|
+
# get '/books/:id', to: 'books#show', as: :book
|
980
|
+
# end
|
981
|
+
#
|
982
|
+
# route = router.recognize(:book, id: 23)
|
983
|
+
# route.verb # => "GET" (default)
|
984
|
+
# route.routable? # => true
|
985
|
+
# route.params # => {:id=>"23"}
|
986
|
+
#
|
987
|
+
# @example Failing Recognition For Unknown Path
|
988
|
+
# require 'hanami/router'
|
989
|
+
#
|
990
|
+
# router = Hanami::Router.new do
|
991
|
+
# get '/books/:id', to: 'books#show', as: :book
|
992
|
+
# end
|
993
|
+
#
|
994
|
+
# route = router.recognize('/books')
|
995
|
+
# route.verb # => "GET" (default)
|
996
|
+
# route.routable? # => false
|
997
|
+
#
|
998
|
+
# @example Failing Recognition For Path With Wrong HTTP Verb
|
999
|
+
# require 'hanami/router'
|
1000
|
+
#
|
1001
|
+
# router = Hanami::Router.new do
|
1002
|
+
# get '/books/:id', to: 'books#show', as: :book
|
1003
|
+
# end
|
1004
|
+
#
|
1005
|
+
# route = router.recognize('/books/23', method: :post)
|
1006
|
+
# route.verb # => "POST"
|
1007
|
+
# route.routable? # => false
|
1008
|
+
#
|
1009
|
+
# @example Failing Recognition For Rack Env With Wrong HTTP Verb
|
1010
|
+
# require 'hanami/router'
|
1011
|
+
#
|
1012
|
+
# router = Hanami::Router.new do
|
1013
|
+
# get '/books/:id', to: 'books#show', as: :book
|
1014
|
+
# end
|
1015
|
+
#
|
1016
|
+
# route = router.recognize(Rack::MockRequest.env_for('/books/23', method: :post))
|
1017
|
+
# route.verb # => "POST"
|
1018
|
+
# route.routable? # => false
|
1019
|
+
#
|
1020
|
+
# @example Failing Recognition Named Route With Wrong Params
|
1021
|
+
# require 'hanami/router'
|
1022
|
+
#
|
1023
|
+
# router = Hanami::Router.new do
|
1024
|
+
# get '/books/:id', to: 'books#show', as: :book
|
1025
|
+
# end
|
1026
|
+
#
|
1027
|
+
# route = router.recognize(:book)
|
1028
|
+
# route.verb # => "GET" (default)
|
1029
|
+
# route.routable? # => false
|
1030
|
+
#
|
1031
|
+
# @example Failing Recognition Named Route With Wrong HTTP Verb
|
1032
|
+
# require 'hanami/router'
|
1033
|
+
#
|
1034
|
+
# router = Hanami::Router.new do
|
1035
|
+
# get '/books/:id', to: 'books#show', as: :book
|
1036
|
+
# end
|
1037
|
+
#
|
1038
|
+
# route = router.recognize(:book, {method: :post}, {id: 1})
|
1039
|
+
# route.verb # => "POST"
|
1040
|
+
# route.routable? # => false
|
1041
|
+
# route.params # => {:id=>"1"}
|
1042
|
+
def recognize(env, options = {}, params = nil)
|
1043
|
+
require 'hanami/routing/recognized_route'
|
1044
|
+
|
1045
|
+
env = env_for(env, options, params)
|
1046
|
+
responses, _ = *@router.recognize(env)
|
1047
|
+
|
1048
|
+
Routing::RecognizedRoute.new(
|
1049
|
+
responses.nil? ? responses : responses.first,
|
1050
|
+
env, @router)
|
1051
|
+
end
|
1052
|
+
|
1053
|
+
# Generate an relative URL for a specified named route.
|
1054
|
+
# The additional arguments will be used to compose the relative URL - in
|
1055
|
+
# case it has tokens to match - and for compose the query string.
|
1056
|
+
#
|
1057
|
+
# @param route [Symbol] the route name
|
1058
|
+
#
|
1059
|
+
# @return [String]
|
1060
|
+
#
|
1061
|
+
# @raise [Hanami::Routing::InvalidRouteException] when the router fails to
|
1062
|
+
# recognize a route, because of the given arguments.
|
1063
|
+
#
|
1064
|
+
# @since 0.1.0
|
1065
|
+
#
|
1066
|
+
# @example
|
1067
|
+
# require 'hanami/router'
|
1068
|
+
#
|
1069
|
+
# router = Hanami::Router.new(scheme: 'https', host: 'hanamirb.org')
|
1070
|
+
# router.get '/login', to: 'sessions#new', as: :login
|
1071
|
+
# router.get '/:name', to: 'frameworks#show', as: :framework
|
1072
|
+
#
|
1073
|
+
# router.path(:login) # => "/login"
|
1074
|
+
# router.path(:login, return_to: '/dashboard') # => "/login?return_to=%2Fdashboard"
|
1075
|
+
# router.path(:framework, name: 'router') # => "/router"
|
1076
|
+
def path(route, *args)
|
1077
|
+
@router.path(route, *args)
|
1078
|
+
end
|
1079
|
+
|
1080
|
+
# Generate a URL for a specified named route.
|
1081
|
+
# The additional arguments will be used to compose the relative URL - in
|
1082
|
+
# case it has tokens to match - and for compose the query string.
|
1083
|
+
#
|
1084
|
+
# @param route [Symbol] the route name
|
1085
|
+
#
|
1086
|
+
# @return [String]
|
1087
|
+
#
|
1088
|
+
# @raise [Hanami::Routing::InvalidRouteException] when the router fails to
|
1089
|
+
# recognize a route, because of the given arguments.
|
1090
|
+
#
|
1091
|
+
# @since 0.1.0
|
1092
|
+
#
|
1093
|
+
# @example
|
1094
|
+
# require 'hanami/router'
|
1095
|
+
#
|
1096
|
+
# router = Hanami::Router.new(scheme: 'https', host: 'hanamirb.org')
|
1097
|
+
# router.get '/login', to: 'sessions#new', as: :login
|
1098
|
+
# router.get '/:name', to: 'frameworks#show', as: :framework
|
1099
|
+
#
|
1100
|
+
# router.url(:login) # => "https://hanamirb.org/login"
|
1101
|
+
# router.url(:login, return_to: '/dashboard') # => "https://hanamirb.org/login?return_to=%2Fdashboard"
|
1102
|
+
# router.url(:framework, name: 'router') # => "https://hanamirb.org/router"
|
1103
|
+
def url(route, *args)
|
1104
|
+
@router.url(route, *args)
|
1105
|
+
end
|
1106
|
+
|
1107
|
+
# Returns an routes inspector
|
1108
|
+
#
|
1109
|
+
# @since 0.2.0
|
1110
|
+
#
|
1111
|
+
# @see Hanami::Routing::RoutesInspector
|
1112
|
+
#
|
1113
|
+
# @example
|
1114
|
+
# require 'hanami/router'
|
1115
|
+
#
|
1116
|
+
# router = Hanami::Router.new do
|
1117
|
+
# get '/', to: 'home#index'
|
1118
|
+
# get '/login', to: 'sessions#new', as: :login
|
1119
|
+
# post '/login', to: 'sessions#create'
|
1120
|
+
# delete '/logout', to: 'sessions#destroy', as: :logout
|
1121
|
+
# end
|
1122
|
+
#
|
1123
|
+
# puts router.inspector
|
1124
|
+
# # => GET, HEAD / Home::Index
|
1125
|
+
# login GET, HEAD /login Sessions::New
|
1126
|
+
# POST /login Sessions::Create
|
1127
|
+
# logout GET, HEAD /logout Sessions::Destroy
|
1128
|
+
def inspector
|
1129
|
+
require 'hanami/routing/routes_inspector'
|
1130
|
+
Routing::RoutesInspector.new(@router.routes)
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
protected
|
1134
|
+
|
1135
|
+
# Fabricate Rack env for the given Rack env, path or named route
|
1136
|
+
#
|
1137
|
+
# @param env [Hash, String, Symbol] Rack env, path or route name
|
1138
|
+
# @param options [Hash] a set of options for Rack env or route params
|
1139
|
+
# @param params [Hash] a set of params
|
1140
|
+
#
|
1141
|
+
# @return [Hash] Rack env
|
1142
|
+
#
|
1143
|
+
# @since 0.5.0
|
1144
|
+
# @api private
|
1145
|
+
#
|
1146
|
+
# @see Hanami::Router#recognize
|
1147
|
+
# @see http://www.rubydoc.info/github/rack/rack/Rack%2FMockRequest.env_for
|
1148
|
+
def env_for(env, options = {}, params = nil)
|
1149
|
+
env = case env
|
1150
|
+
when String
|
1151
|
+
Rack::MockRequest.env_for(env, options)
|
1152
|
+
when Symbol
|
1153
|
+
begin
|
1154
|
+
url = path(env, params || options)
|
1155
|
+
return env_for(url, options)
|
1156
|
+
rescue Hanami::Routing::InvalidRouteException
|
1157
|
+
{}
|
1158
|
+
end
|
1159
|
+
else
|
1160
|
+
env
|
1161
|
+
end
|
1162
|
+
end
|
6
1163
|
end
|
7
1164
|
end
|