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
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