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