hanami-router 1.3.1 → 2.0.0.alpha4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +97 -443
  5. data/hanami-router.gemspec +23 -19
  6. data/lib/hanami/middleware/body_parser.rb +21 -15
  7. data/lib/hanami/middleware/body_parser/class_interface.rb +62 -56
  8. data/lib/hanami/middleware/body_parser/errors.rb +7 -4
  9. data/lib/hanami/middleware/body_parser/json_parser.rb +9 -7
  10. data/lib/hanami/middleware/body_parser/parser.rb +58 -0
  11. data/lib/hanami/middleware/error.rb +16 -0
  12. data/lib/hanami/router.rb +548 -957
  13. data/lib/hanami/router/block.rb +88 -0
  14. data/lib/hanami/router/error.rb +67 -0
  15. data/lib/hanami/router/node.rb +91 -0
  16. data/lib/hanami/router/params.rb +35 -0
  17. data/lib/hanami/router/prefix.rb +67 -0
  18. data/lib/hanami/router/recognized_route.rb +92 -0
  19. data/lib/hanami/router/redirect.rb +28 -0
  20. data/lib/hanami/router/segment.rb +19 -0
  21. data/lib/hanami/router/trie.rb +63 -0
  22. data/lib/hanami/router/url_helpers.rb +40 -0
  23. data/lib/hanami/router/version.rb +4 -1
  24. metadata +61 -41
  25. data/lib/hanami-router.rb +0 -1
  26. data/lib/hanami/routing/endpoint.rb +0 -195
  27. data/lib/hanami/routing/endpoint_resolver.rb +0 -238
  28. data/lib/hanami/routing/error.rb +0 -7
  29. data/lib/hanami/routing/force_ssl.rb +0 -212
  30. data/lib/hanami/routing/http_router.rb +0 -220
  31. data/lib/hanami/routing/http_router_monkey_patch.rb +0 -38
  32. data/lib/hanami/routing/namespace.rb +0 -98
  33. data/lib/hanami/routing/parsers.rb +0 -113
  34. data/lib/hanami/routing/parsing/json_parser.rb +0 -33
  35. data/lib/hanami/routing/parsing/parser.rb +0 -61
  36. data/lib/hanami/routing/recognized_route.rb +0 -219
  37. data/lib/hanami/routing/resource.rb +0 -119
  38. data/lib/hanami/routing/resource/action.rb +0 -402
  39. data/lib/hanami/routing/resource/nested.rb +0 -41
  40. data/lib/hanami/routing/resource/options.rb +0 -74
  41. data/lib/hanami/routing/resources.rb +0 -48
  42. data/lib/hanami/routing/resources/action.rb +0 -156
  43. data/lib/hanami/routing/route.rb +0 -71
  44. data/lib/hanami/routing/routes_inspector.rb +0 -221
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8808a8d351f9112f87f1df9e75be976fed071962c8e263f2d6aa6da83a715fd5
4
- data.tar.gz: d055ac20d1319ba913521324890b742344e012705b725fb50e7406ae41a46162
3
+ metadata.gz: a855344a3dcd5d256259802c803eae8c7e41aeaeda1f108855d668458ec6c32d
4
+ data.tar.gz: 79f333141e1453d4bd4c4f4e09fd41e54bb2cab94d3889268a6a765ec9d52a16
5
5
  SHA512:
6
- metadata.gz: f93fb103dd4e81fb5548e737866845836e0e21daf39b7b920c2ee2fe623cb015342df053d327f3ad406d64c65fc6deeeb5f62476be5dbf40ebb81d2301d5b7e5
7
- data.tar.gz: 04a405c315e50825027604176badd96d9f136b830b57cf7223e4c190fad9f0bb5ab91488b7da8c801c86fd41033b5f97fe14c9a170b4bdde7271479f8fcc47ef
6
+ metadata.gz: 1dc165c33814c2869852526395de10368b14918b09af6d4dbac56e2610f6ae8dcd8e78eb1034e0b858a48e30d80470976af692e1a235cb58a54561c1dc7393b7
7
+ data.tar.gz: a73e23f4d0875143660610be0b8e349be143dcdbed62a258070d0b1973ee876c57b68ae8df90f1fc786bf9e689ec40a16ced52053dbd16f893efe4b62fa70e85
@@ -1,6 +1,50 @@
1
1
  # Hanami::Router
2
2
  Rack compatible HTTP router for Ruby
3
3
 
4
+ ## v2.0.0.alpha4 - 2021-01-16
5
+ ### Added
6
+ - [Luca Guidi] Official support for MRI 3.0
7
+ - [Luca Guidi] Introduced `Hanami::Middleware::BodyParser::Parser` as superclass for body parsers
8
+ - [Paweł Świątkowski] Added `not_found:` option to `Hanami::Router#initialize` to customize HTTP 404 status
9
+
10
+ ## v2.0.0.alpha3 - 2020-05-20
11
+ ### Fixed
12
+ - [Luca Guidi] `Hanami::Router#initialize` do not yield block if not given
13
+ - [Luca Guidi] Ensure to not accidentally cache response headers for HTTP 404 and 405
14
+ - [Luca Guidi] Ensure scoped root to not be added as trailing slash
15
+
16
+ ## v2.0.0.alpha2 - 2020-02-19
17
+ ### Added
18
+ - [Luca Guidi] Block syntax. Routes definition accept a block which returning value is the body of the Rack response.
19
+ - [Luca Guidi] Added `resolver:` option to `Hanami::Router#initialize` to provide your own strategy to load endpoints.
20
+
21
+ ### Changed
22
+ - [Luca Guidi] Removed `Hanami::Router#resource` and `#resources`.
23
+ - [Luca Guidi] Removed loading of routes endpoints.
24
+ - [Luca Guidi] Removed `inflector:` from `Hanami::Router#initialize`
25
+ - [Luca Guidi] Removed `scheme:`, `host:`, `port:` from `Hanami::Router#initialize`, use `base_url:` instead.
26
+
27
+ ## v2.0.0.alpha1 - 2019-01-30
28
+ ### Added
29
+ - [Luca Guidi] Introduce `Hanami::Router#scope` to support single routing tier for Hanami
30
+ - [Semyon Pupkov] Added `inflector:` option for `Hanami::Router#initialize` based on `dry-inflector`
31
+
32
+ ### Changed
33
+ - [Luca Guidi] Drop support for Ruby: MRI 2.3, and 2.4.
34
+ - [Luca Guidi] Renamed `Hanami::Router#namespace` => `#prefix`
35
+ - [Gustavo Caso] Remove body cleanup for `HEAD` requests
36
+ - [Semyon Pupkov] Remove the ability to force SSL (`force_ssl:` option for `Hanami::Router#initialize`)
37
+ - [Gustavo Caso] Remove router body parsers (`parsers:` option for `Hanami::Router#initialize`)
38
+ - [Luca Guidi] Globbed path requires named capture (was `get "/files/*"`, now is `get "/files/*names"`)
39
+ - [Luca Guidi] Router is frozen after initialization
40
+ - [Luca Guidi] All the code base respects the frozen string pragma
41
+ - [Luca Guidi] `Hanami::Router#initialize` requires `configuration:` option if routes endpoints are `Hanami::Action` subclasses
42
+
43
+ ## v1.3.2 - 2019-02-13
44
+ ### Added
45
+ - [Luca Guidi] Official support for Ruby: MRI 2.7
46
+ - [Luca Guidi] Support `rack` 2.1
47
+
4
48
  ## v1.3.1 - 2019-01-18
5
49
  ### Added
6
50
  - [Luca Guidi] Official support for Ruby: MRI 2.6
data/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- Copyright © 2014-2017 Luca Guidi
1
+ Copyright © 2014-2021 Luca Guidi
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -5,9 +5,8 @@ Rack compatible, lightweight and fast HTTP Router for Ruby and [Hanami](http://h
5
5
  ## Status
6
6
 
7
7
  [![Gem Version](https://badge.fury.io/rb/hanami-router.svg)](https://badge.fury.io/rb/hanami-router)
8
- [![TravisCI](https://travis-ci.org/hanami/router.svg?branch=master)](https://travis-ci.org/hanami/router)
9
- [![CircleCI](https://circleci.com/gh/hanami/router/tree/master.svg?style=svg)](https://circleci.com/gh/hanami/router/tree/master)
10
- [![Test Coverage](https://codecov.io/gh/hanami/router/branch/master/graph/badge.svg)](https://codecov.io/gh/hanami/router)
8
+ [![CI](https://github.com/hanami/router/workflows/ci/badge.svg?branch=unstable)](https://github.com/hanami/router/actions?query=workflow%3Aci+branch%3Aunstable)
9
+ [![Test Coverage](https://codecov.io/gh/hanami/router/branch/unstable/graph/badge.svg)](https://codecov.io/gh/hanami/router)
11
10
  [![Depfu](https://badges.depfu.com/badges/5f6b8e8fa3b0d082539f0b0f84d55960/overview.svg)](https://depfu.com/github/hanami/router?project=Bundler)
12
11
  [![Inline Docs](http://inch-ci.org/github/hanami/router.svg)](http://inch-ci.org/github/hanami/router)
13
12
 
@@ -22,7 +21,7 @@ Rack compatible, lightweight and fast HTTP Router for Ruby and [Hanami](http://h
22
21
 
23
22
  ## Rubies
24
23
 
25
- __Hanami::Router__ supports Ruby (MRI) 2.3+, JRuby 9.1.5.0+
24
+ __Hanami::Router__ supports Ruby (MRI) 2.6+
26
25
 
27
26
 
28
27
  ## Installation
@@ -30,7 +29,7 @@ __Hanami::Router__ supports Ruby (MRI) 2.3+, JRuby 9.1.5.0+
30
29
  Add this line to your application's Gemfile:
31
30
 
32
31
  ```ruby
33
- gem 'hanami-router'
32
+ gem "hanami-router"
34
33
  ```
35
34
 
36
35
  And then execute:
@@ -47,14 +46,23 @@ $ gem install hanami-router
47
46
 
48
47
  ## Getting Started
49
48
 
49
+ Create a file named `config.ru`
50
+
50
51
  ```ruby
51
- require 'hanami/router'
52
+ # frozen_string_literal: true
53
+ require "hanami/router"
52
54
 
53
55
  app = Hanami::Router.new do
54
- get '/', to: ->(env) { [200, {}, ['Welcome to Hanami::Router!']] }
56
+ get "/", to: ->(env) { [200, {}, ["Welcome to Hanami!"]] }
55
57
  end
56
58
 
57
- Rack::Server.start app: app, Port: 2300
59
+ run app
60
+ ```
61
+
62
+ From the shell:
63
+
64
+ ```shell
65
+ $ bundle exec rackup
58
66
  ```
59
67
 
60
68
  ## Usage
@@ -68,144 +76,114 @@ For the standalone usage, it supports neat features:
68
76
 
69
77
  ```ruby
70
78
  Hanami::Router.new do
71
- root to: ->(env) { [200, {}, ['Hello']] }
72
- get '/lambda', to: ->(env) { [200, {}, ['World']] }
73
- get '/dashboard', to: Dashboard::Index
74
- get '/rack-app', to: RackApp.new
75
- get '/flowers', to: 'flowers#index'
76
- get '/flowers/:id', to: 'flowers#show'
77
-
78
- redirect '/legacy', to: '/'
79
-
80
- mount Api::App, at: '/api'
81
-
82
- namespace 'admin' do
83
- get '/users', to: Users::Index
84
- end
85
-
86
- resource 'identity' do
87
- member do
88
- get '/avatar'
89
- end
79
+ root to: ->(env) { [200, {}, ["Hello"]] }
80
+ get "/lambda", to: ->(env) { [200, {}, ["World"]] }
81
+ get "/dashboard", to: Dashboard::Index
82
+ get "/rack-app", to: RackApp.new
90
83
 
91
- collection do
92
- get '/api_keys'
93
- end
94
- end
84
+ redirect "/legacy", to: "/"
95
85
 
96
- resources 'robots' do
97
- member do
98
- patch '/activate'
99
- end
86
+ mount Api::App, at: "/api"
100
87
 
101
- collection do
102
- get '/search'
103
- end
88
+ scope "admin" do
89
+ get "/users", to: Users::Index
104
90
  end
105
91
  end
106
92
  ```
107
93
 
108
-
109
-
110
94
  ### Fixed string matching:
111
95
 
112
96
  ```ruby
113
- router = Hanami::Router.new
114
- router.get '/hanami', to: ->(env) { [200, {}, ['Hello from Hanami!']] }
97
+ Hanami::Router.new do
98
+ get "/hanami", to: ->(env) { [200, {}, ["Hello from Hanami!"]] }
99
+ end
115
100
  ```
116
101
 
117
-
118
-
119
102
  ### String matching with variables:
120
103
 
121
104
  ```ruby
122
- router = Hanami::Router.new
123
- router.get '/flowers/:id', to: ->(env) { [200, {}, ["Hello from Flower no. #{ env['router.params'][:id] }!"]] }
105
+ Hanami::Router.new do
106
+ get "/flowers/:id", to: ->(env) { [200, {}, ["Hello from Flower no. #{ env["router.params"][:id] }!"]] }
107
+ end
124
108
  ```
125
109
 
126
-
127
-
128
110
  ### Variables Constraints:
129
111
 
130
112
  ```ruby
131
- router = Hanami::Router.new
132
- router.get '/flowers/:id', id: /\d+/, to: ->(env) { [200, {}, [":id must be a number!"]] }
113
+ Hanami::Router.new do
114
+ get "/flowers/:id", id: /\d+/, to: ->(env) { [200, {}, [":id must be a number!"]] }
115
+ end
133
116
  ```
134
117
 
135
-
136
-
137
118
  ### String matching with globbing:
138
119
 
139
120
  ```ruby
140
- router = Hanami::Router.new
141
- router.get '/*', to: ->(env) { [200, {}, ["This is catch all: #{ env['router.params'].inspect }!"]] }
121
+ Hanami::Router.new do
122
+ get "/*match", to: ->(env) { [200, {}, ["This is catch all: #{ env["router.params"].inspect }!"]] }
123
+ end
142
124
  ```
143
125
 
144
-
145
-
146
126
  ### String matching with optional tokens:
147
127
 
148
128
  ```ruby
149
- router = Hanami::Router.new
150
- router.get '/hanami(.:format)' to: ->(env) { [200, {}, ["You've requested #{ env['router.params'][:format] }!"]] }
129
+ Hanami::Router.new do
130
+ get "/hanami(.:format)" to: ->(env) { [200, {}, ["You"ve requested #{ env["router.params"][:format] }!"]] }
131
+ end
151
132
  ```
152
133
 
153
-
154
-
155
134
  ### Support for the most common HTTP methods:
156
135
 
157
136
  ```ruby
158
- router = Hanami::Router.new
159
- endpoint = ->(env) { [200, {}, ['Hello from Hanami!']] }
160
-
161
- router.get '/hanami', to: endpoint
162
- router.post '/hanami', to: endpoint
163
- router.put '/hanami', to: endpoint
164
- router.patch '/hanami', to: endpoint
165
- router.delete '/hanami', to: endpoint
166
- router.trace '/hanami', to: endpoint
167
- ```
168
-
137
+ endpoint = ->(env) { [200, {}, ["Hello from Hanami!"]] }
169
138
 
139
+ Hanami::Router.new do
140
+ get "/hanami", to: endpoint
141
+ post "/hanami", to: endpoint
142
+ put "/hanami", to: endpoint
143
+ patch "/hanami", to: endpoint
144
+ delete "/hanami", to: endpoint
145
+ trace "/hanami", to: endpoint
146
+ options "/hanami", to: endpoint
147
+ end
148
+ ```
170
149
 
171
150
  ### Root:
172
151
 
173
152
  ```ruby
174
- router = Hanami::Router.new
175
- router.root to: ->(env) { [200, {}, ['Hello from Hanami!']] }
153
+ Hanami::Router.new do
154
+ root to: ->(env) { [200, {}, ["Hello from Hanami!"]] }
155
+ end
176
156
  ```
177
157
 
178
-
179
-
180
158
  ### Redirect:
181
159
 
182
160
  ```ruby
183
- router = Hanami::Router.new
184
- router.get '/redirect_destination', to: ->(env) { [200, {}, ['Redirect destination!']] }
185
- router.redirect '/legacy', to: '/redirect_destination'
161
+ Hanami::Router.new do
162
+ get "/redirect_destination", to: ->(env) { [200, {}, ["Redirect destination!"]] }
163
+ redirect "/legacy", to: "/redirect_destination"
164
+ end
186
165
  ```
187
166
 
188
-
189
-
190
167
  ### Named routes:
191
168
 
192
169
  ```ruby
193
- router = Hanami::Router.new(scheme: 'https', host: 'hanamirb.org')
194
- router.get '/hanami', to: ->(env) { [200, {}, ['Hello from Hanami!']] }, as: :hanami
170
+ router = Hanami::Router.new(scheme: "https", host: "hanamirb.org") do
171
+ get "/hanami", to: ->(env) { [200, {}, ["Hello from Hanami!"]] }, as: :hanami
172
+ end
195
173
 
196
174
  router.path(:hanami) # => "/hanami"
197
175
  router.url(:hanami) # => "https://hanamirb.org/hanami"
198
176
  ```
199
177
 
200
178
 
201
-
202
- ### Namespaced routes:
179
+ ### Scopes:
203
180
 
204
181
  ```ruby
205
- router = Hanami::Router.new
206
- router.namespace 'animals' do
207
- namespace 'mammals' do
208
- get '/cats', to: ->(env) { [200, {}, ['Meow!']] }, as: :cats
182
+ router = Hanami::Router.new do
183
+ scope "animals" do
184
+ scope "mammals" do
185
+ get "/cats", to: ->(env) { [200, {}, ["Meow!"]] }, as: :cats
186
+ end
209
187
  end
210
188
  end
211
189
 
@@ -218,364 +196,39 @@ router.path(:animals_mammals_cats) # => "/animals/mammals/cats"
218
196
 
219
197
  ### Mount Rack applications:
220
198
 
199
+ Mounting a Rack application will forward all kind of HTTP requests to the app,
200
+ when the request path matches the `at:` path.
201
+
221
202
  ```ruby
222
203
  Hanami::Router.new do
223
- mount RackOne, at: '/rack1'
224
- mount RackTwo, at: '/rack2'
225
- mount RackThree.new, at: '/rack3'
226
- mount ->(env) {[200, {}, ['Rack Four']]}, at: '/rack4'
227
- mount 'dashboard#index', at: '/dashboard'
204
+ mount MyRackApp.new, at: "/foo"
228
205
  end
229
206
  ```
230
207
 
231
- 1. `RackOne` is used as it is (class), because it respond to `.call`
232
- 2. `RackTwo` is initialized, because it respond to `#call`
233
- 3. `RackThree` is used as it is (object), because it respond to `#call`
234
- 4. That Proc is used as it is, because it respond to `#call`
235
- 5. That string is resolved as `Dashboard::Index` ([Hanami::Controller](https://github.com/hanami/controller) integration)
236
-
237
-
238
-
239
208
  ### Duck typed endpoints:
240
209
 
241
210
  Everything that responds to `#call` is invoked as it is:
242
211
 
243
- ```ruby
244
- router = Hanami::Router.new
245
- router.get '/hanami', to: ->(env) { [200, {}, ['Hello from Hanami!']] }
246
- router.get '/middleware', to: Middleware
247
- router.get '/rack-app', to: RackApp.new
248
- router.get '/method', to: ActionControllerSubclass.action(:new)
249
- ```
250
-
251
-
252
- If it's a string, it tries to instantiate a class from it:
253
-
254
- ```ruby
255
- class RackApp
256
- def call(env)
257
- # ...
258
- end
259
- end
260
-
261
- router = Hanami::Router.new
262
- router.get '/hanami', to: 'rack_app' # it will map to RackApp.new
263
- ```
264
-
265
- It also supports Controller + Action syntax:
266
-
267
- ```ruby
268
- module Flowers
269
- class Index
270
- def call(env)
271
- # ...
272
- end
273
- end
274
- end
275
-
276
- router = Hanami::Router.new
277
- router.get '/flowers', to: 'flowers#index' # it will map to Flowers::Index.new
278
- ```
279
-
280
-
281
-
282
- ### Implicit Not Found (404):
283
-
284
- ```ruby
285
- router = Hanami::Router.new
286
- router.call(Rack::MockRequest.env_for('/unknown')).status # => 404
287
- ```
288
-
289
- ### Controllers:
290
-
291
- `Hanami::Router` has a special convention for controllers naming.
292
- It allows to declare an action as an endpoint, with a special syntax: `<controller>#<action>`.
293
-
294
212
  ```ruby
295
213
  Hanami::Router.new do
296
- get '/', to: 'welcome#index'
297
- end
298
- ```
299
-
300
- In the example above, the router will look for the `Welcome::Index` action.
301
-
302
- #### Namespaces
303
-
304
- In applications where for maintainability or technical reasons, this convention
305
- can't work, `Hanami::Router` can accept a `:namespace` option, which defines the
306
- Ruby namespace where to look for actions.
307
-
308
- For instance, given a Hanami full stack application called `Bookshelf`, the
309
- controllers are available under `Bookshelf::Controllers`.
310
-
311
- ```ruby
312
- Hanami::Router.new(namespace: Bookshelf::Controllers) do
313
- get '/', to: 'welcome#index'
314
- end
315
- ```
316
-
317
- In the example above, the router will look for the `Bookshelf::Controllers::Welcome::Index` action.
318
-
319
- ### RESTful Resource:
320
-
321
- ```ruby
322
- router = Hanami::Router.new
323
- router.resource 'identity'
324
- ```
325
-
326
- It will map:
327
-
328
- <table>
329
- <tr>
330
- <th>Verb</th>
331
- <th>Path</th>
332
- <th>Action</th>
333
- <th>Name</th>
334
- <th>Named Route</th>
335
- </tr>
336
- <tr>
337
- <td>GET</td>
338
- <td>/identity</td>
339
- <td>Identity::Show</td>
340
- <td>:show</td>
341
- <td>:identity</td>
342
- </tr>
343
- <tr>
344
- <td>GET</td>
345
- <td>/identity/new</td>
346
- <td>Identity::New</td>
347
- <td>:new</td>
348
- <td>:new_identity</td>
349
- </tr>
350
- <tr>
351
- <td>POST</td>
352
- <td>/identity</td>
353
- <td>Identity::Create</td>
354
- <td>:create</td>
355
- <td>:identity</td>
356
- </tr>
357
- <tr>
358
- <td>GET</td>
359
- <td>/identity/edit</td>
360
- <td>Identity::Edit</td>
361
- <td>:edit</td>
362
- <td>:edit_identity</td>
363
- </tr>
364
- <tr>
365
- <td>PATCH</td>
366
- <td>/identity</td>
367
- <td>Identity::Update</td>
368
- <td>:update</td>
369
- <td>:identity</td>
370
- </tr>
371
- <tr>
372
- <td>DELETE</td>
373
- <td>/identity</td>
374
- <td>Identity::Destroy</td>
375
- <td>:destroy</td>
376
- <td>:identity</td>
377
- </tr>
378
- </table>
379
-
380
- If you don't need all the default endpoints, just do:
381
-
382
- ```ruby
383
- router = Hanami::Router.new
384
- router.resource 'identity', only: [:edit, :update]
385
-
386
- #### which is equivalent to:
387
-
388
- router.resource 'identity', except: [:show, :new, :create, :destroy]
389
- ```
390
-
391
-
392
- If you need extra endpoints:
393
-
394
- ```ruby
395
- router = Hanami::Router.new
396
- router.resource 'identity' do
397
- member do
398
- get 'avatar' # maps to Identity::Avatar
399
- end
400
-
401
- collection do
402
- get 'authorizations' # maps to Identity::Authorizations
403
- end
404
- end
405
-
406
- router.path(:avatar_identity) # => /identity/avatar
407
- router.path(:authorizations_identity) # => /identity/authorizations
408
- ```
409
-
410
-
411
- Configure controller:
412
-
413
- ```ruby
414
- router = Hanami::Router.new
415
- router.resource 'profile', controller: 'identity'
416
-
417
- router.path(:profile) # => /profile # Will route to Identity::Show
418
- ```
419
-
420
- #### Nested Resources
421
-
422
- We can nest resource(s):
423
-
424
- ```ruby
425
- router = Hanami::Router.new
426
- router.resource :identity do
427
- resource :avatar
428
- resources :api_keys
429
- end
430
-
431
- router.path(:identity_avatar) # => /identity/avatar
432
- router.path(:new_identity_avatar) # => /identity/avatar/new
433
- router.path(:edit_identity_avatar) # => /identity/avatar/new
434
-
435
- router.path(:identity_api_keys) # => /identity/api_keys
436
- router.path(:identity_api_key, id: 1) # => /identity/api_keys/:id
437
- router.path(:new_identity_api_key) # => /identity/api_keys/new
438
- router.path(:edit_identity_api_key, id: 1) # => /identity/api_keys/:id/edit
439
- ```
440
-
441
-
442
-
443
- ### RESTful Resources:
444
-
445
- ```ruby
446
- router = Hanami::Router.new
447
- router.resources 'flowers'
448
- ```
449
-
450
- It will map:
451
-
452
- <table>
453
- <tr>
454
- <th>Verb</th>
455
- <th>Path</th>
456
- <th>Action</th>
457
- <th>Name</th>
458
- <th>Named Route</th>
459
- </tr>
460
- <tr>
461
- <td>GET</td>
462
- <td>/flowers</td>
463
- <td>Flowers::Index</td>
464
- <td>:index</td>
465
- <td>:flowers</td>
466
- </tr>
467
- <tr>
468
- <td>GET</td>
469
- <td>/flowers/:id</td>
470
- <td>Flowers::Show</td>
471
- <td>:show</td>
472
- <td>:flower</td>
473
- </tr>
474
- <tr>
475
- <td>GET</td>
476
- <td>/flowers/new</td>
477
- <td>Flowers::New</td>
478
- <td>:new</td>
479
- <td>:new_flower</td>
480
- </tr>
481
- <tr>
482
- <td>POST</td>
483
- <td>/flowers</td>
484
- <td>Flowers::Create</td>
485
- <td>:create</td>
486
- <td>:flowers</td>
487
- </tr>
488
- <tr>
489
- <td>GET</td>
490
- <td>/flowers/:id/edit</td>
491
- <td>Flowers::Edit</td>
492
- <td>:edit</td>
493
- <td>:edit_flower</td>
494
- </tr>
495
- <tr>
496
- <td>PATCH</td>
497
- <td>/flowers/:id</td>
498
- <td>Flowers::Update</td>
499
- <td>:update</td>
500
- <td>:flower</td>
501
- </tr>
502
- <tr>
503
- <td>DELETE</td>
504
- <td>/flowers/:id</td>
505
- <td>Flowers::Destroy</td>
506
- <td>:destroy</td>
507
- <td>:flower</td>
508
- </tr>
509
- </table>
510
-
511
-
512
- ```ruby
513
- router.path(:flowers) # => /flowers
514
- router.path(:flower, id: 23) # => /flowers/23
515
- router.path(:edit_flower, id: 23) # => /flowers/23/edit
516
- ```
517
-
518
-
519
-
520
- If you don't need all the default endpoints, just do:
521
-
522
- ```ruby
523
- router = Hanami::Router.new
524
- router.resources 'flowers', only: [:new, :create, :show]
525
-
526
- #### which is equivalent to:
527
-
528
- router.resources 'flowers', except: [:index, :edit, :update, :destroy]
529
- ```
530
-
531
-
532
- If you need extra endpoints:
533
-
534
- ```ruby
535
- router = Hanami::Router.new
536
- router.resources 'flowers' do
537
- member do
538
- get 'toggle' # maps to Flowers::Toggle
539
- end
540
-
541
- collection do
542
- get 'search' # maps to Flowers::Search
543
- end
214
+ get "/hanami", to: ->(env) { [200, {}, ["Hello from Hanami!"]] }
215
+ get "/rack-app", to: RackApp.new
216
+ get "/method", to: ActionControllerSubclass.action(:new)
544
217
  end
545
-
546
- router.path(:toggle_flower, id: 23) # => /flowers/23/toggle
547
- router.path(:search_flowers) # => /flowers/search
548
218
  ```
549
219
 
550
-
551
- Configure controller:
220
+ ### Implicit Not Found (404):
552
221
 
553
222
  ```ruby
554
223
  router = Hanami::Router.new
555
- router.resources 'blossoms', controller: 'flowers'
556
-
557
- router.path(:blossom, id: 23) # => /blossoms/23 # Will route to Flowers::Show
224
+ router.call(Rack::MockRequest.env_for("/unknown")).status # => 404
558
225
  ```
559
226
 
560
- #### Nested Resources
561
-
562
- We can nest resource(s):
227
+ ### Explicit Not Found:
563
228
 
564
229
  ```ruby
565
- router = Hanami::Router.new
566
- router.resources :users do
567
- resource :avatar
568
- resources :favorites
569
- end
570
-
571
- router.path(:user_avatar, user_id: 1) # => /users/1/avatar
572
- router.path(:new_user_avatar, user_id: 1) # => /users/1/avatar/new
573
- router.path(:edit_user_avatar, user_id: 1) # => /users/1/avatar/edit
574
-
575
- router.path(:user_favorites, user_id: 1) # => /users/1/favorites
576
- router.path(:user_favorite, user_id: 1, id: 2) # => /users/1/favorites/2
577
- router.path(:new_user_favorites, user_id: 1) # => /users/1/favorites/new
578
- router.path(:edit_user_favorites, user_id: 1, id: 2) # => /users/1/favorites/2/edit
230
+ router = Hanami::Router.new(not_found: ->(_) { [499, {}, []]})
231
+ router.call(Rack::MockRequest.env_for("/unknown")).status # => 499
579
232
  ```
580
233
 
581
234
  ### Body Parsers
@@ -593,11 +246,13 @@ It comes with a built-in JSON parser and allows to pass custom parsers.
593
246
  #### JSON Parsing
594
247
 
595
248
  ```ruby
596
- require 'hanami/router'
597
- require 'hanami/middleware/body_parser'
249
+ # frozen_string_literal: true
250
+
251
+ require "hanami/router"
252
+ require "hanami/middleware/body_parser"
598
253
 
599
254
  app = Hanami::Router.new do
600
- patch '/books/:id', to: ->(env) { [200, {},[env['router.params'].inspect]] }
255
+ patch "/books/:id", to: ->(env) { [200, {}, [env["router.params"].inspect]] }
601
256
  end
602
257
 
603
258
  use Hanami::Middleware::BodyParser, :json
@@ -627,13 +282,15 @@ If you want to use a different JSON backend, include `multi_json` in your `Gemfi
627
282
  #### Custom Parsers
628
283
 
629
284
  ```ruby
630
- require 'hanami/router'
631
- require 'hanami/middleware/body_parser'
285
+ # frozen_string_literal: true
286
+
287
+ require "hanami/router"
288
+ require "hanami/middleware/body_parser"
632
289
 
633
- # See Hanami::Routing::Parsing::Parser
290
+ # See Hanami::Middleware::BodyParser::Parser
634
291
  class XmlParser < Hanami::Middleware::BodyParser::Parser
635
292
  def mime_types
636
- ['application/xml', 'text/xml']
293
+ ["application/xml", "text/xml"]
637
294
  end
638
295
 
639
296
  # Parse body and return a Hash
@@ -645,7 +302,7 @@ class XmlParser < Hanami::Middleware::BodyParser::Parser
645
302
  end
646
303
 
647
304
  app = Hanami::Router.new do
648
- patch '/authors/:id', to: ->(env) { [200, {},[env['router.params'].inspect]] }
305
+ patch "/authors/:id", to: ->(env) { [200, {}, [env["router.params"].inspect]] }
649
306
  end
650
307
 
651
308
  use Hanami::Middleware::BodyParser, XmlParser
@@ -665,13 +322,15 @@ curl http://localhost:2300/authors/1 \
665
322
  ## Testing
666
323
 
667
324
  ```ruby
668
- require 'hanami/router'
325
+ # frozen_string_literal: true
326
+
327
+ require "hanami/router"
669
328
 
670
329
  router = Hanami::Router.new do
671
- get '/books/:id', to: 'books#show', as: :book
330
+ get "/books/:id", to: "books#show", as: :book
672
331
  end
673
332
 
674
- route = router.recognize('/books/23')
333
+ route = router.recognize("/books/23")
675
334
  route.verb # "GET"
676
335
  route.action # => "books#show"
677
336
  route.params # => {:id=>"23"}
@@ -683,7 +342,7 @@ route.action # => "books#show"
683
342
  route.params # => {:id=>"23"}
684
343
  route.routable? # => true
685
344
 
686
- route = router.recognize('/books/23', method: :post)
345
+ route = router.recognize("/books/23", method: :post)
687
346
  route.verb # "POST"
688
347
  route.routable? # => false
689
348
  ```
@@ -700,13 +359,8 @@ __Hanami::Router__ uses [Semantic Versioning 2.0.0](http://semver.org)
700
359
  4. Push to the branch (`git push origin my-new-feature`)
701
360
  5. Create new Pull Request
702
361
 
703
- ## Acknowledgements
704
-
705
- Thanks to Joshua Hull ([@joshbuddy](https://github.com/joshbuddy)) for his
706
- [http_router](http://rubygems.org/gems/http_router).
707
-
708
362
  ## Copyright
709
363
 
710
- Copyright © 2014-2017 Luca Guidi – Released under MIT License
364
+ Copyright © 2014-2021 Luca Guidi – Released under MIT License
711
365
 
712
366
  This project was formerly known as Lotus (`lotus-router`).