lotus-router 0.0.0 → 0.1.0

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/.coveralls.yml +2 -0
  3. data/.gitignore +5 -13
  4. data/.travis.yml +5 -0
  5. data/.yardopts +3 -0
  6. data/Gemfile +10 -3
  7. data/README.md +470 -6
  8. data/Rakefile +10 -1
  9. data/benchmarks/callable +23 -0
  10. data/benchmarks/named_routes +72 -0
  11. data/benchmarks/resource +44 -0
  12. data/benchmarks/resources +58 -0
  13. data/benchmarks/routes +67 -0
  14. data/benchmarks/run.sh +11 -0
  15. data/benchmarks/utils.rb +56 -0
  16. data/lib/lotus-router.rb +1 -0
  17. data/lib/lotus/router.rb +752 -3
  18. data/lib/lotus/router/version.rb +2 -2
  19. data/lib/lotus/routing/endpoint.rb +114 -0
  20. data/lib/lotus/routing/endpoint_resolver.rb +251 -0
  21. data/lib/lotus/routing/http_router.rb +130 -0
  22. data/lib/lotus/routing/namespace.rb +86 -0
  23. data/lib/lotus/routing/resource.rb +73 -0
  24. data/lib/lotus/routing/resource/action.rb +340 -0
  25. data/lib/lotus/routing/resource/options.rb +48 -0
  26. data/lib/lotus/routing/resources.rb +40 -0
  27. data/lib/lotus/routing/resources/action.rb +123 -0
  28. data/lib/lotus/routing/route.rb +53 -0
  29. data/lotus-router.gemspec +16 -12
  30. data/test/fixtures.rb +193 -0
  31. data/test/integration/client_error_test.rb +16 -0
  32. data/test/integration/pass_on_response_test.rb +13 -0
  33. data/test/named_routes_test.rb +123 -0
  34. data/test/namespace_test.rb +289 -0
  35. data/test/new_test.rb +67 -0
  36. data/test/redirect_test.rb +33 -0
  37. data/test/resource_test.rb +128 -0
  38. data/test/resources_test.rb +136 -0
  39. data/test/routing/endpoint_resolver_test.rb +110 -0
  40. data/test/routing/resource/options_test.rb +36 -0
  41. data/test/routing_test.rb +99 -0
  42. data/test/test_helper.rb +32 -0
  43. data/test/version_test.rb +7 -0
  44. metadata +102 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 37d48dce216b9c22c8472e65075ef3e591cdd347
4
- data.tar.gz: 49906b3a4e85f0fc632eef8a6b00f27c4165d9d8
3
+ metadata.gz: ff45eb9a0d8db10815f562e9d5af4e5cf2649b7a
4
+ data.tar.gz: 3a6e353ac3840fc2bab5a0324ec50294493efa20
5
5
  SHA512:
6
- metadata.gz: 5541516f19802d8e34b1daeebec19ceb4f75672a59f8de12dec86a8c0855195fc6d68ee0a3d46346e47f983190aee527bb706c762751e9f9c1220b5e5b38c617
7
- data.tar.gz: e449094e8eb2583434924e269f484942011cc641dc0c017afa03aefe7ddaba382bfbe6c2418d7b3be70e4daf07ab49e2849428a7ba373f0095b6f2bffbf98d08
6
+ metadata.gz: 5aba74771a3578cb3654a687b428e9fdca6bbdc2cc186dc1777ff28b9862a4a9f50d7daa9de80fbfc191bcf67ca3ab35eabfdcdbffedfb9d0043aa62601d8b72
7
+ data.tar.gz: e0d18ad2c7e1cdb275a58871a321cd635b383c1c6308635cfeab29bfe5859e62eeeab0c728414ba118601787855a3b3e01fc602120ddf0c45304af63b20d84e4
data/.coveralls.yml ADDED
@@ -0,0 +1,2 @@
1
+ service_name: travis-pro
2
+ repo_token: RWmBgbtOIElBQus1VYqcrDotPcgtz1Seu
data/.gitignore CHANGED
@@ -1,17 +1,9 @@
1
1
  *.gem
2
- *.rbc
2
+ .devnotes
3
+ .greenbar
3
4
  .bundle
4
- .config
5
- .yardoc
6
5
  Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
- coverage
6
+ .yardoc
10
7
  doc/
11
- lib/bundler/man
12
- pkg
13
- rdoc
14
- spec/reports
15
- test/tmp
16
- test/version_tmp
17
- tmp
8
+ coverage/
9
+ tmp/
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ script: 'bundle exec rake'
3
+ rvm:
4
+ - 2.0.0
5
+ - 2.1.0
data/.yardopts ADDED
@@ -0,0 +1,3 @@
1
+ -
2
+ LICENSE.txt
3
+ lib/**/*.rb
data/Gemfile CHANGED
@@ -1,4 +1,11 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in lotus-router.gemspec
1
+ source 'http://rubygems.org'
4
2
  gemspec
3
+
4
+ unless ENV['TRAVIS']
5
+ gem 'debugger', require: false, platforms: :ruby
6
+ gem 'yard', require: false
7
+ gem 'simplecov', require: false
8
+ gem 'lotus-utils', require: false, path: '../lotus-utils'
9
+ end
10
+
11
+ gem 'coveralls', require: false
data/README.md CHANGED
@@ -1,29 +1,493 @@
1
1
  # Lotus::Router
2
2
 
3
- TODO: Write a gem description
3
+ Rack compatible, lightweight and fast HTTP Router for [Lotus](http://lotusrb.org).
4
+
5
+ ## Status
6
+
7
+ [![Gem Version](https://badge.fury.io/rb/lotus-router.png)](http://badge.fury.io/rb/lotus-router)
8
+ [![Build Status](https://secure.travis-ci.org/lotus/router.png?branch=master)](http://travis-ci.org/lotus/router?branch=master)
9
+ [![Coverage](https://coveralls.io/repos/lotus/router/badge.png?branch=master)](https://coveralls.io/r/lotus/router)
10
+ [![Code Climate](https://codeclimate.com/github/lotus/router.png)](https://codeclimate.com/github/lotus/router)
11
+ [![Dependencies](https://gemnasium.com/lotus/router.png)](https://gemnasium.com/lotus/router)
12
+
13
+ ## Contact
14
+
15
+ * Home page: http://lotusrb.org
16
+ * Mailing List: http://lotusrb.org/mailing-list
17
+ * API Doc: http://rdoc.info/gems/lotus-router
18
+ * Bugs/Issues: https://github.com/lotus/router/issues
19
+ * Support: http://stackoverflow.com/questions/tagged/lotusrb
20
+
21
+ ## Rubies
22
+
23
+ __Lotus::Router__ supports Ruby (MRI) 2+
24
+
4
25
 
5
26
  ## Installation
6
27
 
7
28
  Add this line to your application's Gemfile:
8
29
 
9
- gem 'lotus-router'
30
+ ```ruby
31
+ gem 'lotus-router'
32
+ ```
10
33
 
11
34
  And then execute:
12
35
 
13
- $ bundle
36
+ ```shell
37
+ $ bundle
38
+ ```
14
39
 
15
40
  Or install it yourself as:
16
41
 
17
- $ gem install lotus-router
42
+ ```shell
43
+ $ gem install lotus-router
44
+ ```
45
+
46
+ ## Getting Started
47
+
48
+ ```ruby
49
+ require 'lotus/router'
50
+
51
+ app = Lotus::Router.new do
52
+ get '/', to: ->(env) { [200, {}, ['Welcome to Lotus::Router!']] }
53
+ end
54
+
55
+ Rack::Server.start app: app, Port: 2306
56
+ ```
18
57
 
19
58
  ## Usage
20
59
 
21
- TODO: Write usage instructions here
60
+ __Lotus::Router__ is designed to work as a standalone framework or within a
61
+ context of a [Lotus](http://lotusrb.org) application.
62
+
63
+ For the standalone usage, it supports neat features:
64
+
65
+ ### A Beautiful DSL:
66
+
67
+ ```ruby
68
+ Lotus::Router.new do
69
+ get '/', to: ->(env) { [200, {}, ['Hi!']] }
70
+ get '/dashboard', to: DashboardController::Index
71
+ get '/rack-app', to: RackApp.new
72
+ get '/flowers', to: 'flowers#index'
73
+ get '/flowers/:id', to: 'flowers#show'
74
+
75
+ redirect '/legacy', to: '/'
76
+
77
+ namespace 'admin' do
78
+ get '/users', to: UsersController::Index
79
+ end
80
+
81
+ resource 'identity' do
82
+ member do
83
+ get '/avatar'
84
+ end
85
+
86
+ collection do
87
+ get '/api_keys'
88
+ end
89
+ end
90
+
91
+ resources 'robots' do
92
+ member do
93
+ patch '/activate'
94
+ end
95
+
96
+ collection do
97
+ get '/search'
98
+ end
99
+ end
100
+ end
101
+ ```
102
+
103
+
104
+
105
+ ### Fixed string matching:
106
+
107
+ ```ruby
108
+ router = Lotus::Router.new
109
+ router.get '/lotus', to: ->(env) { [200, {}, ['Hello from Lotus!']] }
110
+ ```
111
+
112
+
113
+
114
+ ### String matching with variables:
115
+
116
+ ```ruby
117
+ router = Lotus::Router.new
118
+ router.get '/flowers/:id', to: ->(env) { [200, {}, ["Hello from Flower no. #{ env['router.params'][:id] }!"]] }
119
+ ```
120
+
121
+
122
+
123
+ ### Variables Constraints:
124
+
125
+ ```ruby
126
+ router = Lotus::Router.new
127
+ router.get '/flowers/:id', id: /\d+/, to: ->(env) { [200, {}, [":id must be a number!"]] }
128
+ ```
129
+
130
+
131
+
132
+ ### String matching with globbling:
133
+
134
+ ```ruby
135
+ router = Lotus::Router.new
136
+ router.get '/*', to: ->(env) { [200, {}, ["This is catch all: #{ env['router.params'].inspect }!"]] }
137
+ ```
138
+
139
+
140
+
141
+ ### String matching with optional tokens:
142
+
143
+ ```ruby
144
+ router = Lotus::Router.new
145
+ router.get '/lotus(.:format)' to: ->(env) { [200, {}, ["You've requested #{ env['router.params'][:format] }!"]] }
146
+ ```
147
+
148
+
149
+
150
+ ### Support for the most common HTTP methods:
151
+
152
+ ```ruby
153
+ router = Lotus::Router.new
154
+ endpoint = ->(env) { [200, {}, ['Hello from Lotus!']] }
155
+
156
+ router.get '/lotus', to: endpoint
157
+ router.post '/lotus', to: endpoint
158
+ router.put '/lotus', to: endpoint
159
+ router.patch '/lotus', to: endpoint
160
+ router.delete '/lotus', to: endpoint
161
+ router.trace '/lotus', to: endpoint
162
+ ```
163
+
164
+
165
+
166
+ ### Redirect:
167
+
168
+ ```ruby
169
+ router = Lotus::Router.new
170
+ router.get '/redirect_destination', to: ->(env) { [200, {}, ['Redirect destination!']] }
171
+ router.redirect '/legacy', to: '/redirect_destination'
172
+ ```
173
+
174
+
175
+
176
+ ### Named routes:
177
+
178
+ ```ruby
179
+ router = Lotus::Router.new(scheme: 'https', host: 'lotusrb.org')
180
+ router.get '/lotus', to: ->(env) { [200, {}, ['Hello from Lotus!']] }, as: :lotus
181
+
182
+ router.path(:lotus) # => "/lotus"
183
+ router.url(:lotus) # => "https://lotusrb.org/lotus"
184
+ ```
185
+
186
+
187
+
188
+ ### Namespaced routes:
189
+
190
+ ```ruby
191
+ router = Lotus::Router.new
192
+ router.namespace 'animals' do
193
+ namespace 'mammals' do
194
+ get '/cats', to: ->(env) { [200, {}, ['Meow!']] }, as: :cats
195
+ end
196
+ end
197
+
198
+ # or
199
+
200
+ router.get '/cats', prefix: '/animals/mammals', to:->(env) { [200, {}, ['Meow!']] }, as: :cats
201
+
202
+ # and it generates:
203
+
204
+ router.path(:animals_mammals_cats) # => "/animals/mammals/cats"
205
+ ```
206
+
207
+
208
+
209
+ ### Duck typed endpoints:
210
+
211
+ Everything that responds to `#call` is invoked as it is:
212
+
213
+ ```ruby
214
+ router = Lotus::Router.new
215
+ router.get '/lotus', to: ->(env) { [200, {}, ['Hello from Lotus!']] }
216
+ router.get '/middleware', to: Middleware
217
+ router.get '/rack-app', to: RackApp.new
218
+ router.get '/method', to: ActionControllerSubclass.action(:new)
219
+ ```
220
+
221
+
222
+ If it's a string, it tries to instantiate a class from it:
223
+
224
+ ```ruby
225
+ class RackApp
226
+ def call(env)
227
+ # ...
228
+ end
229
+ end
230
+
231
+ router = Lotus::Router.new
232
+ router.get '/lotus', to: 'rack_app' # it will map to RackApp.new
233
+ ```
234
+
235
+ It also supports Controller + Action syntax:
236
+
237
+ ```ruby
238
+ class FlowersController
239
+ class Index
240
+ def call(env)
241
+ # ...
242
+ end
243
+ end
244
+ end
245
+
246
+ router = Lotus::Router.new
247
+ router.get '/flowers', to: 'flowers#index' # it will map to FlowersController::Index.new
248
+ ```
249
+
250
+
251
+
252
+ ### Implicit Not Found (404):
253
+
254
+ ```ruby
255
+ router = Lotus::Router.new
256
+ router.call(Rack::MockRequest.env_for('/unknown')).status # => 404
257
+ ```
258
+
259
+
260
+
261
+ ### RESTful Resource:
262
+
263
+ ```ruby
264
+ router = Lotus::Router.new
265
+ router.resource 'identity'
266
+ ```
267
+
268
+ It will map:
269
+
270
+ <table>
271
+ <tr>
272
+ <th>Verb</th>
273
+ <th>Path</th>
274
+ <th>Action</th>
275
+ <th>Name</th>
276
+ <th>Named Route</th>
277
+ </tr>
278
+ <tr>
279
+ <td>GET</td>
280
+ <td>/identity</td>
281
+ <td>IdentityController::Show</td>
282
+ <td>:show</td>
283
+ <td>:identity</td>
284
+ </tr>
285
+ <tr>
286
+ <td>GET</td>
287
+ <td>/identity/new</td>
288
+ <td>IdentityController::New</td>
289
+ <td>:new</td>
290
+ <td>:new_identity</td>
291
+ </tr>
292
+ <tr>
293
+ <td>POST</td>
294
+ <td>/identity</td>
295
+ <td>IdentityController::Create</td>
296
+ <td>:create</td>
297
+ <td>:identity</td>
298
+ </tr>
299
+ <tr>
300
+ <td>GET</td>
301
+ <td>/identity/edit</td>
302
+ <td>IdentityController::Edit</td>
303
+ <td>:edit</td>
304
+ <td>:edit_identity</td>
305
+ </tr>
306
+ <tr>
307
+ <td>PATCH</td>
308
+ <td>/identity</td>
309
+ <td>IdentityController::Update</td>
310
+ <td>:update</td>
311
+ <td>:identity</td>
312
+ </tr>
313
+ <tr>
314
+ <td>DELETE</td>
315
+ <td>/identity</td>
316
+ <td>IdentityController::Destroy</td>
317
+ <td>:destroy</td>
318
+ <td>:identity</td>
319
+ </tr>
320
+ </table>
321
+
322
+ If you don't need all the default endpoints, just do:
323
+
324
+ ```ruby
325
+ router = Lotus::Router.new
326
+ router.resource 'identity', only: [:edit, :update]
327
+
328
+ # which is equivalent to:
329
+
330
+ router.resource 'identity', except: [:show, :new, :create, :destroy]
331
+ ```
332
+
333
+
334
+ If you need extra endpoints:
335
+
336
+ ```ruby
337
+ router = Lotus::Router.new
338
+ router.resource 'identity' do
339
+ member do
340
+ get '/avatar' # maps to IdentityController::Avatar
341
+ end
342
+
343
+ collection do
344
+ get '/authorizations' # maps to IdentityController::Authorizations
345
+ end
346
+ end
347
+
348
+ router.path(:avatar_identity) # => /identity/avatar
349
+ router.path(:authorizations_identity) # => /identity/authorizations
350
+ ```
351
+
352
+
353
+
354
+ ### RESTful Resources:
355
+
356
+ ```ruby
357
+ router = Lotus::Router.new
358
+ router.resources 'flowers'
359
+ ```
360
+
361
+ It will map:
362
+
363
+ <table>
364
+ <tr>
365
+ <th>Verb</th>
366
+ <th>Path</th>
367
+ <th>Action</th>
368
+ <th>Name</th>
369
+ <th>Named Route</th>
370
+ </tr>
371
+ <tr>
372
+ <td>GET</td>
373
+ <td>/flowers</td>
374
+ <td>FlowersController::Index</td>
375
+ <td>:index</td>
376
+ <td>:flowers</td>
377
+ </tr>
378
+ <tr>
379
+ <td>GET</td>
380
+ <td>/flowers/:id</td>
381
+ <td>FlowersController::Show</td>
382
+ <td>:show</td>
383
+ <td>:flowers</td>
384
+ </tr>
385
+ <tr>
386
+ <td>GET</td>
387
+ <td>/flowers/new</td>
388
+ <td>FlowersController::New</td>
389
+ <td>:new</td>
390
+ <td>:new_flowers</td>
391
+ </tr>
392
+ <tr>
393
+ <td>POST</td>
394
+ <td>/flowers</td>
395
+ <td>FlowersController::Create</td>
396
+ <td>:create</td>
397
+ <td>:flowers</td>
398
+ </tr>
399
+ <tr>
400
+ <td>GET</td>
401
+ <td>/flowers/:id/edit</td>
402
+ <td>FlowersController::Edit</td>
403
+ <td>:edit</td>
404
+ <td>:edit_flowers</td>
405
+ </tr>
406
+ <tr>
407
+ <td>PATCH</td>
408
+ <td>/flowers/:id</td>
409
+ <td>FlowersController::Update</td>
410
+ <td>:update</td>
411
+ <td>:flowers</td>
412
+ </tr>
413
+ <tr>
414
+ <td>DELETE</td>
415
+ <td>/flowers/:id</td>
416
+ <td>FlowersController::Destroy</td>
417
+ <td>:destroy</td>
418
+ <td>:flowers</td>
419
+ </tr>
420
+ </table>
421
+
422
+
423
+ ```ruby
424
+ router.path(:flowers) # => /flowers
425
+ router.path(:flowers, id: 23) # => /flowers/23
426
+ router.path(:edit_flowers, id: 23) # => /flowers/23/edit
427
+ ```
428
+
429
+
430
+
431
+ If you don't need all the default endpoints, just do:
432
+
433
+ ```ruby
434
+ router = Lotus::Router.new
435
+ router.resources 'flowers', only: [:new, :create, :show]
436
+
437
+ # which is equivalent to:
438
+
439
+ router.resources 'flowers', except: [:index, :edit, :update, :destroy]
440
+ ```
441
+
442
+
443
+ If you need extra endpoints:
444
+
445
+ ```ruby
446
+ router = Lotus::Router.new
447
+ router.resources 'flowers' do
448
+ member do
449
+ get '/toggle' # maps to FlowersController::Toggle
450
+ end
451
+ collection do
452
+ get '/search' # maps to FlowersController::Search
453
+ end
454
+ end
455
+
456
+ router.path(:toggle_flowers, id: 23) # => /flowers/23/toggle
457
+ router.path(:search_flowers) # => /flowers/search
458
+ ```
459
+
460
+ ## Testing
461
+
462
+ ```ruby
463
+ require 'lotus/router'
464
+ require 'rack/request'
465
+
466
+ router = Lotus::Router.new do
467
+ get '/', to: ->(env) { [200, {}, ['Hi!']] }
468
+ end
469
+
470
+ app = Rack::MockRequest.new(router)
471
+ app.get('/') # => #<Rack::MockResponse:0x007fc4540dc238 ...>
472
+ ```
473
+
474
+ ## Versioning
475
+
476
+ __Lotus::Router__ uses [Semantic Versioning 2.0.0](http://semver.org)
22
477
 
23
478
  ## Contributing
24
479
 
25
- 1. Fork it ( http://github.com/<my-github-username>/lotus-router/fork )
480
+ 1. Fork it
26
481
  2. Create your feature branch (`git checkout -b my-new-feature`)
27
482
  3. Commit your changes (`git commit -am 'Add some feature'`)
28
483
  4. Push to the branch (`git push origin my-new-feature`)
29
484
  5. Create new Pull Request
485
+
486
+ ## Acknowledgements
487
+
488
+ Thanks to Joshua Hull ([@joshbuddy](https://github.com/joshbuddy)) for his
489
+ [http_router](http://rubygems.org/gems/http_router).
490
+
491
+ ## Copyright
492
+
493
+ Copyright 2014 Luca Guidi – Released under MIT License