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
data/Rakefile CHANGED
@@ -1 +1,10 @@
1
- require "bundler/gem_tasks"
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'bundler/gem_tasks'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.pattern = 'test/**/*_test.rb'
7
+ t.libs.push 'test'
8
+ end
9
+
10
+ task default: :test
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby -W0
2
+ load File.dirname(__FILE__) + '/utils.rb'
3
+
4
+ Benchmark.bm(50) do |b|
5
+ ##
6
+ # Callable
7
+ #
8
+ router = Lotus::Router.new
9
+ app = Rack::MockRequest.new(router)
10
+
11
+ b.report 'generating from callable endpoints' do
12
+ $callable.each do |route|
13
+ router.get route, &$endpoint
14
+ end
15
+ end
16
+
17
+ b.report 'recognizing from callable endpoints' do
18
+ TIMES.times do
19
+ app.get($callable.sample)
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env ruby -W0
2
+ load File.dirname(__FILE__) + '/utils.rb'
3
+
4
+ Benchmark.bm(50) do |b|
5
+
6
+ ##
7
+ # Callable
8
+ #
9
+ router = Lotus::Router.new
10
+ app = Rack::MockRequest.new(router)
11
+
12
+ b.report 'generating named routes' do
13
+ $named_routes.each do |(name, as)|
14
+ router.get name, to: $endpoint, as: as
15
+ end
16
+ end
17
+
18
+ b.report 'recognizing named routes' do
19
+ TIMES.times do
20
+ app.get($named_routes.sample.first)
21
+ end
22
+ end
23
+
24
+ ##
25
+ # Class
26
+ #
27
+ router = Lotus::Router.new
28
+ app = Rack::MockRequest.new(router)
29
+
30
+ $named_routes.each do |(name, _)|
31
+ eval "#{ Lotus::Utils::String.new(name).classify } = Class.new($controller)"
32
+ end
33
+
34
+ b.report 'generating named routes (class endpoints)' do
35
+ $named_routes.each do |(name, as)|
36
+ router.get name, to: $endpoint, as: as
37
+ end
38
+ end
39
+
40
+ b.report 'recognizing named routes (class endpoints)' do
41
+ TIMES.times do
42
+ app.get($named_routes.sample.first)
43
+ end
44
+ end
45
+
46
+ ##
47
+ # Lazy
48
+ #
49
+ router = Lotus::Router.new
50
+ app = Rack::MockRequest.new(router)
51
+
52
+ $lazy = $lazy.map do |r|
53
+ [r, r.to_sym]
54
+ end
55
+
56
+ b.report 'generating routes (lazy endpoints)' do
57
+ $lazy.each do |(name, as)|
58
+ router.get name, to: name, as: as
59
+ end
60
+ end
61
+
62
+ $lazy.each do |(name, _)|
63
+ eval "#{ Lotus::Utils::String.new(name).classify } = Class.new($controller)"
64
+ end
65
+
66
+ b.report 'recognizing routes (lazy endpoints)' do
67
+ TIMES.times do
68
+ app.get($lazy.sample.first)
69
+ end
70
+ end
71
+ end
72
+
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env ruby -W0
2
+ load File.dirname(__FILE__) + '/utils.rb'
3
+
4
+ Benchmark.bm(50) do |b|
5
+ ##
6
+ # Class
7
+ #
8
+ router = Lotus::Router.new
9
+ app = Rack::MockRequest.new(router)
10
+
11
+ b.report 'generating resource (class endpoints)' do
12
+ $resource.each do |r|
13
+ router.resource r
14
+ end
15
+ end
16
+
17
+ b.report 'recognizing resource (class endpoints)' do
18
+ TIMES.times do
19
+ app.get($resource.sample)
20
+ end
21
+ end
22
+
23
+ ##
24
+ # Lazy
25
+ #
26
+ router = Lotus::Router.new
27
+ app = Rack::MockRequest.new(router)
28
+
29
+ b.report 'generating resource (lazy endpoints)' do
30
+ $lazy.each do |route|
31
+ router.resource route
32
+ end
33
+ end
34
+
35
+ $lazy.each do |w|
36
+ eval "#{ Lotus::Utils::String.new(w).classify }Controller = Class.new($resource_controller)"
37
+ end
38
+
39
+ b.report 'recognizing routes (lazy endpoints)' do
40
+ TIMES.times do
41
+ app.get($lazy.sample)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env ruby -W0
2
+ load File.dirname(__FILE__) + '/utils.rb'
3
+
4
+ Benchmark.bm(50) do |b|
5
+ ##
6
+ # Class
7
+ #
8
+ router = Lotus::Router.new
9
+ app = Rack::MockRequest.new(router)
10
+
11
+ b.report 'generating resources (class endpoints)' do
12
+ $resources.each do |r|
13
+ router.resources r
14
+ end
15
+ end
16
+
17
+ b.report 'recognizing resources (class endpoints)' do
18
+ TIMES.times do
19
+ app.get($resources.sample)
20
+ end
21
+ end
22
+
23
+ b.report 'recognizing resources with param (class endpoints)' do
24
+ param = '/23'
25
+ TIMES.times do
26
+ app.get($resources.sample + param)
27
+ end
28
+ end
29
+
30
+ ##
31
+ # Lazy
32
+ #
33
+ router = Lotus::Router.new
34
+ app = Rack::MockRequest.new(router)
35
+
36
+ b.report 'generating resources (lazy endpoints)' do
37
+ $lazy.each do |r|
38
+ router.resources r
39
+ end
40
+ end
41
+
42
+ $lazy.each do |w|
43
+ eval "#{ Lotus::Utils::String.new(w).classify }Controller = Class.new($resources_controller)"
44
+ end
45
+
46
+ b.report 'recognizing resources (lazy endpoints)' do
47
+ TIMES.times do
48
+ app.get($lazy.sample)
49
+ end
50
+ end
51
+
52
+ b.report 'recognizing resources with param (lazy endpoints)' do
53
+ param = '/23'
54
+ TIMES.times do
55
+ app.get($lazy.sample + param)
56
+ end
57
+ end
58
+ end
data/benchmarks/routes ADDED
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env ruby -W0
2
+ load File.dirname(__FILE__) + '/utils.rb'
3
+
4
+ Benchmark.bm(50) do |b|
5
+
6
+ ##
7
+ # Callable
8
+ #
9
+ router = Lotus::Router.new
10
+ app = Rack::MockRequest.new(router)
11
+
12
+ b.report 'generating routes' do
13
+ $routes.each do |route|
14
+ router.get route, to: $endpoint
15
+ end
16
+ end
17
+
18
+ b.report 'recognizing routes' do
19
+ TIMES.times do
20
+ app.get($routes.sample)
21
+ end
22
+ end
23
+
24
+ ##
25
+ # Class
26
+ #
27
+ router = Lotus::Router.new
28
+ app = Rack::MockRequest.new(router)
29
+
30
+ $routes.each do |route|
31
+ eval "#{ Lotus::Utils::String.new(route).classify } = Class.new($controller)"
32
+ end
33
+
34
+ b.report 'generating routes (class endpoints)' do
35
+ $routes.each do |route|
36
+ router.get route, to: route
37
+ end
38
+ end
39
+
40
+ b.report 'recognizing routes (class endpoints)' do
41
+ TIMES.times do
42
+ app.get($routes.sample)
43
+ end
44
+ end
45
+
46
+ ##
47
+ # Lazy
48
+ #
49
+ router = Lotus::Router.new
50
+ app = Rack::MockRequest.new(router)
51
+
52
+ b.report 'generating routes (lazy endpoints)' do
53
+ $lazy.each do |route|
54
+ router.get route, to: route
55
+ end
56
+ end
57
+
58
+ $lazy.each do |route|
59
+ eval "#{ Lotus::Utils::String.new(route).classify } = Class.new($controller)"
60
+ end
61
+
62
+ b.report 'recognizing routes (lazy endpoints)' do
63
+ TIMES.times do
64
+ app.get($lazy.sample)
65
+ end
66
+ end
67
+ end
data/benchmarks/run.sh ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env bash
2
+ benchmarks=$(pwd)/$( dirname "${BASH_SOURCE[0]}" )/*
3
+
4
+ for benchmark in $benchmarks
5
+ do
6
+ if ! [[ "${benchmark#*.}" =~ (rb|sh)$ ]]; then
7
+ $benchmark
8
+ echo "================================================================================================"
9
+ echo ""
10
+ fi
11
+ done
@@ -0,0 +1,56 @@
1
+ $:.unshift 'lib'
2
+ require 'benchmark'
3
+ require 'lotus/router'
4
+
5
+ # head -$((${RANDOM} % `wc -l < /usr/share/dict/words` + 1)) /usr/share/dict/words | tail -1
6
+
7
+ BATCH_SIZE = (ENV['BATCH_SIZE'] || 1000 ).to_i
8
+ TIMES = (ENV['TIMES'] || 100000).to_i
9
+
10
+ dict = File.readlines('/usr/share/dict/words').each {|l| l.chomp! }.uniq
11
+ $routes, $named_routes, $callable, $resource, $resources, $lazy, _ = *dict.each_slice(BATCH_SIZE).to_a
12
+
13
+ puts "Loading #{ BATCH_SIZE } routes, calling for #{ TIMES } times...\n"
14
+
15
+ class Controller
16
+ def call(env)
17
+ [200, {}, ['']]
18
+ end
19
+ end
20
+
21
+ class ResourceController
22
+ class Action
23
+ def call(env)
24
+ [200, {}, ['']]
25
+ end
26
+ end
27
+ class New < Action; end
28
+ class Create < Action; end
29
+ class Show < Action; end
30
+ class Edit < Action; end
31
+ class Update < Action; end
32
+ class Destroy < Action; end
33
+ end
34
+
35
+ class ResourcesController < ResourceController
36
+ class Index < Action; end
37
+ end
38
+
39
+ $endpoint = ->(env) { [200, {}, ['']] }
40
+ $controller = Controller
41
+ $resource_controller = ResourceController
42
+ $resources_controller = ResourcesController
43
+
44
+ $named_routes = $named_routes.map do |r|
45
+ [r, r.to_sym]
46
+ end
47
+
48
+ $resource.each do |w|
49
+ eval "#{ Lotus::Utils::String.new(w).classify }Controller = Class.new($resource_controller)"
50
+ end
51
+
52
+ $resources.each do |w|
53
+ eval "#{ Lotus::Utils::String.new(w).classify }Controller = Class.new($resources_controller)"
54
+ end
55
+
56
+ GC.start
@@ -0,0 +1 @@
1
+ require 'lotus/router'
data/lib/lotus/router.rb CHANGED
@@ -1,7 +1,756 @@
1
- require "lotus/router/version"
1
+ require 'lotus/routing/http_router'
2
+ require 'lotus/routing/namespace'
3
+ require 'lotus/routing/resource'
4
+ require 'lotus/routing/resources'
2
5
 
3
6
  module Lotus
4
- module Router
5
- # Your code goes here...
7
+ # Rack compatible, lightweight and fast HTTP Router.
8
+ #
9
+ # @since 0.1.0
10
+ #
11
+ # @example It offers an intuitive DSL, that supports most of the HTTP verbs:
12
+ # require 'lotus/router'
13
+ #
14
+ # endpoint = ->(env) { [200, {}, ['Welcome to Lotus::Router!']] }
15
+ # router = Lotus::Router.new do
16
+ # get '/', to: endpoint
17
+ # post '/', to: endpoint
18
+ # put '/', to: endpoint
19
+ # patch '/', to: endpoint
20
+ # delete '/', to: endpoint
21
+ # head '/', to: endpoint
22
+ # options '/', to: endpoint
23
+ # trace '/', to: endpoint
24
+ # end
25
+ #
26
+ #
27
+ #
28
+ # @example Specify an endpoint with `:to` (Rack compatible object)
29
+ # require 'lotus/router'
30
+ #
31
+ # endpoint = ->(env) { [200, {}, ['Welcome to Lotus::Router!']] }
32
+ # router = Lotus::Router.new do
33
+ # get '/', to: endpoint
34
+ # end
35
+ #
36
+ # # :to is mandatory for the default resolver (`Lotus::Routing::EndpointResolver.new`),
37
+ # # This behavior can be changed by passing a custom resolver to `Lotus::Router#initialize`
38
+ #
39
+ #
40
+ #
41
+ # @example Specify an endpoint with `:to` (controller and action string)
42
+ # require 'lotus/router'
43
+ #
44
+ # router = Lotus::Router.new do
45
+ # get '/', to: 'articles#show' # => ArticlesController::Show
46
+ # end
47
+ #
48
+ # # This is a builtin feature for a Lotus::Controller convention.
49
+ #
50
+ #
51
+ #
52
+ # @example Specify a prefix with `:prefix`
53
+ # require 'lotus/router'
54
+ #
55
+ # endpoint = ->(env) { [200, {}, ['Welcome to Lotus::Router!']] }
56
+ # router = Lotus::Router.new do
57
+ # get '/welcome', to: endpoint, prefix: 'dashboard' # => '/dashboard/welcome'
58
+ # end
59
+ #
60
+ # # :prefix isn't mandatory for the default resolver (`Lotus::Routing::EndpointResolver.new`),
61
+ # # This behavior can be changed by passing a custom resolver to `Lotus::Router#initialize`
62
+ #
63
+ #
64
+ #
65
+ # @example Specify a named route with `:as`
66
+ # require 'lotus/router'
67
+ #
68
+ # endpoint = ->(env) { [200, {}, ['Welcome to Lotus::Router!']] }
69
+ # router = Lotus::Router.new(scheme: 'https', host: 'lotusrb.org') do
70
+ # get '/', to: endpoint, as: :root
71
+ # end
72
+ #
73
+ # router.path(:root) # => '/'
74
+ # router.url(:root) # => 'https://lotusrb.org/'
75
+ #
76
+ # # This isn't mandatory for the default route class (`Lotus::Routing::Route`),
77
+ # # This behavior can be changed by passing a custom route to `Lotus::Router#initialize`
78
+ class Router
79
+ # Initialize the router.
80
+ #
81
+ # @param options [Hash] the options to initialize the router
82
+ #
83
+ # @option options [String] :scheme The HTTP scheme (defaults to `"http"`)
84
+ # @option options [String] :host The URL host (defaults to `"localhost"`)
85
+ # @option options [String] :port The URL port (defaults to `"80"`)
86
+ # @option options [Object, #resolve, #find, #action_separator] :resolver
87
+ # the route resolver (defaults to `Lotus::Routing::EndpointResolver.new`)
88
+ # @option options [Object, #generate] :route the route class
89
+ # (defaults to `Lotus::Routing::Route`)
90
+ # @option options [String] :action_separator the separator between controller
91
+ # and action name (eg. 'dashboard#show', where '#' is the :action_separator)
92
+ #
93
+ # @param blk [Proc] the optional block to define the routes
94
+ #
95
+ # @return [Lotus::Router] self
96
+ #
97
+ # @since 0.1.0
98
+ #
99
+ # @example
100
+ # require 'lotus/router'
101
+ #
102
+ # endpoint = ->(env) { [200, {}, ['Welcome to Lotus::Router!']] }
103
+ #
104
+ # router = Lotus::Router.new
105
+ # router.get '/', to: endpoint
106
+ #
107
+ # # or
108
+ #
109
+ # router = Lotus::Router.new do
110
+ # get '/', to: endpoint
111
+ # end
112
+ def initialize(options = {}, &blk)
113
+ @router = Routing::HttpRouter.new(options)
114
+ instance_eval(&blk) if block_given?
115
+ end
116
+
117
+ # Defines a route that accepts a GET request for the given path.
118
+ #
119
+ # @param path [String] the relative URL to be matched
120
+ #
121
+ # @param options [Hash] the options to customize the route
122
+ # @option options [String,Proc,Class,Object#call] :to the endpoint
123
+ # @option options [String] :prefix an optional path prefix
124
+ #
125
+ # @param blk [Proc] the anonymous proc to be used as endpoint for the route
126
+ #
127
+ # @return [Lotus::Roting::Route] this may vary according to the :route
128
+ # option passed to the constructor
129
+ #
130
+ # @since 0.1.0
131
+ #
132
+ # @example Fixed matching string
133
+ # require 'lotus/router'
134
+ #
135
+ # router = Lotus::Router.new
136
+ # router.get '/lotus', to: ->(env) { [200, {}, ['Hello from Lotus!']] }
137
+ #
138
+ # @example String matching with variables
139
+ # require 'lotus/router'
140
+ #
141
+ # router = Lotus::Router.new
142
+ # router.get '/flowers/:id',
143
+ # to: ->(env) {
144
+ # [
145
+ # 200,
146
+ # {},
147
+ # ["Hello from Flower no. #{ env['router.params'][:id] }!"]
148
+ # ]
149
+ # }
150
+ #
151
+ # @example Variables Constraints
152
+ # require 'lotus/router'
153
+ #
154
+ # router = Lotus::Router.new
155
+ # router.get '/flowers/:id',
156
+ # id: /\d+/,
157
+ # to: ->(env) { [200, {}, [":id must be a number!"]] }
158
+ #
159
+ # @example String matching with globbling
160
+ # require 'lotus/router'
161
+ #
162
+ # router = Lotus::Router.new
163
+ # router.get '/*',
164
+ # to: ->(env) {
165
+ # [
166
+ # 200,
167
+ # {},
168
+ # ["This is catch all: #{ env['router.params'].inspect }!"]
169
+ # ]
170
+ # }
171
+ #
172
+ # @example String matching with optional tokens
173
+ # require 'lotus/router'
174
+ #
175
+ # router = Lotus::Router.new
176
+ # router.get '/lotus(.:format)',
177
+ # to: ->(env) {
178
+ # [200, {}, ["You've requested #{ env['router.params'][:format] }!"]]
179
+ # }
180
+ #
181
+ # @example Named routes
182
+ # require 'lotus/router'
183
+ #
184
+ # router = Lotus::Router.new(scheme: 'https', host: 'lotusrb.org')
185
+ # router.get '/lotus',
186
+ # to: ->(env) { [200, {}, ['Hello from Lotus!']] },
187
+ # as: :lotus
188
+ #
189
+ # router.path(:lotus) # => "/lotus"
190
+ # router.url(:lotus) # => "https://lotusrb.org/lotus"
191
+ #
192
+ # @example Prefixed routes
193
+ # require 'lotus/router'
194
+ #
195
+ # router = Lotus::Router.new
196
+ # router.get '/cats',
197
+ # prefix: '/animals/mammals',
198
+ # to: ->(env) { [200, {}, ['Meow!']] },
199
+ # as: :cats
200
+ #
201
+ # router.path(:animals_mammals_cats) # => "/animals/mammals/cats"
202
+ #
203
+ # @example Duck typed endpoints (Rack compatible objects)
204
+ # require 'lotus/router'
205
+ #
206
+ # router = Lotus::Router.new
207
+ #
208
+ # router.get '/lotus', to: ->(env) { [200, {}, ['Hello from Lotus!']] }
209
+ # router.get '/middleware', to: Middleware
210
+ # router.get '/rack-app', to: RackApp.new
211
+ # router.get '/method', to: ActionControllerSubclass.action(:new)
212
+ #
213
+ # # Everything that responds to #call is invoked as it is
214
+ #
215
+ # @example Duck typed endpoints (strings)
216
+ # require 'lotus/router'
217
+ #
218
+ # class RackApp
219
+ # def call(env)
220
+ # # ...
221
+ # end
222
+ # end
223
+ #
224
+ # router = Lotus::Router.new
225
+ # router.get '/lotus', to: 'rack_app' # it will map to RackApp.new
226
+ #
227
+ # @example Duck typed endpoints (string: controller + action)
228
+ # require 'lotus/router'
229
+ #
230
+ # class FlowersController
231
+ # class Index
232
+ # def call(env)
233
+ # # ...
234
+ # end
235
+ # end
236
+ # end
237
+ #
238
+ # router = Lotus::Router.new
239
+ # router.get '/flowers', to: 'flowers#index'
240
+ #
241
+ # # It will map to FlowersController::Index.new, which is the
242
+ # # Lotus::Controller convention.
243
+ def get(path, options = {}, &blk)
244
+ @router.get(path, options, &blk)
245
+ end
246
+
247
+ # Defines a route that accepts a POST request for the given path.
248
+ #
249
+ # @param path [String] the relative URL to be matched
250
+ #
251
+ # @param options [Hash] the options to customize the route
252
+ # @option options [String,Proc,Class,Object#call] :to the endpoint
253
+ # @option options [String] :prefix an optional path prefix
254
+ #
255
+ # @param blk [Proc] the anonymous proc to be used as endpoint for the route
256
+ #
257
+ # @return [Lotus::Roting::Route] this may vary according to the :route
258
+ # option passed to the constructor
259
+ #
260
+ # @see Lotus::Router#get
261
+ #
262
+ # @since 0.1.0
263
+ def post(path, options = {}, &blk)
264
+ @router.post(path, options, &blk)
265
+ end
266
+
267
+ # Defines a route that accepts a PUT request for the given path.
268
+ #
269
+ # @param path [String] the relative URL to be matched
270
+ #
271
+ # @param options [Hash] the options to customize the route
272
+ # @option options [String,Proc,Class,Object#call] :to the endpoint
273
+ # @option options [String] :prefix an optional path prefix
274
+ #
275
+ # @param blk [Proc] the anonymous proc to be used as endpoint for the route
276
+ #
277
+ # @return [Lotus::Roting::Route] this may vary according to the :route
278
+ # option passed to the constructor
279
+ #
280
+ # @see Lotus::Router#get
281
+ #
282
+ # @since 0.1.0
283
+ def put(path, options = {}, &blk)
284
+ @router.put(path, options, &blk)
285
+ end
286
+
287
+ # Defines a route that accepts a PATCH request for the given path.
288
+ #
289
+ # @param path [String] the relative URL to be matched
290
+ #
291
+ # @param options [Hash] the options to customize the route
292
+ # @option options [String,Proc,Class,Object#call] :to the endpoint
293
+ # @option options [String] :prefix an optional path prefix
294
+ #
295
+ # @param blk [Proc] the anonymous proc to be used as endpoint for the route
296
+ #
297
+ # @return [Lotus::Roting::Route] this may vary according to the :route
298
+ # option passed to the constructor
299
+ #
300
+ # @see Lotus::Router#get
301
+ #
302
+ # @since 0.1.0
303
+ def patch(path, options = {}, &blk)
304
+ @router.patch(path, options, &blk)
305
+ end
306
+
307
+ # Defines a route that accepts a DELETE request for the given path.
308
+ #
309
+ # @param path [String] the relative URL to be matched
310
+ #
311
+ # @param options [Hash] the options to customize the route
312
+ # @option options [String,Proc,Class,Object#call] :to the endpoint
313
+ # @option options [String] :prefix an optional path prefix
314
+ #
315
+ # @param blk [Proc] the anonymous proc to be used as endpoint for the route
316
+ #
317
+ # @return [Lotus::Roting::Route] this may vary according to the :route
318
+ # option passed to the constructor
319
+ #
320
+ # @see Lotus::Router#get
321
+ #
322
+ # @since 0.1.0
323
+ def delete(path, options = {}, &blk)
324
+ @router.delete(path, options, &blk)
325
+ end
326
+
327
+ # Defines a route that accepts a TRACE request for the given path.
328
+ #
329
+ # @param path [String] the relative URL to be matched
330
+ #
331
+ # @param options [Hash] the options to customize the route
332
+ # @option options [String,Proc,Class,Object#call] :to the endpoint
333
+ # @option options [String] :prefix an optional path prefix
334
+ #
335
+ # @param blk [Proc] the anonymous proc to be used as endpoint for the route
336
+ #
337
+ # @return [Lotus::Roting::Route] this may vary according to the :route
338
+ # option passed to the constructor
339
+ #
340
+ # @see Lotus::Router#get
341
+ #
342
+ # @since 0.1.0
343
+ def trace(path, options = {}, &blk)
344
+ @router.trace(path, options, &blk)
345
+ end
346
+
347
+ # Defines a route that accepts a OPTIONS request for the given path.
348
+ #
349
+ # @param path [String] the relative URL to be matched
350
+ #
351
+ # @param options [Hash] the options to customize the route
352
+ # @option options [String,Proc,Class,Object#call] :to the endpoint
353
+ # @option options [String] :prefix an optional path prefix
354
+ #
355
+ # @param blk [Proc] the anonymous proc to be used as endpoint for the route
356
+ #
357
+ # @return [Lotus::Roting::Route] this may vary according to the :route
358
+ # option passed to the constructor
359
+ #
360
+ # @see Lotus::Router#get
361
+ #
362
+ # @since 0.1.0
363
+ def options(path, options = {}, &blk)
364
+ @router.options(path, options, &blk)
365
+ end
366
+
367
+ # Defines an HTTP redirect
368
+ #
369
+ # @param path [String] the path that needs to be redirected
370
+ # @param options [Hash] the options to customize the redirect behavior
371
+ # @option options [Fixnum] the HTTP status to return (defaults to `302`)
372
+ #
373
+ # @return [Lotus::Routing::Route] the generated route.
374
+ # This may vary according to the `:route` option passed to the initializer
375
+ #
376
+ # @since 0.1.0
377
+ #
378
+ # @see Lotus::Router
379
+ #
380
+ # @example
381
+ # require 'lotus/router'
382
+ #
383
+ # Lotus::Router.new do
384
+ # redirect '/legacy', to: '/new_endpoint'
385
+ # redirect '/legacy2', to: '/new_endpoint2', code: 301
386
+ # end
387
+ #
388
+ # @example
389
+ # require 'lotus/router'
390
+ #
391
+ # router = Lotus::Router.new
392
+ # router.redirect '/legacy', to: '/new_endpoint'
393
+ def redirect(path, options = {}, &endpoint)
394
+ get(path).redirect @router.find(options), options[:code] || 302
395
+ end
396
+
397
+ # Defines a Ruby block: all the routes defined within it will be namespaced
398
+ # with the given prefix.
399
+ #
400
+ # Namespaces blocks can be nested multiple times.
401
+ #
402
+ # @param prefix [String] the path prefix
403
+ # @param blk [Proc] the block that defines the resources
404
+ #
405
+ # @return [Lotus::Routing::Namespace] the generated namespace.
406
+ #
407
+ # @since 0.1.0
408
+ #
409
+ # @see Lotus::Router
410
+ #
411
+ # @example Basic example
412
+ # require 'lotus/router'
413
+ #
414
+ # Lotus::Router.new do
415
+ # namespace 'trees' do
416
+ # get '/sequoia', to: endpoint # => '/trees/sequoia'
417
+ # end
418
+ #
419
+ # # equivalent to
420
+ #
421
+ # get '/sequoia', to: endpoint, prefix: 'trees' # => '/trees/sequoia'
422
+ # end
423
+ #
424
+ # @example Nested namespaces
425
+ # require 'lotus/router'
426
+ #
427
+ # Lotus::Router.new do
428
+ # namespace 'animals' do
429
+ # namespace 'mammals' do
430
+ # get '/cats', to: endpoint # => '/animals/mammals/cats'
431
+ # end
432
+ # end
433
+ # end
434
+ #
435
+ # @example
436
+ # require 'lotus/router'
437
+ #
438
+ # router = Lotus::Router.new
439
+ # router.namespace 'trees' do
440
+ # get '/sequoia', to: endpoint # => '/trees/sequoia'
441
+ # end
442
+ def namespace(prefix, &blk)
443
+ Routing::Namespace.new(self, prefix, &blk)
444
+ end
445
+
446
+ # Defines a set of named routes for a single RESTful resource.
447
+ # It has a built-in integration for Lotus::Controller.
448
+ #
449
+ # @param name [String] the name of the resource
450
+ # @param options [Hash] a set of options to customize the routes
451
+ # @option options [Array<Symbol>] :only a subset of the default routes
452
+ # that we want to generate
453
+ # @option options [Array<Symbol>] :except prevent the given routes to be
454
+ # generated
455
+ # @param blk [Proc] a block of code to generate additional routes
456
+ #
457
+ # @return [Lotus::Routing::Resource]
458
+ #
459
+ # @since 0.1.0
460
+ #
461
+ # @see Lotus::Routing::Resource
462
+ # @see Lotus::Routing::Resource::Action
463
+ # @see Lotus::Routing::Resource::Options
464
+ #
465
+ # @example Default usage
466
+ # require 'lotus/router'
467
+ #
468
+ # Lotus::Router.new do
469
+ # resource 'identity'
470
+ # end
471
+ #
472
+ # # It generates:
473
+ # #
474
+ # # +--------+----------------+-----------------------------+----------+----------------+
475
+ # # | Verb | Path | Action | Name | Named Route |
476
+ # # +--------+----------------+-----------------------------+----------+----------------+
477
+ # # | GET | /identity | IdentityController::Show | :show | :identity |
478
+ # # | GET | /identity/new | IdentityController::New | :new | :new_identity |
479
+ # # | POST | /identity | IdentityController::Create | :create | :identity |
480
+ # # | GET | /identity/edit | IdentityController::Edit | :edit | :edit_identity |
481
+ # # | PATCH | /identity | IdentityController::Update | :update | :identity |
482
+ # # | DELETE | /identity | IdentityController::Destroy | :destroy | :identity |
483
+ # # +--------+----------------+-----------------------------+----------+----------------+
484
+ #
485
+ #
486
+ #
487
+ # @example Limit the generated routes with :only
488
+ # require 'lotus/router'
489
+ #
490
+ # Lotus::Router.new do
491
+ # resource 'identity', only: [:show, :new, :create]
492
+ # end
493
+ #
494
+ # # It generates:
495
+ # #
496
+ # # +--------+----------------+-----------------------------+----------+----------------+
497
+ # # | Verb | Path | Action | Name | Named Route |
498
+ # # +--------+----------------+-----------------------------+----------+----------------+
499
+ # # | GET | /identity | IdentityController::Show | :show | :identity |
500
+ # # | GET | /identity/new | IdentityController::New | :new | :new_identity |
501
+ # # | POST | /identity | IdentityController::Create | :create | :identity |
502
+ # # +--------+----------------+-----------------------------+----------+----------------+
503
+ #
504
+ #
505
+ #
506
+ # @example Limit the generated routes with :except
507
+ # require 'lotus/router'
508
+ #
509
+ # Lotus::Router.new do
510
+ # resource 'identity', except: [:edit, :update, :destroy]
511
+ # end
512
+ #
513
+ # # It generates:
514
+ # #
515
+ # # +--------+----------------+-----------------------------+----------+----------------+
516
+ # # | Verb | Path | Action | Name | Named Route |
517
+ # # +--------+----------------+-----------------------------+----------+----------------+
518
+ # # | GET | /identity | IdentityController::Show | :show | :identity |
519
+ # # | GET | /identity/new | IdentityController::New | :new | :new_identity |
520
+ # # | POST | /identity | IdentityController::Create | :create | :identity |
521
+ # # +--------+----------------+-----------------------------+----------+----------------+
522
+ #
523
+ #
524
+ #
525
+ # @example Additional single routes
526
+ # require 'lotus/router'
527
+ #
528
+ # Lotus::Router.new do
529
+ # resource 'identity', only: [] do
530
+ # member do
531
+ # patch 'activate'
532
+ # end
533
+ # end
534
+ # end
535
+ #
536
+ # # It generates:
537
+ # #
538
+ # # +--------+--------------------+------------------------------+------+--------------------+
539
+ # # | Verb | Path | Action | Name | Named Route |
540
+ # # +--------+--------------------+------------------------------+------+--------------------+
541
+ # # | PATCH | /identity/activate | IdentityController::Activate | | :activate_identity |
542
+ # # +--------+--------------------+------------------------------+------+--------------------+
543
+ #
544
+ #
545
+ #
546
+ # @example Additional collection routes
547
+ # require 'lotus/router'
548
+ #
549
+ # Lotus::Router.new do
550
+ # resource 'identity', only: [] do
551
+ # collection do
552
+ # get 'keys'
553
+ # end
554
+ # end
555
+ # end
556
+ #
557
+ # # It generates:
558
+ # #
559
+ # # +------+----------------+--------------------------+------+----------------+
560
+ # # | Verb | Path | Action | Name | Named Route |
561
+ # # +------+----------------+--------------------------+------+----------------+
562
+ # # | GET | /identity/keys | IdentityController::Keys | | :keys_identity |
563
+ # # +------+----------------+--------------------------+------+----------------+
564
+ def resource(name, options = {}, &blk)
565
+ Routing::Resource.new(self, name, options.merge(separator: @router.action_separator), &blk)
566
+ end
567
+
568
+ # Defines a set of named routes for a plural RESTful resource.
569
+ # It has a built-in integration for Lotus::Controller.
570
+ #
571
+ # @param name [String] the name of the resource
572
+ # @param options [Hash] a set of options to customize the routes
573
+ # @option options [Array<Symbol>] :only a subset of the default routes
574
+ # that we want to generate
575
+ # @option options [Array<Symbol>] :except prevent the given routes to be
576
+ # generated
577
+ # @param blk [Proc] a block of code to generate additional routes
578
+ #
579
+ # @return [Lotus::Routing::Resources]
580
+ #
581
+ # @since 0.1.0
582
+ #
583
+ # @see Lotus::Routing::Resources
584
+ # @see Lotus::Routing::Resources::Action
585
+ # @see Lotus::Routing::Resource::Options
586
+ #
587
+ # @example Default usage
588
+ # require 'lotus/router'
589
+ #
590
+ # Lotus::Router.new do
591
+ # resources 'articles'
592
+ # end
593
+ #
594
+ # # It generates:
595
+ # #
596
+ # # +--------+--------------------+-----------------------------+----------+----------------+
597
+ # # | Verb | Path | Action | Name | Named Route |
598
+ # # +--------+--------------------+-----------------------------+----------+----------------+
599
+ # # | GET | /articles | ArticlesController::Index | :index | :articles |
600
+ # # | GET | /articles/:id | ArticlesController::Show | :show | :articles |
601
+ # # | GET | /articles/new | ArticlesController::New | :new | :new_articles |
602
+ # # | POST | /articles | ArticlesController::Create | :create | :articles |
603
+ # # | GET | /articles/:id/edit | ArticlesController::Edit | :edit | :edit_articles |
604
+ # # | PATCH | /articles/:id | ArticlesController::Update | :update | :articles |
605
+ # # | DELETE | /articles/:id | ArticlesController::Destroy | :destroy | :articles |
606
+ # # +--------+--------------------+-----------------------------+----------+----------------+
607
+ #
608
+ #
609
+ #
610
+ # @example Limit the generated routes with :only
611
+ # require 'lotus/router'
612
+ #
613
+ # Lotus::Router.new do
614
+ # resources 'articles', only: [:index]
615
+ # end
616
+ #
617
+ # # It generates:
618
+ # #
619
+ # # +------+-----------+---------------------------+--------+-------------+
620
+ # # | Verb | Path | Action | Name | Named Route |
621
+ # # +------+-----------+---------------------------+--------+-------------+
622
+ # # | GET | /articles | ArticlesController::Index | :index | :articles |
623
+ # # +------+-----------+---------------------------+--------+-------------+
624
+ #
625
+ #
626
+ #
627
+ # @example Limit the generated routes with :except
628
+ # require 'lotus/router'
629
+ #
630
+ # Lotus::Router.new do
631
+ # resources 'articles', except: [:edit, :update]
632
+ # end
633
+ #
634
+ # # It generates:
635
+ # #
636
+ # # +--------+--------------------+-----------------------------+----------+----------------+
637
+ # # | Verb | Path | Action | Name | Named Route |
638
+ # # +--------+--------------------+-----------------------------+----------+----------------+
639
+ # # | GET | /articles | ArticlesController::Index | :index | :articles |
640
+ # # | GET | /articles/:id | ArticlesController::Show | :show | :articles |
641
+ # # | GET | /articles/new | ArticlesController::New | :new | :new_articles |
642
+ # # | POST | /articles | ArticlesController::Create | :create | :articles |
643
+ # # | DELETE | /articles/:id | ArticlesController::Destroy | :destroy | :articles |
644
+ # # +--------+--------------------+-----------------------------+----------+----------------+
645
+ #
646
+ #
647
+ #
648
+ # @example Additional single routes
649
+ # require 'lotus/router'
650
+ #
651
+ # Lotus::Router.new do
652
+ # resources 'articles', only: [] do
653
+ # member do
654
+ # patch 'publish'
655
+ # end
656
+ # end
657
+ # end
658
+ #
659
+ # # It generates:
660
+ # #
661
+ # # +--------+-----------------------+-----------------------------+------+-------------------+
662
+ # # | Verb | Path | Action | Name | Named Route |
663
+ # # +--------+-----------------------+-----------------------------+------+-------------------+
664
+ # # | PATCH | /articles/:id/publish | ArticlesController::Publish | | :publish_articles |
665
+ # # +--------+-----------------------+-----------------------------+------+-------------------+
666
+ #
667
+ #
668
+ #
669
+ # @example Additional collection routes
670
+ # require 'lotus/router'
671
+ #
672
+ # Lotus::Router.new do
673
+ # resources 'articles', only: [] do
674
+ # collection do
675
+ # get 'search'
676
+ # end
677
+ # end
678
+ # end
679
+ #
680
+ # # It generates:
681
+ # #
682
+ # # +------+------------------+----------------------------+------+------------------+
683
+ # # | Verb | Path | Action | Name | Named Route |
684
+ # # +------+------------------+----------------------------+------+------------------+
685
+ # # | GET | /articles/search | ArticlesController::Search | | :search_articles |
686
+ # # +------+------------------+----------------------------+------+------------------+
687
+ def resources(name, options = {}, &blk)
688
+ Routing::Resources.new(self, name, options.merge(separator: @router.action_separator), &blk)
689
+ end
690
+
691
+ # Resolve the given Rack env to a registered endpoint and invoke it.
692
+ #
693
+ # @param env [Hash] a Rack env instance
694
+ #
695
+ # @return [Rack::Response, Array]
696
+ #
697
+ # @since 0.1.0
698
+ def call(env)
699
+ @router.call(env)
700
+ end
701
+
702
+ # Generate an relative URL for a specified named route.
703
+ # The additional arguments will be used to compose the relative URL - in
704
+ # case it has tokens to match - and for compose the query string.
705
+ #
706
+ # @param route [Symbol] the route name
707
+ #
708
+ # @return [String]
709
+ #
710
+ # @raise [Lotus::Routing::InvalidRouteException] when the router fails to
711
+ # recognize a route, because of the given arguments.
712
+ #
713
+ # @since 0.1.0
714
+ #
715
+ # @example
716
+ # require 'lotus/router'
717
+ #
718
+ # router = Lotus::Router.new(scheme: 'https', host: 'lotusrb.org')
719
+ # router.get '/login', to: 'sessions#new', as: :login
720
+ # router.get '/:name', to: 'frameworks#show', as: :framework
721
+ #
722
+ # router.path(:login) # => "/login"
723
+ # router.path(:login, return_to: '/dashboard') # => "/login?return_to=%2Fdashboard"
724
+ # router.path(:framework, name: 'router') # => "/router"
725
+ def path(route, *args)
726
+ @router.path(route, *args)
727
+ end
728
+
729
+ # Generate a URL for a specified named route.
730
+ # The additional arguments will be used to compose the relative URL - in
731
+ # case it has tokens to match - and for compose the query string.
732
+ #
733
+ # @param route [Symbol] the route name
734
+ #
735
+ # @return [String]
736
+ #
737
+ # @raise [Lotus::Routing::InvalidRouteException] when the router fails to
738
+ # recognize a route, because of the given arguments.
739
+ #
740
+ # @since 0.1.0
741
+ #
742
+ # @example
743
+ # require 'lotus/router'
744
+ #
745
+ # router = Lotus::Router.new(scheme: 'https', host: 'lotusrb.org')
746
+ # router.get '/login', to: 'sessions#new', as: :login
747
+ # router.get '/:name', to: 'frameworks#show', as: :framework
748
+ #
749
+ # router.url(:login) # => "https://lotusrb.org/login"
750
+ # router.url(:login, return_to: '/dashboard') # => "https://lotusrb.org/login?return_to=%2Fdashboard"
751
+ # router.url(:framework, name: 'router') # => "https://lotusrb.org/router"
752
+ def url(route, *args)
753
+ @router.url(route, *args)
754
+ end
6
755
  end
7
756
  end