lotus-router 0.0.0 → 0.1.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.
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