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.
- checksums.yaml +4 -4
- data/.coveralls.yml +2 -0
- data/.gitignore +5 -13
- data/.travis.yml +5 -0
- data/.yardopts +3 -0
- data/Gemfile +10 -3
- data/README.md +470 -6
- data/Rakefile +10 -1
- data/benchmarks/callable +23 -0
- data/benchmarks/named_routes +72 -0
- data/benchmarks/resource +44 -0
- data/benchmarks/resources +58 -0
- data/benchmarks/routes +67 -0
- data/benchmarks/run.sh +11 -0
- data/benchmarks/utils.rb +56 -0
- data/lib/lotus-router.rb +1 -0
- data/lib/lotus/router.rb +752 -3
- data/lib/lotus/router/version.rb +2 -2
- data/lib/lotus/routing/endpoint.rb +114 -0
- data/lib/lotus/routing/endpoint_resolver.rb +251 -0
- data/lib/lotus/routing/http_router.rb +130 -0
- data/lib/lotus/routing/namespace.rb +86 -0
- data/lib/lotus/routing/resource.rb +73 -0
- data/lib/lotus/routing/resource/action.rb +340 -0
- data/lib/lotus/routing/resource/options.rb +48 -0
- data/lib/lotus/routing/resources.rb +40 -0
- data/lib/lotus/routing/resources/action.rb +123 -0
- data/lib/lotus/routing/route.rb +53 -0
- data/lotus-router.gemspec +16 -12
- data/test/fixtures.rb +193 -0
- data/test/integration/client_error_test.rb +16 -0
- data/test/integration/pass_on_response_test.rb +13 -0
- data/test/named_routes_test.rb +123 -0
- data/test/namespace_test.rb +289 -0
- data/test/new_test.rb +67 -0
- data/test/redirect_test.rb +33 -0
- data/test/resource_test.rb +128 -0
- data/test/resources_test.rb +136 -0
- data/test/routing/endpoint_resolver_test.rb +110 -0
- data/test/routing/resource/options_test.rb +36 -0
- data/test/routing_test.rb +99 -0
- data/test/test_helper.rb +32 -0
- data/test/version_test.rb +7 -0
- metadata +102 -10
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'lotus/utils/path_prefix'
|
3
|
+
|
4
|
+
module Lotus
|
5
|
+
module Routing
|
6
|
+
# Namespace for routes.
|
7
|
+
# Implementation of Lotus::Router#namespace
|
8
|
+
#
|
9
|
+
# @since 0.1.0
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
#
|
13
|
+
# @see Lotus::Router#namespace
|
14
|
+
class Namespace < SimpleDelegator
|
15
|
+
# @api private
|
16
|
+
# @since 0.1.0
|
17
|
+
def initialize(router, name, &blk)
|
18
|
+
@router = router
|
19
|
+
@name = Utils::PathPrefix.new(name)
|
20
|
+
__setobj__(@router)
|
21
|
+
instance_eval(&blk)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @api private
|
25
|
+
# @since 0.1.0
|
26
|
+
def get(path, options = {}, &endpoint)
|
27
|
+
super(@name.join(path), options, &endpoint)
|
28
|
+
end
|
29
|
+
|
30
|
+
# @api private
|
31
|
+
# @since 0.1.0
|
32
|
+
def post(path, options = {}, &endpoint)
|
33
|
+
super(@name.join(path), options, &endpoint)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @api private
|
37
|
+
# @since 0.1.0
|
38
|
+
def put(path, options = {}, &endpoint)
|
39
|
+
super(@name.join(path), options, &endpoint)
|
40
|
+
end
|
41
|
+
|
42
|
+
# @api private
|
43
|
+
# @since 0.1.0
|
44
|
+
def patch(path, options = {}, &endpoint)
|
45
|
+
super(@name.join(path), options, &endpoint)
|
46
|
+
end
|
47
|
+
|
48
|
+
# @api private
|
49
|
+
# @since 0.1.0
|
50
|
+
def delete(path, options = {}, &endpoint)
|
51
|
+
super(@name.join(path), options, &endpoint)
|
52
|
+
end
|
53
|
+
|
54
|
+
# @api private
|
55
|
+
# @since 0.1.0
|
56
|
+
def trace(path, options = {}, &endpoint)
|
57
|
+
super(@name.join(path), options, &endpoint)
|
58
|
+
end
|
59
|
+
|
60
|
+
# @api private
|
61
|
+
# @since 0.1.0
|
62
|
+
def resource(name, options = {})
|
63
|
+
super name, options.merge(prefix: @name)
|
64
|
+
end
|
65
|
+
|
66
|
+
# @api private
|
67
|
+
# @since 0.1.0
|
68
|
+
def resources(name, options = {})
|
69
|
+
super name, options.merge(prefix: @name)
|
70
|
+
end
|
71
|
+
|
72
|
+
# @api private
|
73
|
+
# @since 0.1.0
|
74
|
+
def redirect(path, options = {}, &endpoint)
|
75
|
+
super(@name.join(path), options.merge(prefix: @name), &endpoint)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Supports nested namespaces
|
79
|
+
# @api private
|
80
|
+
# @since 0.1.0
|
81
|
+
def namespace(name, &blk)
|
82
|
+
Routing::Namespace.new(self, name, &blk)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'lotus/utils/class_attribute'
|
2
|
+
require 'lotus/routing/resource/options'
|
3
|
+
require 'lotus/routing/resource/action'
|
4
|
+
|
5
|
+
module Lotus
|
6
|
+
module Routing
|
7
|
+
# Set of RESTful resource routes
|
8
|
+
# Implementation of Lotus::Router#resource
|
9
|
+
#
|
10
|
+
# @since 0.1.0
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
#
|
14
|
+
# @see Lotus::Router#resource
|
15
|
+
class Resource
|
16
|
+
include Utils::ClassAttribute
|
17
|
+
|
18
|
+
# Set of default routes
|
19
|
+
#
|
20
|
+
# @api private
|
21
|
+
# @since 0.1.0
|
22
|
+
class_attribute :actions
|
23
|
+
self.actions = [:new, :create, :show, :edit, :update, :destroy]
|
24
|
+
|
25
|
+
# Action class
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
# @since 0.1.0
|
29
|
+
class_attribute :action
|
30
|
+
self.action = Resource::Action
|
31
|
+
|
32
|
+
# Member action class
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
# @since 0.1.0
|
36
|
+
class_attribute :member
|
37
|
+
self.member = Resource::MemberAction
|
38
|
+
|
39
|
+
# Collection action class
|
40
|
+
#
|
41
|
+
# @api private
|
42
|
+
# @since 0.1.0
|
43
|
+
class_attribute :collection
|
44
|
+
self.collection = Resource::CollectionAction
|
45
|
+
|
46
|
+
# @api private
|
47
|
+
# @since 0.1.0
|
48
|
+
def initialize(router, name, options = {}, &blk)
|
49
|
+
@router = router
|
50
|
+
@name = name
|
51
|
+
@options = Options.new(self.class.actions, options.merge(name: @name))
|
52
|
+
generate(&blk)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def generate(&blk)
|
57
|
+
@options.actions.each do |action|
|
58
|
+
self.class.action.generate(@router, action, @options)
|
59
|
+
end
|
60
|
+
|
61
|
+
instance_eval(&blk) if block_given?
|
62
|
+
end
|
63
|
+
|
64
|
+
def member(&blk)
|
65
|
+
self.class.member.new(@router, @options.merge(prefix: @name), &blk)
|
66
|
+
end
|
67
|
+
|
68
|
+
def collection(&blk)
|
69
|
+
self.class.collection.new(@router, @options.merge(prefix: @name), &blk)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,340 @@
|
|
1
|
+
require 'lotus/utils/string'
|
2
|
+
require 'lotus/utils/path_prefix'
|
3
|
+
require 'lotus/utils/class_attribute'
|
4
|
+
|
5
|
+
module Lotus
|
6
|
+
module Routing
|
7
|
+
class Resource
|
8
|
+
# Action for RESTful resource
|
9
|
+
#
|
10
|
+
# @since 0.1.0
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
#
|
14
|
+
# @see Lotus::Router#resource
|
15
|
+
class Action
|
16
|
+
include Utils::ClassAttribute
|
17
|
+
|
18
|
+
# Ruby namespace where lookup for default subclasses.
|
19
|
+
#
|
20
|
+
# @api private
|
21
|
+
# @since 0.1.0
|
22
|
+
class_attribute :namespace
|
23
|
+
self.namespace = Resource
|
24
|
+
|
25
|
+
# Accepted HTTP verb
|
26
|
+
#
|
27
|
+
# @api private
|
28
|
+
# @since 0.1.0
|
29
|
+
class_attribute :verb
|
30
|
+
self.verb = :get
|
31
|
+
|
32
|
+
# Generate an action for the given router
|
33
|
+
#
|
34
|
+
# @param router [Lotus::Router]
|
35
|
+
# @param action [Lotus::Routing::Resource::Action]
|
36
|
+
# @param options [Hash]
|
37
|
+
#
|
38
|
+
# @api private
|
39
|
+
#
|
40
|
+
# @since 0.1.0
|
41
|
+
def self.generate(router, action, options)
|
42
|
+
class_for(action).new(router, options)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Initialize an action
|
46
|
+
#
|
47
|
+
# @param router [Lotus::Router]
|
48
|
+
# @param options [Hash]
|
49
|
+
# @param blk [Proc]
|
50
|
+
#
|
51
|
+
# @api private
|
52
|
+
#
|
53
|
+
# @since 0.1.0
|
54
|
+
def initialize(router, options, &blk)
|
55
|
+
@router, @options = router, options
|
56
|
+
generate(&blk)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Generate an action for the given router
|
60
|
+
#
|
61
|
+
# @param blk [Proc]
|
62
|
+
#
|
63
|
+
# @api private
|
64
|
+
#
|
65
|
+
# @since 0.1.0
|
66
|
+
def generate(&blk)
|
67
|
+
@router.send verb, path, to: endpoint, as: as
|
68
|
+
instance_eval(&blk) if block_given?
|
69
|
+
end
|
70
|
+
|
71
|
+
# Resource name
|
72
|
+
#
|
73
|
+
# @api private
|
74
|
+
# @since 0.1.0
|
75
|
+
#
|
76
|
+
# @example
|
77
|
+
# require 'lotus/router'
|
78
|
+
#
|
79
|
+
# Lotus::Router.new do
|
80
|
+
# resource 'identity'
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# # 'identity' is the name passed in the @options
|
84
|
+
def resource_name
|
85
|
+
@options[:name]
|
86
|
+
end
|
87
|
+
|
88
|
+
# Path prefix
|
89
|
+
#
|
90
|
+
# @api private
|
91
|
+
# @since 0.1.0
|
92
|
+
def prefix
|
93
|
+
@prefix ||= Utils::PathPrefix.new @options[:prefix]
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
# Load a subclass, according to the given action name
|
98
|
+
#
|
99
|
+
# @param action [String] the action name
|
100
|
+
#
|
101
|
+
# @example
|
102
|
+
# Lotus::Routing::Resource::Action.send(:class_for, 'New') # =>
|
103
|
+
# Lotus::Routing::Resource::New
|
104
|
+
#
|
105
|
+
# @api private
|
106
|
+
# @since 0.1.0
|
107
|
+
def self.class_for(action)
|
108
|
+
Utils::Class.load!(Utils::String.new(action).classify, namespace)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Accepted HTTP verb
|
112
|
+
#
|
113
|
+
# @see Lotus::Routing::Resource::Action.verb
|
114
|
+
#
|
115
|
+
# @api private
|
116
|
+
# @since 0.1.0
|
117
|
+
def verb
|
118
|
+
self.class.verb
|
119
|
+
end
|
120
|
+
|
121
|
+
# The prefixed URL relative path
|
122
|
+
#
|
123
|
+
# @example
|
124
|
+
# require 'lotus/router'
|
125
|
+
#
|
126
|
+
# Lotus::Router.new do
|
127
|
+
# resources 'flowers'
|
128
|
+
#
|
129
|
+
# prefix 'animals' do
|
130
|
+
# resources 'mammals'
|
131
|
+
# end
|
132
|
+
# end
|
133
|
+
#
|
134
|
+
# # It will generate paths like '/flowers', '/flowers/:id' ..
|
135
|
+
# # It will generate paths like '/animals/mammals', '/animals/mammals/:id' ..
|
136
|
+
#
|
137
|
+
# @api private
|
138
|
+
# @since 0.1.0
|
139
|
+
def path
|
140
|
+
prefix.join(rest_path)
|
141
|
+
end
|
142
|
+
|
143
|
+
# The URL relative path
|
144
|
+
#
|
145
|
+
# @example
|
146
|
+
# '/flowers'
|
147
|
+
# '/flowers/new'
|
148
|
+
# '/flowers/:id'
|
149
|
+
#
|
150
|
+
# @api private
|
151
|
+
# @since 0.1.0
|
152
|
+
def rest_path
|
153
|
+
"/#{ resource_name }"
|
154
|
+
end
|
155
|
+
|
156
|
+
# The prefixed name of the action within the whole context of the router.
|
157
|
+
#
|
158
|
+
# @example
|
159
|
+
# require 'lotus/router'
|
160
|
+
#
|
161
|
+
# Lotus::Router.new do
|
162
|
+
# resources 'flowers'
|
163
|
+
#
|
164
|
+
# prefix 'animals' do
|
165
|
+
# resources 'mammals'
|
166
|
+
# end
|
167
|
+
# end
|
168
|
+
#
|
169
|
+
# # It will generate named routes like :flowers, :new_flowers ..
|
170
|
+
# # It will generate named routes like :animals_mammals, :animals_new_mammals ..
|
171
|
+
#
|
172
|
+
# @api private
|
173
|
+
# @since 0.1.0
|
174
|
+
def as
|
175
|
+
prefix.relative_join(named_route, '_').to_sym
|
176
|
+
end
|
177
|
+
|
178
|
+
# The name of the action within the whole context of the router.
|
179
|
+
#
|
180
|
+
# @example
|
181
|
+
# :flowers
|
182
|
+
# :new_flowers
|
183
|
+
#
|
184
|
+
# @api private
|
185
|
+
# @since 0.1.0
|
186
|
+
def named_route
|
187
|
+
resource_name
|
188
|
+
end
|
189
|
+
|
190
|
+
# The name of the RESTful action.
|
191
|
+
#
|
192
|
+
# @example
|
193
|
+
# 'index'
|
194
|
+
# 'new'
|
195
|
+
# 'create'
|
196
|
+
#
|
197
|
+
# @api private
|
198
|
+
# @since 0.1.0
|
199
|
+
def action_name
|
200
|
+
Utils::String.new(self.class.name).demodulize.downcase
|
201
|
+
end
|
202
|
+
|
203
|
+
# A string that represents the endpoint to be loaded.
|
204
|
+
# It is composed by controller and action name.
|
205
|
+
#
|
206
|
+
# @see Lotus::Routing::Resource::Action#separator
|
207
|
+
#
|
208
|
+
# @example
|
209
|
+
# 'flowers#index'
|
210
|
+
#
|
211
|
+
# @api private
|
212
|
+
# @since 0.1.0
|
213
|
+
def endpoint
|
214
|
+
[ resource_name, action_name ].join separator
|
215
|
+
end
|
216
|
+
|
217
|
+
# Separator between controller and action name
|
218
|
+
#
|
219
|
+
# @see Lotus::Routing::EndpointResolver#separator
|
220
|
+
#
|
221
|
+
# @example
|
222
|
+
# '#' # default
|
223
|
+
#
|
224
|
+
# @api private
|
225
|
+
# @since 0.1.0
|
226
|
+
def separator
|
227
|
+
@options[:separator]
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# Collection action
|
232
|
+
# It implements #collection within a #resource block.
|
233
|
+
#
|
234
|
+
# @api private
|
235
|
+
# @since 0.1.0
|
236
|
+
# @see Lotus::Router#resource
|
237
|
+
class CollectionAction < Action
|
238
|
+
def generate(&blk)
|
239
|
+
instance_eval(&blk) if block_given?
|
240
|
+
end
|
241
|
+
|
242
|
+
protected
|
243
|
+
def method_missing(m, *args, &blk)
|
244
|
+
verb, path, _ = m, *args
|
245
|
+
@router.send verb, path(path), to: endpoint(path), as: as(path)
|
246
|
+
end
|
247
|
+
|
248
|
+
private
|
249
|
+
def path(path)
|
250
|
+
prefix.join(path)
|
251
|
+
end
|
252
|
+
|
253
|
+
def endpoint(path)
|
254
|
+
[ resource_name, path ].join separator
|
255
|
+
end
|
256
|
+
|
257
|
+
def as(path)
|
258
|
+
Utils::PathPrefix.new(path).relative_join(resource_name, '_').to_sym
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
# Collection action
|
263
|
+
# It implements #member within a #resource block.
|
264
|
+
#
|
265
|
+
# @api private
|
266
|
+
# @since 0.1.0
|
267
|
+
# @see Lotus::Router#resource
|
268
|
+
class MemberAction < CollectionAction
|
269
|
+
end
|
270
|
+
|
271
|
+
# Implementation of common methods for concrete member actions
|
272
|
+
#
|
273
|
+
# @api private
|
274
|
+
# @since 0.1.0
|
275
|
+
module DefaultMemberAction
|
276
|
+
private
|
277
|
+
def rest_path
|
278
|
+
"/#{ resource_name }/#{ action_name }"
|
279
|
+
end
|
280
|
+
|
281
|
+
def named_route
|
282
|
+
"#{ action_name }_#{ resource_name }"
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
# New action
|
287
|
+
#
|
288
|
+
# @api private
|
289
|
+
# @since 0.1.0
|
290
|
+
# @see Lotus::Router#resource
|
291
|
+
class New < Action
|
292
|
+
include DefaultMemberAction
|
293
|
+
end
|
294
|
+
|
295
|
+
# Create action
|
296
|
+
#
|
297
|
+
# @api private
|
298
|
+
# @since 0.1.0
|
299
|
+
# @see Lotus::Router#resource
|
300
|
+
class Create < Action
|
301
|
+
self.verb = :post
|
302
|
+
end
|
303
|
+
|
304
|
+
# Show action
|
305
|
+
#
|
306
|
+
# @api private
|
307
|
+
# @since 0.1.0
|
308
|
+
# @see Lotus::Router#resource
|
309
|
+
class Show < Action
|
310
|
+
end
|
311
|
+
|
312
|
+
# Edit action
|
313
|
+
#
|
314
|
+
# @api private
|
315
|
+
# @since 0.1.0
|
316
|
+
# @see Lotus::Router#resource
|
317
|
+
class Edit < Action
|
318
|
+
include DefaultMemberAction
|
319
|
+
end
|
320
|
+
|
321
|
+
# Update action
|
322
|
+
#
|
323
|
+
# @api private
|
324
|
+
# @since 0.1.0
|
325
|
+
# @see Lotus::Router#resource
|
326
|
+
class Update < Action
|
327
|
+
self.verb = :patch
|
328
|
+
end
|
329
|
+
|
330
|
+
# Destroy action
|
331
|
+
#
|
332
|
+
# @api private
|
333
|
+
# @since 0.1.0
|
334
|
+
# @see Lotus::Router#resource
|
335
|
+
class Destroy < Action
|
336
|
+
self.verb = :delete
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|