hanami-router 1.3.1 → 2.0.0.alpha4

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