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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +44 -0
- data/LICENSE.md +1 -1
- data/README.md +97 -443
- data/hanami-router.gemspec +23 -19
- data/lib/hanami/middleware/body_parser.rb +21 -15
- data/lib/hanami/middleware/body_parser/class_interface.rb +62 -56
- data/lib/hanami/middleware/body_parser/errors.rb +7 -4
- data/lib/hanami/middleware/body_parser/json_parser.rb +9 -7
- data/lib/hanami/middleware/body_parser/parser.rb +58 -0
- data/lib/hanami/middleware/error.rb +16 -0
- data/lib/hanami/router.rb +548 -957
- data/lib/hanami/router/block.rb +88 -0
- data/lib/hanami/router/error.rb +67 -0
- data/lib/hanami/router/node.rb +91 -0
- data/lib/hanami/router/params.rb +35 -0
- data/lib/hanami/router/prefix.rb +67 -0
- data/lib/hanami/router/recognized_route.rb +92 -0
- data/lib/hanami/router/redirect.rb +28 -0
- data/lib/hanami/router/segment.rb +19 -0
- data/lib/hanami/router/trie.rb +63 -0
- data/lib/hanami/router/url_helpers.rb +40 -0
- data/lib/hanami/router/version.rb +4 -1
- metadata +61 -41
- data/lib/hanami-router.rb +0 -1
- data/lib/hanami/routing/endpoint.rb +0 -195
- data/lib/hanami/routing/endpoint_resolver.rb +0 -238
- data/lib/hanami/routing/error.rb +0 -7
- data/lib/hanami/routing/force_ssl.rb +0 -212
- data/lib/hanami/routing/http_router.rb +0 -220
- data/lib/hanami/routing/http_router_monkey_patch.rb +0 -38
- data/lib/hanami/routing/namespace.rb +0 -98
- data/lib/hanami/routing/parsers.rb +0 -113
- data/lib/hanami/routing/parsing/json_parser.rb +0 -33
- data/lib/hanami/routing/parsing/parser.rb +0 -61
- data/lib/hanami/routing/recognized_route.rb +0 -219
- data/lib/hanami/routing/resource.rb +0 -119
- data/lib/hanami/routing/resource/action.rb +0 -402
- data/lib/hanami/routing/resource/nested.rb +0 -41
- data/lib/hanami/routing/resource/options.rb +0 -74
- data/lib/hanami/routing/resources.rb +0 -48
- data/lib/hanami/routing/resources/action.rb +0 -156
- data/lib/hanami/routing/route.rb +0 -71
- data/lib/hanami/routing/routes_inspector.rb +0 -221
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a855344a3dcd5d256259802c803eae8c7e41aeaeda1f108855d668458ec6c32d
|
4
|
+
data.tar.gz: 79f333141e1453d4bd4c4f4e09fd41e54bb2cab94d3889268a6a765ec9d52a16
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1dc165c33814c2869852526395de10368b14918b09af6d4dbac56e2610f6ae8dcd8e78eb1034e0b858a48e30d80470976af692e1a235cb58a54561c1dc7393b7
|
7
|
+
data.tar.gz: a73e23f4d0875143660610be0b8e349be143dcdbed62a258070d0b1973ee876c57b68ae8df90f1fc786bf9e689ec40a16ced52053dbd16f893efe4b62fa70e85
|
data/CHANGELOG.md
CHANGED
@@ -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
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
|
[](https://badge.fury.io/rb/hanami-router)
|
8
|
-
[](https://codecov.io/gh/hanami/router)
|
8
|
+
[](https://github.com/hanami/router/actions?query=workflow%3Aci+branch%3Aunstable)
|
9
|
+
[](https://codecov.io/gh/hanami/router)
|
11
10
|
[](https://depfu.com/github/hanami/router?project=Bundler)
|
12
11
|
[](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.
|
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
|
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
|
-
|
52
|
+
# frozen_string_literal: true
|
53
|
+
require "hanami/router"
|
52
54
|
|
53
55
|
app = Hanami::Router.new do
|
54
|
-
get
|
56
|
+
get "/", to: ->(env) { [200, {}, ["Welcome to Hanami!"]] }
|
55
57
|
end
|
56
58
|
|
57
|
-
|
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, {}, [
|
72
|
-
get
|
73
|
-
get
|
74
|
-
get
|
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
|
-
|
92
|
-
get '/api_keys'
|
93
|
-
end
|
94
|
-
end
|
84
|
+
redirect "/legacy", to: "/"
|
95
85
|
|
96
|
-
|
97
|
-
member do
|
98
|
-
patch '/activate'
|
99
|
-
end
|
86
|
+
mount Api::App, at: "/api"
|
100
87
|
|
101
|
-
|
102
|
-
|
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
|
-
|
114
|
-
|
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
|
-
|
123
|
-
|
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
|
-
|
132
|
-
|
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
|
-
|
141
|
-
|
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
|
-
|
150
|
-
|
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
|
-
|
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
|
-
|
175
|
-
|
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
|
-
|
184
|
-
|
185
|
-
|
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:
|
194
|
-
|
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
|
-
|
207
|
-
|
208
|
-
|
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
|
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
|
297
|
-
|
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.
|
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
|
-
|
561
|
-
|
562
|
-
We can nest resource(s):
|
227
|
+
### Explicit Not Found:
|
563
228
|
|
564
229
|
```ruby
|
565
|
-
router = Hanami::Router.new
|
566
|
-
router.
|
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
|
-
|
597
|
-
|
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
|
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
|
-
|
631
|
-
|
285
|
+
# frozen_string_literal: true
|
286
|
+
|
287
|
+
require "hanami/router"
|
288
|
+
require "hanami/middleware/body_parser"
|
632
289
|
|
633
|
-
# See Hanami::
|
290
|
+
# See Hanami::Middleware::BodyParser::Parser
|
634
291
|
class XmlParser < Hanami::Middleware::BodyParser::Parser
|
635
292
|
def mime_types
|
636
|
-
[
|
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
|
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
|
-
|
325
|
+
# frozen_string_literal: true
|
326
|
+
|
327
|
+
require "hanami/router"
|
669
328
|
|
670
329
|
router = Hanami::Router.new do
|
671
|
-
get
|
330
|
+
get "/books/:id", to: "books#show", as: :book
|
672
331
|
end
|
673
332
|
|
674
|
-
route = router.recognize(
|
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(
|
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-
|
364
|
+
Copyright © 2014-2021 Luca Guidi – Released under MIT License
|
711
365
|
|
712
366
|
This project was formerly known as Lotus (`lotus-router`).
|