hanami-router 0.0.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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`).