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