hanami-router 0.0.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 38ded28dfed0dcaba83b95f9951d26362364d18b
4
- data.tar.gz: ec03802168690ac1732f0d5d3ae6bc75a07c3ad9
3
+ metadata.gz: c56fe3e09566c9c52b9bba52b1532971003ee171
4
+ data.tar.gz: 8dd727332e2d8f642b69b1c2bc9b775253bb5295
5
5
  SHA512:
6
- metadata.gz: eac74055883b768c2eb704b0ad6883e658ea647ed0fa02be13f691897ebd002e3f0fa00b727b8d12d3b5f673ac940fb0ec45514ce481484efb4f0a4f1a330bab
7
- data.tar.gz: d3c102eaf443e25d1b4ee895aea5266b513d463101700bfbc4519c1594641ac29cb704cf608fef6476f11b51139b8110b368107ada6ab08a1b2603fc24f06f38
6
+ metadata.gz: 75299b0aa4540923f2bc275b44a2a0fedee0252ca3ff3be2d25ac9f89979125626bf73d6a21483b9d42a5c6a790d9fe3f642a87fd621166a56a3996ae149b353
7
+ data.tar.gz: 1cfff39ddebd9aeb968eb7dc9220f7da03984fab27b0e94f159eacb9f170de6f4ac9c72ee734bce30ef1dc5f4cd8eccf47498c5ef61a82f2a761c5a1515ea8a6
data/CHANGELOG.md ADDED
@@ -0,0 +1,101 @@
1
+ # Hanami::Router
2
+ Rack compatible HTTP router for Ruby
3
+
4
+ ## v0.6.0 - 2016-01-22
5
+ ### Changed
6
+ - [Luca Guidi] Renamed the project
7
+
8
+ ## v0.5.1 - 2016-01-19
9
+ - [Anton Davydov] Print stacked lines for routes inspection
10
+
11
+ ## v0.5.0 - 2016-01-12
12
+ ### Added
13
+ - [Luca Guidi] Added `Lotus::Router#recognize` as a testing facility. Example `router.recognize('/') # => associated route`
14
+ - [Luca Guidi] Added `Lotus::Router.define` in order to wrap routes definitions in `config/routes.rb` when `Lotus::Router` is used outside of Lotus projects
15
+ - [David Strauß] Make `Lotus::Routing::Parsing::JsonParser` compatible with `application/vnd.api+json` MIME Type
16
+ - [Alfonso Uceda Pompa] Improved exception messages for `Lotus::Router#path` and `#url`
17
+
18
+ ### Fixed
19
+ - [Alfonso Uceda Pompa] Ensure `Lotus::Router#path` and `#url` to generate correct URL for mounted applications
20
+ - [Vladislav Zarakovsky] Ensure Force SSL mode to respect Rack SPEC
21
+
22
+ ### Changed
23
+ - [Alfonso Uceda Pompa] A failure for body parsers raises a `Lotus::Routing::Parsing::BodyParsingError` exception
24
+ - [Karim Tarek] Introduced `Lotus::Router::Error` and let all the framework exceptions to inherit from it.
25
+
26
+ ## v0.4.3 - 2015-09-30
27
+ ### Added
28
+ - [Luca Guidi] Official support for JRuby 9k+
29
+
30
+ ## v0.4.2 - 2015-07-10
31
+ ### Fixed
32
+ - [Alfonso Uceda Pompa] Ensure mounted applications to not repeat their prefix (eg `/admin/admin`)
33
+ - [Thiago Felippe] Ensure router inspector properly prints routes with repeated entries (eg `/admin/dashboard/admin`)
34
+
35
+ ## v0.4.1 - 2015-06-23
36
+ ### Added
37
+ - [Alfonso Uceda Pompa] Force SSL (eg `Lotus::Router.new(force_ssl: true`).
38
+ - [Alfonso Uceda Pompa] Allow router to accept a `:prefix` option, in order to generate prefixed routes.
39
+
40
+ ## v0.4.0 - 2015-05-15
41
+ ### Added
42
+ - [Alfonso Uceda Pompa] Nested RESTful resource(s)
43
+
44
+ ### Changed
45
+ - [Alfonso Uceda Pompa] RESTful resource(s) have a correct pluralization/singularization for variables and named routes (eg. `/books/:id` is now `:book` instead of `:books`)
46
+
47
+ ## v0.3.0 - 2015-03-23
48
+
49
+ ## v0.2.1 - 2015-01-30
50
+ ### Added
51
+ - [Alfonso Uceda Pompa] Lotus::Action compat: invoke `.call` if defined, otherwise fall back to `#call`.
52
+
53
+ ## v0.2.0 - 2014-12-23
54
+ ### Added
55
+ - [Luca Guidi & Alfonso Uceda Pompa] Introduced routes inspector for CLI
56
+ - [Luca Guidi & Janko Marohnić] Introduced body parser for JSON
57
+ - [Luca Guidi] Introduced request body parsers: they parse body and turn into params.
58
+ - [Fred Wu] Introduced Router#define
59
+
60
+ ### Fixed
61
+ - [Luca Guidi] Fix for member/collection actions in RESTful resource(s): allow to take actions with a leading slash.
62
+ - [Janko Marohnić] Fix for nested namespaces and RESTful resource(s) under namespace. They were generating wrong route names.
63
+ - [Luca Guidi] Made InvalidRouteException to inherit from StandardError so it can be catched from anonymous `rescue` clause
64
+ - [Luca Guidi] Fix RESTful resource(s) to respect :only/:except options
65
+
66
+ ### Changed
67
+ - [Luca Guidi] Aligned naming conventions with Lotus::Controller: no more BooksController::Index. Use Books::Index instead.
68
+ - [Luca Guidi] Removed `:prefix` option for routes. Use `#namespace` blocks instead.
69
+ - [Janko Marohnić] Make 301 the default redirect status
70
+
71
+ ## v0.1.1 - 2014-06-23
72
+ ### Added
73
+ - [Luca Guidi] Introduced Lotus::Router#mount
74
+ - [Luca Guidi] Let specify a pattern for Lotus::Routing::EndpointResolver
75
+ - [Luca Guidi] Make Lotus::Routing::Endpoint::EndpointNotFound to inherit from StandardError, instead of Exception. This make it compatible with Rack::ShowExceptions.
76
+
77
+ ## v0.1.0 - 2014-01-23
78
+ ### Added
79
+ - [Luca Guidi] Official support for Ruby 2.1
80
+ - [Luca Guidi] Added support for OPTIONS HTTP verb
81
+ - [Luca Guidi] Added Lotus::Routing::EndpointNotFound when a lazy endpoint can't be found
82
+ - [Luca Guidi] Make action separator customizable via Lotus::Router options.
83
+ - [Luca Guidi] Catch http_router exceptions and re-raise them with names under Lotus::Routing. This helps to have a stable public API.
84
+ - [Luca Guidi] Lotus::Routing::Resource::CollectionAction use configurable controller and action name separator over the hardcoded value
85
+ - [Luca Guidi] Implemented Lotus::Routing::Namespace#resource
86
+ - [Luca Guidi] Lotus::Routing::EndpointResolver now accepts options to inject namespace and suffix
87
+ - [Luca Guidi] Allow resolver and route class to be injected via options
88
+ - [Luca Guidi] Return 404 for not found and 405 for unacceptable HTTP method
89
+ - [Luca Guidi] Allow non-finished Rack responses to be used
90
+ - [Luca Guidi] Implemented lazy loading for endpoints
91
+ - [Luca Guidi] Implemented Lotus::Router.new to take a block and define routes
92
+ - [Luca Guidi] Add support for resource
93
+ - [Luca Guidi] Support for resource's member and collection
94
+ - [Luca Guidi] Add support for namespaces
95
+ - [Luca Guidi] Added support for RESTful resources
96
+ - [Luca Guidi] Add support for POST, DELETE, PUT, PATCH, TRACE
97
+ - [Luca Guidi] Routes constraints
98
+ - [Luca Guidi] Named urls
99
+ - [Luca Guidi] Added support for Procs as endpoints
100
+ - [Luca Guidi] Implemented redirect
101
+ - [Luca Guidi] Basic routing
data/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ Copyright © 2014-2016 Luca Guidi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,8 +1,29 @@
1
1
  # Hanami::Router
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/hanami/router`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ Rack compatible, lightweight and fast HTTP Router for Ruby and [Hanami](http://hanamirb.org).
4
+
5
+ ## Status
6
+
7
+ [![Gem Version](http://img.shields.io/gem/v/hanami-router.svg)](https://badge.fury.io/rb/hanami-router)
8
+ [![Build Status](http://img.shields.io/travis/hanami/router/master.svg)](https://travis-ci.org/hanami/router?branch=master)
9
+ [![Coverage](http://img.shields.io/coveralls/hanami/router/master.svg)](https://coveralls.io/r/hanami/router)
10
+ [![Code Climate](http://img.shields.io/codeclimate/github/hanami/router.svg)](https://codeclimate.com/github/hanami/router)
11
+ [![Dependencies](http://img.shields.io/gemnasium/hanami/router.svg)](https://gemnasium.com/hanami/router)
12
+ [![Inline docs](http://inch-ci.org/github/hanami/router.png)](http://inch-ci.org/github/hanami/router)
13
+
14
+ ## Contact
15
+
16
+ * Home page: http://hanamirb.org
17
+ * Mailing List: http://hanamirb.org/mailing-list
18
+ * API Doc: http://rdoc.info/gems/hanami-router
19
+ * Bugs/Issues: https://github.com/hanami/router/issues
20
+ * Support: http://stackoverflow.com/questions/tagged/hanami
21
+ * Chat: http://chat.hanamirb.org
22
+
23
+ ## Rubies
24
+
25
+ __Hanami::Router__ supports Ruby (MRI) 2.2+, JRuby 9k+
4
26
 
5
- TODO: Delete this and the text above, and describe your gem
6
27
 
7
28
  ## Installation
8
29
 
@@ -14,23 +35,660 @@ gem 'hanami-router'
14
35
 
15
36
  And then execute:
16
37
 
17
- $ bundle
38
+ ```shell
39
+ $ bundle
40
+ ```
18
41
 
19
42
  Or install it yourself as:
20
43
 
21
- $ gem install hanami-router
44
+ ```shell
45
+ $ gem install hanami-router
46
+ ```
47
+
48
+ ## Getting Started
49
+
50
+ ```ruby
51
+ require 'hanami/router'
52
+
53
+ app = Hanami::Router.new do
54
+ get '/', to: ->(env) { [200, {}, ['Welcome to Hanami::Router!']] }
55
+ end
56
+
57
+ Rack::Server.start app: app, Port: 2300
58
+ ```
22
59
 
23
60
  ## Usage
24
61
 
25
- TODO: Write usage instructions here
62
+ __Hanami::Router__ is designed to work as a standalone framework or within a
63
+ context of a [Hanami](http://hanamirb.org) application.
64
+
65
+ For the standalone usage, it supports neat features:
66
+
67
+ ### A Beautiful DSL:
68
+
69
+ ```ruby
70
+ Hanami::Router.new do
71
+ get '/', to: ->(env) { [200, {}, ['Hi!']] }
72
+ get '/dashboard', to: Dashboard::Index
73
+ get '/rack-app', to: RackApp.new
74
+ get '/flowers', to: 'flowers#index'
75
+ get '/flowers/:id', to: 'flowers#show'
76
+
77
+ redirect '/legacy', to: '/'
78
+
79
+ mount Api::App, at: '/api'
80
+
81
+ namespace 'admin' do
82
+ get '/users', to: Users::Index
83
+ end
84
+
85
+ resource 'identity' do
86
+ member do
87
+ get '/avatar'
88
+ end
89
+
90
+ collection do
91
+ get '/api_keys'
92
+ end
93
+ end
94
+
95
+ resources 'robots' do
96
+ member do
97
+ patch '/activate'
98
+ end
99
+
100
+ collection do
101
+ get '/search'
102
+ end
103
+ end
104
+ end
105
+ ```
106
+
107
+
108
+
109
+ ### Fixed string matching:
110
+
111
+ ```ruby
112
+ router = Hanami::Router.new
113
+ router.get '/hanami', to: ->(env) { [200, {}, ['Hello from Hanami!']] }
114
+ ```
115
+
116
+
117
+
118
+ ### String matching with variables:
119
+
120
+ ```ruby
121
+ router = Hanami::Router.new
122
+ router.get '/flowers/:id', to: ->(env) { [200, {}, ["Hello from Flower no. #{ env['router.params'][:id] }!"]] }
123
+ ```
124
+
125
+
126
+
127
+ ### Variables Constraints:
128
+
129
+ ```ruby
130
+ router = Hanami::Router.new
131
+ router.get '/flowers/:id', id: /\d+/, to: ->(env) { [200, {}, [":id must be a number!"]] }
132
+ ```
133
+
134
+
135
+
136
+ ### String matching with globbing:
137
+
138
+ ```ruby
139
+ router = Hanami::Router.new
140
+ router.get '/*', to: ->(env) { [200, {}, ["This is catch all: #{ env['router.params'].inspect }!"]] }
141
+ ```
142
+
143
+
144
+
145
+ ### String matching with optional tokens:
146
+
147
+ ```ruby
148
+ router = Hanami::Router.new
149
+ router.get '/hanami(.:format)' to: ->(env) { [200, {}, ["You've requested #{ env['router.params'][:format] }!"]] }
150
+ ```
151
+
152
+
153
+
154
+ ### Support for the most common HTTP methods:
155
+
156
+ ```ruby
157
+ router = Hanami::Router.new
158
+ endpoint = ->(env) { [200, {}, ['Hello from Hanami!']] }
159
+
160
+ router.get '/hanami', to: endpoint
161
+ router.post '/hanami', to: endpoint
162
+ router.put '/hanami', to: endpoint
163
+ router.patch '/hanami', to: endpoint
164
+ router.delete '/hanami', to: endpoint
165
+ router.trace '/hanami', to: endpoint
166
+ ```
167
+
168
+
169
+
170
+ ### Redirect:
171
+
172
+ ```ruby
173
+ router = Hanami::Router.new
174
+ router.get '/redirect_destination', to: ->(env) { [200, {}, ['Redirect destination!']] }
175
+ router.redirect '/legacy', to: '/redirect_destination'
176
+ ```
177
+
178
+
179
+
180
+ ### Named routes:
181
+
182
+ ```ruby
183
+ router = Hanami::Router.new(scheme: 'https', host: 'hanamirb.org')
184
+ router.get '/hanami', to: ->(env) { [200, {}, ['Hello from Hanami!']] }, as: :hanami
185
+
186
+ router.path(:hanami) # => "/hanami"
187
+ router.url(:hanami) # => "https://hanamirb.org/hanami"
188
+ ```
189
+
190
+
191
+
192
+ ### Namespaced routes:
193
+
194
+ ```ruby
195
+ router = Hanami::Router.new
196
+ router.namespace 'animals' do
197
+ namespace 'mammals' do
198
+ get '/cats', to: ->(env) { [200, {}, ['Meow!']] }, as: :cats
199
+ end
200
+ end
201
+
202
+ # and it generates:
203
+
204
+ router.path(:animals_mammals_cats) # => "/animals/mammals/cats"
205
+ ```
206
+
207
+
208
+
209
+ ### Mount Rack applications:
210
+
211
+ ```ruby
212
+ Hanami::Router.new do
213
+ mount RackOne, at: '/rack1'
214
+ mount RackTwo, at: '/rack2'
215
+ mount RackThree.new, at: '/rack3'
216
+ mount ->(env) {[200, {}, ['Rack Four']]}, at: '/rack4'
217
+ mount 'dashboard#index', at: '/dashboard'
218
+ end
219
+ ```
220
+
221
+ 1. `RackOne` is used as it is (class), because it respond to `.call`
222
+ 2. `RackTwo` is initialized, because it respond to `#call`
223
+ 3. `RackThree` is used as it is (object), because it respond to `#call`
224
+ 4. That Proc is used as it is, because it respond to `#call`
225
+ 5. That string is resolved as `Dashboard::Index` ([Hanami::Controller](https://github.com/hanami/controller) integration)
226
+
227
+
228
+
229
+ ### Duck typed endpoints:
230
+
231
+ Everything that responds to `#call` is invoked as it is:
232
+
233
+ ```ruby
234
+ router = Hanami::Router.new
235
+ router.get '/hanami', to: ->(env) { [200, {}, ['Hello from Hanami!']] }
236
+ router.get '/middleware', to: Middleware
237
+ router.get '/rack-app', to: RackApp.new
238
+ router.get '/method', to: ActionControllerSubclass.action(:new)
239
+ ```
240
+
241
+
242
+ If it's a string, it tries to instantiate a class from it:
243
+
244
+ ```ruby
245
+ class RackApp
246
+ def call(env)
247
+ # ...
248
+ end
249
+ end
250
+
251
+ router = Hanami::Router.new
252
+ router.get '/hanami', to: 'rack_app' # it will map to RackApp.new
253
+ ```
254
+
255
+ It also supports Controller + Action syntax:
256
+
257
+ ```ruby
258
+ module Flowers
259
+ class Index
260
+ def call(env)
261
+ # ...
262
+ end
263
+ end
264
+ end
265
+
266
+ router = Hanami::Router.new
267
+ router.get '/flowers', to: 'flowers#index' # it will map to Flowers::Index.new
268
+ ```
269
+
270
+
271
+
272
+ ### Implicit Not Found (404):
273
+
274
+ ```ruby
275
+ router = Hanami::Router.new
276
+ router.call(Rack::MockRequest.env_for('/unknown')).status # => 404
277
+ ```
278
+
279
+ ### Controllers:
280
+
281
+ `Hanami::Router` has a special convention for controllers naming.
282
+ It allows to declare an action as an endpoint, with a special syntax: `<controller>#<action>`.
283
+
284
+ ```ruby
285
+ Hanami::Router.new do
286
+ get '/', to: 'welcome#index'
287
+ end
288
+ ```
289
+
290
+ In the example above, the router will look for the `Welcome::Index` action.
26
291
 
27
- ## Development
292
+ #### Namespaces
28
293
 
29
- After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
294
+ In applications where for maintainability or technical reasons, this convention
295
+ can't work, `Hanami::Router` can accept a `:namespace` option, which defines the
296
+ Ruby namespace where to look for actions.
30
297
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
298
+ For instance, given a Hanami full stack application called `Bookshelf`, the
299
+ controllers are available under `Bookshelf::Controllers`.
300
+
301
+ ```ruby
302
+ Hanami::Router.new(namespace: Bookshelf::Controllers) do
303
+ get '/', to: 'welcome#index'
304
+ end
305
+ ```
306
+
307
+ In the example above, the router will look for the `Bookshelf::Controllers::Welcome::Index` action.
308
+
309
+ ### RESTful Resource:
310
+
311
+ ```ruby
312
+ router = Hanami::Router.new
313
+ router.resource 'identity'
314
+ ```
315
+
316
+ It will map:
317
+
318
+ <table>
319
+ <tr>
320
+ <th>Verb</th>
321
+ <th>Path</th>
322
+ <th>Action</th>
323
+ <th>Name</th>
324
+ <th>Named Route</th>
325
+ </tr>
326
+ <tr>
327
+ <td>GET</td>
328
+ <td>/identity</td>
329
+ <td>Identity::Show</td>
330
+ <td>:show</td>
331
+ <td>:identity</td>
332
+ </tr>
333
+ <tr>
334
+ <td>GET</td>
335
+ <td>/identity/new</td>
336
+ <td>Identity::New</td>
337
+ <td>:new</td>
338
+ <td>:new_identity</td>
339
+ </tr>
340
+ <tr>
341
+ <td>POST</td>
342
+ <td>/identity</td>
343
+ <td>Identity::Create</td>
344
+ <td>:create</td>
345
+ <td>:identity</td>
346
+ </tr>
347
+ <tr>
348
+ <td>GET</td>
349
+ <td>/identity/edit</td>
350
+ <td>Identity::Edit</td>
351
+ <td>:edit</td>
352
+ <td>:edit_identity</td>
353
+ </tr>
354
+ <tr>
355
+ <td>PATCH</td>
356
+ <td>/identity</td>
357
+ <td>Identity::Update</td>
358
+ <td>:update</td>
359
+ <td>:identity</td>
360
+ </tr>
361
+ <tr>
362
+ <td>DELETE</td>
363
+ <td>/identity</td>
364
+ <td>Identity::Destroy</td>
365
+ <td>:destroy</td>
366
+ <td>:identity</td>
367
+ </tr>
368
+ </table>
369
+
370
+ If you don't need all the default endpoints, just do:
371
+
372
+ ```ruby
373
+ router = Hanami::Router.new
374
+ router.resource 'identity', only: [:edit, :update]
375
+
376
+ #### which is equivalent to:
377
+
378
+ router.resource 'identity', except: [:show, :new, :create, :destroy]
379
+ ```
380
+
381
+
382
+ If you need extra endpoints:
383
+
384
+ ```ruby
385
+ router = Hanami::Router.new
386
+ router.resource 'identity' do
387
+ member do
388
+ get 'avatar' # maps to Identity::Avatar
389
+ end
390
+
391
+ collection do
392
+ get 'authorizations' # maps to Identity::Authorizations
393
+ end
394
+ end
395
+
396
+ router.path(:avatar_identity) # => /identity/avatar
397
+ router.path(:authorizations_identity) # => /identity/authorizations
398
+ ```
399
+
400
+
401
+ Configure controller:
402
+
403
+ ```ruby
404
+ router = Hanami::Router.new
405
+ router.resource 'profile', controller: 'identity'
406
+
407
+ router.path(:profile) # => /profile # Will route to Identity::Show
408
+ ```
409
+
410
+ #### Nested Resources
411
+
412
+ We can nest resource(s):
413
+
414
+ ```ruby
415
+ router = Hanami::Router.new
416
+ router.resource :identity do
417
+ resource :avatar
418
+ resources :api_keys
419
+ end
420
+
421
+ router.path(:identity_avatar) # => /identity/avatar
422
+ router.path(:new_identity_avatar) # => /identity/avatar/new
423
+ router.path(:edit_identity_avatar) # => /identity/avatar/new
424
+
425
+ router.path(:identity_api_keys) # => /identity/api_keys
426
+ router.path(:identity_api_key) # => /identity/api_keys/:id
427
+ router.path(:new_identity_api_key) # => /identity/api_keys/new
428
+ router.path(:edit_identity_api_key) # => /identity/api_keys/:id/edit
429
+ ```
430
+
431
+
432
+
433
+ ### RESTful Resources:
434
+
435
+ ```ruby
436
+ router = Hanami::Router.new
437
+ router.resources 'flowers'
438
+ ```
439
+
440
+ It will map:
441
+
442
+ <table>
443
+ <tr>
444
+ <th>Verb</th>
445
+ <th>Path</th>
446
+ <th>Action</th>
447
+ <th>Name</th>
448
+ <th>Named Route</th>
449
+ </tr>
450
+ <tr>
451
+ <td>GET</td>
452
+ <td>/flowers</td>
453
+ <td>Flowers::Index</td>
454
+ <td>:index</td>
455
+ <td>:flowers</td>
456
+ </tr>
457
+ <tr>
458
+ <td>GET</td>
459
+ <td>/flowers/:id</td>
460
+ <td>Flowers::Show</td>
461
+ <td>:show</td>
462
+ <td>:flower</td>
463
+ </tr>
464
+ <tr>
465
+ <td>GET</td>
466
+ <td>/flowers/new</td>
467
+ <td>Flowers::New</td>
468
+ <td>:new</td>
469
+ <td>:new_flower</td>
470
+ </tr>
471
+ <tr>
472
+ <td>POST</td>
473
+ <td>/flowers</td>
474
+ <td>Flowers::Create</td>
475
+ <td>:create</td>
476
+ <td>:flowers</td>
477
+ </tr>
478
+ <tr>
479
+ <td>GET</td>
480
+ <td>/flowers/:id/edit</td>
481
+ <td>Flowers::Edit</td>
482
+ <td>:edit</td>
483
+ <td>:edit_flower</td>
484
+ </tr>
485
+ <tr>
486
+ <td>PATCH</td>
487
+ <td>/flowers/:id</td>
488
+ <td>Flowers::Update</td>
489
+ <td>:update</td>
490
+ <td>:flower</td>
491
+ </tr>
492
+ <tr>
493
+ <td>DELETE</td>
494
+ <td>/flowers/:id</td>
495
+ <td>Flowers::Destroy</td>
496
+ <td>:destroy</td>
497
+ <td>:flower</td>
498
+ </tr>
499
+ </table>
500
+
501
+
502
+ ```ruby
503
+ router.path(:flowers) # => /flowers
504
+ router.path(:flower, id: 23) # => /flowers/23
505
+ router.path(:edit_flower, id: 23) # => /flowers/23/edit
506
+ ```
507
+
508
+
509
+
510
+ If you don't need all the default endpoints, just do:
511
+
512
+ ```ruby
513
+ router = Hanami::Router.new
514
+ router.resources 'flowers', only: [:new, :create, :show]
515
+
516
+ #### which is equivalent to:
517
+
518
+ router.resources 'flowers', except: [:index, :edit, :update, :destroy]
519
+ ```
520
+
521
+
522
+ If you need extra endpoints:
523
+
524
+ ```ruby
525
+ router = Hanami::Router.new
526
+ router.resources 'flowers' do
527
+ member do
528
+ get 'toggle' # maps to Flowers::Toggle
529
+ end
530
+
531
+ collection do
532
+ get 'search' # maps to Flowers::Search
533
+ end
534
+ end
535
+
536
+ router.path(:toggle_flower, id: 23) # => /flowers/23/toggle
537
+ router.path(:search_flowers) # => /flowers/search
538
+ ```
539
+
540
+
541
+ Configure controller:
542
+
543
+ ```ruby
544
+ router = Hanami::Router.new
545
+ router.resources 'blossoms', controller: 'flowers'
546
+
547
+ router.path(:blossom, id: 23) # => /blossoms/23 # Will route to Flowers::Show
548
+ ```
549
+
550
+ #### Nested Resources
551
+
552
+ We can nest resource(s):
553
+
554
+ ```ruby
555
+ router = Hanami::Router.new
556
+ router.resources :users do
557
+ resource :avatar
558
+ resources :favorites
559
+ end
560
+
561
+ router.path(:user_avatar, user_id: 1) # => /users/1/avatar
562
+ router.path(:new_user_avatar, user_id: 1) # => /users/1/avatar/new
563
+ router.path(:edit_user_avatar, user_id: 1) # => /users/1/avatar/edit
564
+
565
+ router.path(:user_favorites, user_id: 1) # => /users/1/favorites
566
+ router.path(:user_favorite, user_id: 1, id: 2) # => /users/1/favorites/2
567
+ router.path(:new_user_favorites, user_id: 1) # => /users/1/favorites/new
568
+ router.path(:edit_user_favorites, user_id: 1, id: 2) # => /users/1/favorites/2/edit
569
+ ```
570
+
571
+ ### Body Parsers
572
+
573
+ Rack ignores request bodies unless they come from a form submission.
574
+ If we have a JSON endpoint, the payload isn't available in the params hash:
575
+
576
+ ```ruby
577
+ Rack::Request.new(env).params # => {}
578
+ ```
579
+
580
+ This feature enables body parsing for specific MIME Types.
581
+ It comes with a built-in JSON parser and allows to pass custom parsers.
582
+
583
+ #### JSON Parsing
584
+
585
+ ```ruby
586
+ require 'hanami/router'
587
+
588
+ endpoint = ->(env) { [200, {},[env['router.params'].inspect]] }
589
+
590
+ router = Hanami::Router.new(parsers: [:json]) do
591
+ patch '/books/:id', to: endpoint
592
+ end
593
+ ```
594
+
595
+ ```shell
596
+ curl http://localhost:2300/books/1 \
597
+ -H "Content-Type: application/json" \
598
+ -H "Accept: application/json" \
599
+ -d '{"published":"true"}' \
600
+ -X PATCH
601
+
602
+ # => [200, {}, ["{:published=>\"true\",:id=>\"1\"}"]]
603
+ ```
604
+
605
+ If the json can't be parsed an exception is raised:
606
+
607
+ ```ruby
608
+ Hanami::Routing::Parsing::BodyParsingError
609
+ ```
610
+
611
+ #### Custom Parsers
612
+
613
+ ```ruby
614
+ require 'hanami/router'
615
+
616
+ # See Hanami::Routing::Parsing::Parser
617
+ class XmlParser
618
+ def mime_types
619
+ ['application/xml', 'text/xml']
620
+ end
621
+
622
+ # Parse body and return a Hash
623
+ def parse(body)
624
+ # parse xml
625
+ rescue SomeXmlParsingError => e
626
+ raise Hanami::Routing::Parsing::BodyParsingError.new(e)
627
+ end
628
+ end
629
+
630
+ endpoint = ->(env) { [200, {},[env['router.params'].inspect]] }
631
+
632
+ router = Hanami::Router.new(parsers: [XmlParser.new]) do
633
+ patch '/authors/:id', to: endpoint
634
+ end
635
+ ```
636
+
637
+ ```shell
638
+ curl http://localhost:2300/authors/1 \
639
+ -H "Content-Type: application/xml" \
640
+ -H "Accept: application/xml" \
641
+ -d '<name>LG</name>' \
642
+ -X PATCH
643
+
644
+ # => [200, {}, ["{:name=>\"LG\",:id=>\"1\"}"]]
645
+ ```
646
+
647
+ ## Testing
648
+
649
+ ```ruby
650
+ require 'hanami/router'
651
+
652
+ router = Hanami::Router.new do
653
+ get '/books/:id', to: 'books#show', as: :book
654
+ end
655
+
656
+ route = router.recognize('/books/23')
657
+ route.verb # "GET"
658
+ route.action # => "books#show"
659
+ route.params # => {:id=>"23"}
660
+ route.routable? # => true
661
+
662
+ route = router.recognize(:book, id: 23)
663
+ route.verb # "GET"
664
+ route.action # => "books#show"
665
+ route.params # => {:id=>"23"}
666
+ route.routable? # => true
667
+
668
+ route = router.recognize('/books/23', method: :post)
669
+ route.verb # "POST"
670
+ route.routable? # => false
671
+ ```
672
+
673
+ ## Versioning
674
+
675
+ __Hanami::Router__ uses [Semantic Versioning 2.0.0](http://semver.org)
32
676
 
33
677
  ## Contributing
34
678
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/hanami-router.
679
+ 1. Fork it
680
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
681
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
682
+ 4. Push to the branch (`git push origin my-new-feature`)
683
+ 5. Create new Pull Request
684
+
685
+ ## Acknowledgements
686
+
687
+ Thanks to Joshua Hull ([@joshbuddy](https://github.com/joshbuddy)) for his
688
+ [http_router](http://rubygems.org/gems/http_router).
689
+
690
+ ## Copyright
691
+
692
+ Copyright © 2014-2016 Luca Guidi – Released under MIT License
36
693
 
694
+ This project was formerly known as Lotus (`lotus-router`).