lotus-router 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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