lotus-router 0.3.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 221714d5d9c1a560423ad18e2153c17d88200680
4
- data.tar.gz: 41706f3509decc6458e1852f53c5e404e30e4239
3
+ metadata.gz: b3fcb5db6c1bdb046aeb93c5d0950301d5ed551d
4
+ data.tar.gz: 577b5c170f3d469369ce4c43c11782f6f9d6ea3c
5
5
  SHA512:
6
- metadata.gz: aaa5c9a07b4ca993792027a16b6ebaa62fe1c2b5ce48a85921ee4c3fa9efdc81884016c48efddb6280708c4fcd1b4a5d0653f4fad6493a3ba6e2b224850ebee7
7
- data.tar.gz: 7e0f8ad5a549698236d8ee46d4b6d7d5af2029d9a1034db599a234b1098aa27809993bc77d8fe638a2353a59726c359d014c954b056da9a521a5b384c51a6fb1
6
+ metadata.gz: 189025ab49239109b74a6f939585d2cdc56b7568a9864c0a82af39fc166a36b8f4f4ae7e5335a53b5ca3bcadd2120b75705ca6dc8d12a1b9bf05501aaa45f652
7
+ data.tar.gz: 339c1ac13cb39971e7594675646b2c7f2a0baafa96a5bd7b44c16be0ce291d089857abbd797ab6fa6be44fa1b7a279c1d159562acd1d46c60ba97c43779d102f
@@ -1,6 +1,13 @@
1
1
  # Lotus::Router
2
2
  Rack compatible HTTP router for Ruby
3
3
 
4
+ ## v0.4.0 - 2015-05-15
5
+ ### Added
6
+ - [Alfonso Uceda Pompa] Nested RESTful resource(s)
7
+
8
+ ### Changed
9
+ - [Alfonso Uceda Pompa] RESTful resource(s) have a correct pluralization/singularization for variables and named routes (eg. `/books/:id` is now `:book` instead of `:books`)
10
+
4
11
  ## v0.3.0 - 2015-03-23
5
12
 
6
13
  ## v0.2.1 - 2015-01-30
data/README.md CHANGED
@@ -407,6 +407,27 @@ router.resource 'profile', controller: 'identity'
407
407
  router.path(:profile) # => /profile # Will route to Identity::Show
408
408
  ```
409
409
 
410
+ #### Nested Resources
411
+
412
+ We can nest resource(s):
413
+
414
+ ```ruby
415
+ router = Lotus::Router.new
416
+ router.resource :identity do
417
+ resource :avatar
418
+ resources :api_keys
419
+ end
420
+
421
+ router.path(:identity_avatar) # => /identity/avatar
422
+ router.path(:new_identity_avatar) # => /identity/avatar/new
423
+ router.path(:edit_identity_avatar) # => /identity/avatar/new
424
+
425
+ router.path(:identity_api_keys) # => /identity/api_keys
426
+ router.path(:identity_api_key) # => /identity/api_keys/:id
427
+ router.path(:new_identity_api_key) # => /identity/api_keys/new
428
+ router.path(:edit_identity_api_key) # => /identity/api_keys/:id/edit
429
+ ```
430
+
410
431
 
411
432
 
412
433
  ### RESTful Resources:
@@ -438,14 +459,14 @@ It will map:
438
459
  <td>/flowers/:id</td>
439
460
  <td>Flowers::Show</td>
440
461
  <td>:show</td>
441
- <td>:flowers</td>
462
+ <td>:flower</td>
442
463
  </tr>
443
464
  <tr>
444
465
  <td>GET</td>
445
466
  <td>/flowers/new</td>
446
467
  <td>Flowers::New</td>
447
468
  <td>:new</td>
448
- <td>:new_flowers</td>
469
+ <td>:new_flower</td>
449
470
  </tr>
450
471
  <tr>
451
472
  <td>POST</td>
@@ -459,29 +480,29 @@ It will map:
459
480
  <td>/flowers/:id/edit</td>
460
481
  <td>Flowers::Edit</td>
461
482
  <td>:edit</td>
462
- <td>:edit_flowers</td>
483
+ <td>:edit_flower</td>
463
484
  </tr>
464
485
  <tr>
465
486
  <td>PATCH</td>
466
487
  <td>/flowers/:id</td>
467
488
  <td>Flowers::Update</td>
468
489
  <td>:update</td>
469
- <td>:flowers</td>
490
+ <td>:flower</td>
470
491
  </tr>
471
492
  <tr>
472
493
  <td>DELETE</td>
473
494
  <td>/flowers/:id</td>
474
495
  <td>Flowers::Destroy</td>
475
496
  <td>:destroy</td>
476
- <td>:flowers</td>
497
+ <td>:flower</td>
477
498
  </tr>
478
499
  </table>
479
500
 
480
501
 
481
502
  ```ruby
482
- router.path(:flowers) # => /flowers
483
- router.path(:flowers, id: 23) # => /flowers/23
484
- router.path(:edit_flowers, id: 23) # => /flowers/23/edit
503
+ router.path(:flowers) # => /flowers
504
+ router.path(:flower, id: 23) # => /flowers/23
505
+ router.path(:edit_flower, id: 23) # => /flowers/23/edit
485
506
  ```
486
507
 
487
508
 
@@ -512,8 +533,8 @@ router.resources 'flowers' do
512
533
  end
513
534
  end
514
535
 
515
- router.path(:toggle_flowers, id: 23) # => /flowers/23/toggle
516
- router.path(:search_flowers) # => /flowers/search
536
+ router.path(:toggle_flower, id: 23) # => /flowers/23/toggle
537
+ router.path(:search_flowers) # => /flowers/search
517
538
  ```
518
539
 
519
540
 
@@ -523,7 +544,28 @@ Configure controller:
523
544
  router = Lotus::Router.new
524
545
  router.resources 'blossoms', controller: 'flowers'
525
546
 
526
- router.path(:blossoms, id: 23) # => /blossoms/23 # Will route to Flowers::Show
547
+ router.path(:blossom, id: 23) # => /blossoms/23 # Will route to Flowers::Show
548
+ ```
549
+
550
+ #### Nested Resources
551
+
552
+ We can nest resource(s):
553
+
554
+ ```ruby
555
+ router = Lotus::Router.new
556
+ router.resources :users do
557
+ resource :avatar
558
+ resources :favorites
559
+ end
560
+
561
+ router.path(:user_avatar, user_id: 1) # => /users/1/avatar
562
+ router.path(:new_user_avatar, user_id: 1) # => /users/1/avatar/new
563
+ router.path(:edit_user_avatar, user_id: 1) # => /users/1/avatar/edit
564
+
565
+ router.path(:user_favorites, user_id: 1) # => /users/1/favorites
566
+ router.path(:user_favorite, user_id: 1, id: 2) # => /users/1/favorites/2
567
+ router.path(:new_user_favorites, user_id: 1) # => /users/1/favorites/new
568
+ router.path(:edit_user_favorites, user_id: 1, id: 2) # => /users/1/favorites/2/edit
527
569
  ```
528
570
 
529
571
  ## Testing
@@ -1,6 +1,6 @@
1
1
  module Lotus
2
2
  class Router
3
3
  # @since 0.1.0
4
- VERSION = '0.3.0'.freeze
4
+ VERSION = '0.4.0'.freeze
5
5
  end
6
6
  end
@@ -15,6 +15,10 @@ module Lotus
15
15
  class Resource
16
16
  include Utils::ClassAttribute
17
17
 
18
+ # @api private
19
+ # @since 0.4.0
20
+ NESTED_ROUTES_SEPARATOR = '/'.freeze
21
+
18
22
  # Set of default routes
19
23
  #
20
24
  # @api private
@@ -43,30 +47,69 @@ module Lotus
43
47
  class_attribute :collection
44
48
  self.collection = Resource::CollectionAction
45
49
 
50
+ # @api private
51
+ # @since 0.4.0
52
+ attr_reader :parent
53
+
46
54
  # @api private
47
55
  # @since 0.1.0
48
- def initialize(router, name, options = {}, &blk)
56
+ def initialize(router, name, options = {}, parent = nil, &blk)
49
57
  @router = router
50
58
  @name = name
59
+ @parent = parent
51
60
  @options = Options.new(self.class.actions, options.merge(name: @name))
52
61
  generate(&blk)
53
62
  end
54
63
 
64
+ # Allow nested resources inside resource or resources
65
+ #
66
+ # @since 0.4.0
67
+ #
68
+ # @see Lotus::Router#resources
69
+ def resources(name, options = {}, &blk)
70
+ _resource(Resources, name, options, &blk)
71
+ end
72
+
73
+ # Allow nested resource inside resource or resources
74
+ #
75
+ # @since 0.4.0
76
+ #
77
+ # @see Lotus::Router#resource
78
+ def resource(name, options = {}, &blk)
79
+ _resource(Resource, name, options, &blk)
80
+ end
81
+
82
+ # Return separator
83
+ #
84
+ # @api private
85
+ # @since 0.4.0
86
+ def wildcard_param(route_param = nil)
87
+ NESTED_ROUTES_SEPARATOR
88
+ end
89
+
55
90
  private
91
+
92
+ # @api private
93
+ # @since 0.4.0
94
+ def _resource(klass, name, options, &blk)
95
+ options = options.merge(separator: @options[:separator], namespace: @options[:namespace])
96
+ klass.new(@router, [@name, name].join(NESTED_ROUTES_SEPARATOR), options, self, &blk)
97
+ end
98
+
56
99
  def generate(&blk)
57
100
  instance_eval(&blk) if block_given?
58
101
 
59
102
  @options.actions.each do |action|
60
- self.class.action.generate(@router, action, @options)
103
+ self.class.action.generate(@router, action, @options, self)
61
104
  end
62
105
  end
63
106
 
64
107
  def member(&blk)
65
- self.class.member.new(@router, @options, &blk)
108
+ self.class.member.new(@router, @options, self, &blk)
66
109
  end
67
110
 
68
111
  def collection(&blk)
69
- self.class.collection.new(@router, @options, &blk)
112
+ self.class.collection.new(@router, @options, self, &blk)
70
113
  end
71
114
  end
72
115
  end
@@ -1,6 +1,7 @@
1
1
  require 'lotus/utils/string'
2
2
  require 'lotus/utils/path_prefix'
3
3
  require 'lotus/utils/class_attribute'
4
+ require 'lotus/routing/resource/nested'
4
5
 
5
6
  module Lotus
6
7
  module Routing
@@ -15,6 +16,12 @@ module Lotus
15
16
  class Action
16
17
  include Utils::ClassAttribute
17
18
 
19
+ # Nested routes separator
20
+ #
21
+ # @api private
22
+ # @since 0.4.0
23
+ NESTED_ROUTES_SEPARATOR = '/'.freeze
24
+
18
25
  # Ruby namespace where lookup for default subclasses.
19
26
  #
20
27
  # @api private
@@ -41,25 +48,29 @@ module Lotus
41
48
  # @param router [Lotus::Router]
42
49
  # @param action [Lotus::Routing::Resource::Action]
43
50
  # @param options [Hash]
51
+ # @param resource [Lotus::Routing::Resource, Lotus::Routing::Resources]
44
52
  #
45
53
  # @api private
46
54
  #
47
55
  # @since 0.1.0
48
- def self.generate(router, action, options)
49
- class_for(action).new(router, options)
56
+ def self.generate(router, action, options = {}, resource = nil)
57
+ class_for(action).new(router, options, resource)
50
58
  end
51
59
 
52
60
  # Initialize an action
53
61
  #
54
62
  # @param router [Lotus::Router]
55
63
  # @param options [Hash]
64
+ # @param resource [Lotus::Routing::Resource, Lotus::Routing::Resources]
56
65
  # @param blk [Proc]
57
66
  #
58
67
  # @api private
59
68
  #
60
69
  # @since 0.1.0
61
- def initialize(router, options, &blk)
62
- @router, @options = router, options
70
+ def initialize(router, options = {}, resource = nil, &blk)
71
+ @router = router
72
+ @options = options
73
+ @resource = resource
63
74
  generate(&blk)
64
75
  end
65
76
 
@@ -77,6 +88,8 @@ module Lotus
77
88
 
78
89
  # Resource name
79
90
  #
91
+ # @return [String]
92
+ #
80
93
  # @api private
81
94
  # @since 0.1.0
82
95
  #
@@ -89,7 +102,7 @@ module Lotus
89
102
  #
90
103
  # # 'identity' is the name passed in the @options
91
104
  def resource_name
92
- @options[:name]
105
+ @resource_name ||= @options[:name].to_s
93
106
  end
94
107
 
95
108
  # Namespace
@@ -157,7 +170,7 @@ module Lotus
157
170
  # @api private
158
171
  # @since 0.1.0
159
172
  def rest_path
160
- namespace.join(resource_name)
173
+ namespace.join(_nested_rest_path || resource_name.to_s)
161
174
  end
162
175
 
163
176
  # The namespaced name of the action within the whole context of the router.
@@ -179,7 +192,7 @@ module Lotus
179
192
  # @api private
180
193
  # @since 0.1.0
181
194
  def as
182
- namespace.relative_join(resource_name, self.class.named_route_separator).to_sym
195
+ namespace.relative_join(_singularized_as, self.class.named_route_separator).to_sym
183
196
  end
184
197
 
185
198
  # The name of the RESTful action.
@@ -233,10 +246,30 @@ module Lotus
233
246
  # # Same for other action names
234
247
  #
235
248
  # @api private
236
- # @since x.x.x
249
+ # @since 0.4.0
237
250
  def controller_name
238
251
  @options[:controller] || resource_name
239
252
  end
253
+
254
+ private
255
+
256
+ # Singularize as (helper route)
257
+ #
258
+ # @api private
259
+ # @since 0.4.0
260
+ def _singularized_as
261
+ resource_name.split(NESTED_ROUTES_SEPARATOR).map do |name|
262
+ Lotus::Utils::String.new(name).singularize
263
+ end
264
+ end
265
+
266
+ # Create nested rest path
267
+ #
268
+ # @api private
269
+ # @since 0.4.0
270
+ def _nested_rest_path
271
+ Nested.new(resource_name, @resource).to_path
272
+ end
240
273
  end
241
274
 
242
275
  # Collection action
@@ -0,0 +1,39 @@
1
+ module Lotus
2
+ module Routing
3
+ class Resource
4
+ # Helper class to calculate nested path
5
+ #
6
+ # @api private
7
+ # @since 0.4.0
8
+ class Nested
9
+ # @api private
10
+ # @since 0.4.0
11
+ SEPARATOR = '/'.freeze
12
+
13
+ # @api private
14
+ # @since 0.4.0
15
+ def initialize(resource_name, resource)
16
+ @resource_name = resource_name.to_s.split(SEPARATOR)
17
+ @resource = resource
18
+ @path = []
19
+ _calculate(@resource_name.dup, @resource)
20
+ end
21
+
22
+ # @api private
23
+ # @since 0.4.0
24
+ def to_path
25
+ @path.reverse!.pop
26
+ @resource_name.zip(@path).flatten.join
27
+ end
28
+
29
+ private
30
+
31
+ def _calculate(param_wildcard, resource = nil)
32
+ return if resource.nil?
33
+ @path << resource.wildcard_param(param_wildcard.pop)
34
+ _calculate(param_wildcard, resource.parent)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -35,6 +35,14 @@ module Lotus
35
35
  # @api private
36
36
  # @since 0.1.0
37
37
  self.collection = Resources::CollectionAction
38
+
39
+ # Return wildcard param between separators
40
+ #
41
+ # @api private
42
+ # @since 0.4.0
43
+ def wildcard_param(route_param = nil)
44
+ "/:#{ Lotus::Utils::String.new(route_param).singularize }_id/"
45
+ end
38
46
  end
39
47
  end
40
48
  end
@@ -27,6 +27,21 @@ module Lotus
27
27
  self.identifier = ':id'.freeze
28
28
  end
29
29
 
30
+ # Pluralize concrete actions
31
+ #
32
+ # @api private
33
+ # @since 0.4.0
34
+ module PluralizedAction
35
+ private
36
+ # The name of the RESTful action.
37
+ #
38
+ # @api private
39
+ # @since 0.4.0
40
+ def as
41
+ Lotus::Utils::String.new(super).pluralize
42
+ end
43
+ end
44
+
30
45
  # Collection action
31
46
  # It implements #collection within a #resources block.
32
47
  #
@@ -34,6 +49,9 @@ module Lotus
34
49
  # @since 0.1.0
35
50
  # @see Lotus::Router#resources
36
51
  class CollectionAction < Resource::CollectionAction
52
+ def as(action_name)
53
+ Lotus::Utils::String.new(super(action_name)).pluralize
54
+ end
37
55
  end
38
56
 
39
57
  # Member action
@@ -66,6 +84,7 @@ module Lotus
66
84
  # @since 0.1.0
67
85
  # @see Lotus::Router#resources
68
86
  class Index < Action
87
+ include PluralizedAction
69
88
  self.verb = :get
70
89
  end
71
90
 
@@ -83,6 +102,7 @@ module Lotus
83
102
  # @since 0.1.0
84
103
  # @see Lotus::Router#resources
85
104
  class Create < Resource::Create
105
+ include PluralizedAction
86
106
  end
87
107
 
88
108
  # Show action
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.required_ruby_version = '>= 2.0.0'
21
21
 
22
22
  spec.add_dependency 'http_router', '~> 0.11'
23
- spec.add_dependency 'lotus-utils', '~> 0.4'
23
+ spec.add_dependency 'lotus-utils', '~> 0.4', '>= 0.4.1'
24
24
 
25
25
  spec.add_development_dependency 'bundler', '~> 1.5'
26
26
  spec.add_development_dependency 'minitest', '~> 5'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lotus-router
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luca Guidi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-23 00:00:00.000000000 Z
11
+ date: 2015-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http_router
@@ -31,6 +31,9 @@ dependencies:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0.4'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 0.4.1
34
37
  type: :runtime
35
38
  prerelease: false
36
39
  version_requirements: !ruby/object:Gem::Requirement
@@ -38,6 +41,9 @@ dependencies:
38
41
  - - "~>"
39
42
  - !ruby/object:Gem::Version
40
43
  version: '0.4'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 0.4.1
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: bundler
43
49
  requirement: !ruby/object:Gem::Requirement
@@ -102,6 +108,7 @@ files:
102
108
  - lib/lotus/routing/parsing/parser.rb
103
109
  - lib/lotus/routing/resource.rb
104
110
  - lib/lotus/routing/resource/action.rb
111
+ - lib/lotus/routing/resource/nested.rb
105
112
  - lib/lotus/routing/resource/options.rb
106
113
  - lib/lotus/routing/resources.rb
107
114
  - lib/lotus/routing/resources/action.rb