josevalim-inherited_resources 0.7.0 → 0.7.1

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.
data/README CHANGED
@@ -1,6 +1,6 @@
1
1
  Inherited Resources
2
2
  License: MIT
3
- Version: 0.7.0
3
+ Version: 0.7.1
4
4
 
5
5
  You can also read this README in pretty html at the GitHub project Wiki page:
6
6
 
@@ -288,6 +288,42 @@ messages will be checked in the following order:
288
288
  flash.projects.create.notice
289
289
  flash.actions.create.notice
290
290
 
291
+ Has Scope
292
+ ---------
293
+
294
+ InheritedResources tries to integrate nicely with your model. In order to do so,
295
+ it also is named_scope fluent. Let's suppose our Project model with the scopes:
296
+
297
+ class ProjectsController < ActiveRecord::Base
298
+ named_scope :featured, :conditions => { :featured => true }
299
+ named_scope :by_methodology, proc {|methodology| { :conditions => { :methodology => methodology } } }
300
+ named_scope :limit, proc{|limit| :limit => limit.to_i }
301
+ end
302
+
303
+ Your controller:
304
+
305
+ class GraduationsController < InheritedResources::Base
306
+ has_scope :featured, :boolean => true, :only => :index
307
+ has_scope :by_methodology
308
+ has_scope :limit, :default => 10
309
+ end
310
+
311
+ Then for each request:
312
+
313
+ /projects
314
+ #=> acts like a normal request, but returning 10 projects
315
+
316
+ /projects?featured=true
317
+ #=> calls the featured named scope and bring 10 featured projects
318
+
319
+ /projects?featured=true&by_methodology=agile&limit=20
320
+ #=> brings 20 featured projects with methodology agile
321
+
322
+ You can retrieve the current scopes in use with :current_scopes method.
323
+ In the last case, it would return:
324
+
325
+ { :featured => "true", :by_methodology => "agile", :limit => "20" }
326
+
291
327
  Belongs to
292
328
  ----------
293
329
 
@@ -337,74 +373,12 @@ Warning: calling several belongs_to is the same as nesting them:
337
373
 
338
374
  In other words, the code above is the same as calling nested_belongs_to.
339
375
 
340
- Has Scope
341
- ---------
342
-
343
- InheritedResources besides belongs_to also speaks named_scope. Let's have a
344
- break from projects and talk about graduations. Your model:
345
-
346
- class Graduation < ActiveRecord::Base
347
- named_scope :featured, :conditions => { :featured => true }
348
- named_scope :by_degree, proc {|degree| { :conditions => { :degree => degree } } }
349
- named_scope :limit, proc{|limit| :limit => limit.to_i }
350
- end
351
-
352
- Your controller:
353
-
354
- class GraduationsController < InheritedResources::Base
355
- has_scope :featured, :boolean => true, :only => :index
356
- has_scope :by_degree
357
- has_scope :limit, :default => 10
358
- end
359
-
360
- Then for each request:
361
-
362
- /graduations
363
- #=> acts like a normal request, but returning 10 resources
364
-
365
- /graduations?featured=true
366
- #=> calls the featured named scope and bring 10 featured graduations
367
-
368
- /graduations?featured=true&by_degree=phd&limit=20
369
- #=> brings 20 featured graduations with phd degree
370
-
371
- You can also specify the target of the scope. Let's suppose that a Graduation
372
- has many Students:
373
-
374
- class StudentsController < InheritedResources::Base
375
- belongs_to :graduation
376
-
377
- has_scope :featured, :on => :graduation, :boolean => true, :only => :index
378
- has_scope :by_degree, :on => :graduation, :only => :index
379
- has_scope :limit, :on => :graduation, :default => 10
380
- end
381
-
382
- You can also do this inside the belongs to block:
383
-
384
- class StudentsController < InheritedResources::Base
385
- belongs_to :graduation do
386
- has_scope :featured, :boolean => true, :only => :index
387
- has_scope :by_degree, :only => :index
388
- has_scope :limit, :default => 10
389
- end
390
- end
391
-
392
- Or even better, you can load the scopes from another controller:
393
-
394
- class StudentsController < InheritedResources::Base
395
- belongs_to :graduation do
396
- load_scopes_from GraduationsController
397
- end
398
- end
399
-
400
- Another feature is that you can retrive the current scopes in use with
401
- the method <tt>current_scopes</tt>, that returns a hash.
402
-
403
376
  Polymorphic belongs to
404
377
  ----------------------
405
378
 
406
- Now let's go back to Projects which can now have Files, Messages and Tasks, and
407
- they are all commentable:
379
+ We can go even further. Let's suppose our Projects can now have Files, Messages
380
+ and Tasks, and they are all commentable. In this case, the best solution is to
381
+ use polymorphism:
408
382
 
409
383
  class CommentsController < InheritedResources::Base
410
384
  belongs_to :task, :file, :message, :polymorphic => true
@@ -419,6 +393,12 @@ You can even use it with nested resources:
419
393
  end
420
394
  end
421
395
 
396
+ The url in such cases can be:
397
+
398
+ /project/1/task/13/comments
399
+ /project/1/file/11/comments
400
+ /project/1/message/9/comments
401
+
422
402
  When using polymorphic associations, you get some free helpers:
423
403
 
424
404
  parent? #=> true
@@ -429,8 +409,8 @@ When using polymorphic associations, you get some free helpers:
429
409
  Optional belongs to
430
410
  -------------------
431
411
 
432
- Later you decide to create a view to show all comments, independing they belong
433
- to a task, file or message. You canm reuse your previous controller just doing:
412
+ Later you decide to create a view to show all comments, independent if they belong
413
+ to a task, file or message. You can reuse your polymorphic controller just doing:
434
414
 
435
415
  class ProjectsController < InheritedResources::Base
436
416
  belongs_to :task, :file, :message, :optional => true
@@ -113,7 +113,7 @@ module InheritedResources
113
113
 
114
114
  if chain
115
115
  if method_for_association_chain
116
- apply_scope_to(chain.send(method_for_association_chain), resource_instance_name)
116
+ apply_scope_to(chain.send(method_for_association_chain))
117
117
  else
118
118
  # This only happens when we specify begin_of_association_chain in
119
119
  # a singletion controller without parents. In this case, the chain
@@ -122,7 +122,7 @@ module InheritedResources
122
122
  chain
123
123
  end
124
124
  else
125
- apply_scope_to(resource_class, resource_instance_name)
125
+ apply_scope_to(resource_class)
126
126
  end
127
127
  end
128
128
 
@@ -273,7 +273,7 @@ module InheritedResources
273
273
  # Hook to apply scopes. By default returns only the target_object given.
274
274
  # It's extend by HasScopeHelpers.
275
275
  #
276
- def apply_scope_to(target_object, target_name) #:nodoc:
276
+ def apply_scope_to(target_object) #:nodoc:
277
277
  target_object
278
278
  end
279
279
 
@@ -72,7 +72,6 @@ module InheritedResources
72
72
  parent_config[:parent_class]
73
73
  end
74
74
 
75
- parent = apply_scope_to(parent, parent_symbol)
76
75
  parent = parent.send(parent_config[:finder], params[parent_config[:param]])
77
76
 
78
77
  instance_variable_set("@#{parent_config[:instance_name]}", parent)
@@ -106,40 +106,11 @@ module InheritedResources
106
106
  # /graduations?featured=true&by_degree=phd
107
107
  # #=> brings featured graduations with phd degree
108
108
  #
109
- # You can also specify the target of the scope. Let's suppose that a
110
- # Graduation has many students:
111
- #
112
- # class StudentsController < InheritedResources::Base
113
- # belongs_to :graduation
114
- #
115
- # has_scope :featured, :on => :graduation, :boolean => true, :only => :index
116
- # has_scope :by_degree, :on => :graduation, :only => :index
117
- # end
118
- #
119
- # You can also do this in a block:
120
- #
121
- # class StudentsController < InheritedResources::Base
122
- # belongs_to :graduation do
123
- # has_scope :featured, :boolean => true, :only => :index
124
- # has_scope :by_degree, :only => :index
125
- # end
126
- # end
127
- #
128
- # Or even better:
129
- #
130
- # class StudentsController < InheritedResources::Base
131
- # belongs_to :graduation do
132
- # load_scopes_from GraduationsController
133
- # end
134
- # end
135
- #
136
- # Another feature is that you can retrive the current scopes in use with
137
- # the method <tt>current_scopes</tt> that returns a hash.
109
+ # You can retrieve the current scopes in use with <tt>current_scopes</tt>
110
+ # method. In the last case, it would return: { :featured => "true", :by_degree => "phd" }
138
111
  #
139
112
  # == Options
140
113
  #
141
- # * <tt>:on</tt> - In each resource the scope is applied to. Defaults to the resource class.
142
- #
143
114
  # * <tt>:boolean</tt> - When set to true, call the scope only when the params is true or 1,
144
115
  # and does not send the value as argument.
145
116
  #
@@ -157,54 +128,21 @@ module InheritedResources
157
128
  options = scopes.extract_options!
158
129
 
159
130
  options.symbolize_keys!
160
- options.assert_valid_keys(:on, :boolean, :key, :only, :except, :default)
131
+ options.assert_valid_keys(:boolean, :key, :only, :except, :default)
161
132
 
162
- acts_as_has_scopes!
163
-
164
- scope_target = options.delete(:on) || @@_parent_block_name || self.resources_configuration[:self][:instance_name]
165
- target_config = self.scopes_configuration[scope_target.to_sym] ||= {}
166
-
167
- scopes.each do |scope|
168
- target_config[scope] ||= {}
169
- target_config[scope][:key] = options[:key] || scope
170
- target_config[scope][:only] = Array(options[:only])
171
- target_config[scope][:except] = Array(options[:except])
172
- target_config[scope][:boolean] = options[:boolean] if options.key?(:boolean)
173
- target_config[scope][:default] = options[:default] if options.key?(:default)
133
+ if self.scopes_configuration.empty?
134
+ include HasScopeHelpers
135
+ helper_method :current_scopes
174
136
  end
175
- end
176
137
 
177
- # Load scopes from another controller into the current controller.
178
- #
179
- # You can give :on as option if you want to load just a set of the another
180
- # controller scope.
181
- #
182
- # class TasksController < InheritedResources::Base
183
- # belongs_to :project
184
- # load_scopes_from CompaniesController # load all scopes
185
- # load_scopes_from ProjectsController, :on => :project # load scopes that apply on :project
186
- # end
187
- #
188
- # Whenever called inside a belongs to block, the :on value is guessed:
189
- #
190
- # class TasksController < InheritedResources::Base
191
- # belongs_to :project do
192
- # load_scopes_from ProjectsController # load scopes that apply on :project
193
- # end
194
- #
195
- # load_scopes_from CompaniesController # load all companies controller scopes
196
- # end
197
- #
198
- def load_scopes_from(scopes_controller, options={})
199
- to_merge = if on = options.delete(:on) || @@_parent_block_name
200
- scopes_controller.send(:scopes_configuration).slice(on)
201
- else
202
- scopes_controller.send(:scopes_configuration)
138
+ scopes.each do |scope|
139
+ self.scopes_configuration[scope] ||= {}
140
+ self.scopes_configuration[scope][:key] = options[:key] || scope
141
+ self.scopes_configuration[scope][:only] = Array(options[:only])
142
+ self.scopes_configuration[scope][:except] = Array(options[:except])
143
+ self.scopes_configuration[scope][:boolean] = options[:boolean] if options.key?(:boolean)
144
+ self.scopes_configuration[scope][:default] = options[:default] if options.key?(:default)
203
145
  end
204
-
205
- acts_as_has_scopes! # Add the module before merging scopes.
206
-
207
- self.scopes_configuration.deep_merge!(to_merge)
208
146
  end
209
147
 
210
148
  # Defines that this controller belongs to another resource.
@@ -303,20 +241,11 @@ module InheritedResources
303
241
  config[:route_name] = options.delete(:route_name) || symbol
304
242
  end
305
243
 
306
- # Regenerate url helpers only once when blocks are given
307
244
  if block_given?
308
- raise ArgumentError, "You cannot define multiple associations and give a block to belongs_to." if symbols.size > 1
309
-
310
- unless @@_parent_block_name
311
- @@_parent_block_name = symbols.first
312
- class_eval(&block)
313
- @@_parent_block_name = nil
314
- else
315
- class_eval(&block)
316
- end
245
+ class_eval(&block)
246
+ else
247
+ InheritedResources::UrlHelpers.create_resources_url_helpers!(self)
317
248
  end
318
-
319
- InheritedResources::UrlHelpers.create_resources_url_helpers!(self) unless @@_parent_block_name
320
249
  end
321
250
  alias :nested_belongs_to :belongs_to
322
251
 
@@ -361,19 +290,11 @@ module InheritedResources
361
290
  end
362
291
  end
363
292
 
364
- def acts_as_has_scopes! #:nodoc:
365
- if self.scopes_configuration.empty?
366
- include HasScopeHelpers
367
- helper_method :current_scopes
368
- end
369
- end
370
-
371
293
  # Initialize resources class accessors and set their default values.
372
294
  #
373
295
  def initialize_resources_class_accessors!(base) #:nodoc:
374
296
  # Add and protect class accessors
375
297
  base.class_eval do
376
- @@_parent_block_name = nil # Initialize parent flag
377
298
  metaklass = (class << self; self; end)
378
299
 
379
300
  RESOURCES_CLASS_ACCESSORS.each do |cattr|
@@ -12,11 +12,10 @@ module InheritedResources
12
12
 
13
13
  # Overwrites apply to scope to implement default scope logic.
14
14
  #
15
- def apply_scope_to(target_object, target_name) #:nodoc:
15
+ def apply_scope_to(target_object) #:nodoc:
16
16
  @current_scopes ||= {}
17
- scope_config = self.scopes_configuration[target_name] || {}
18
17
 
19
- scope_config.each do |scope, options|
18
+ self.scopes_configuration.each do |scope, options|
20
19
  next unless apply_scope_to_action?(options)
21
20
  key = options[:key]
22
21
 
@@ -21,7 +21,6 @@ end
21
21
  class SchoolsController < InheritedResources::Base
22
22
  has_scope :by_city
23
23
  has_scope :featured, :boolean => true, :only => :index, :key => :by_featured
24
- has_scope :limit, :default => 10, :except => :index, :on => :anything
25
24
  end
26
25
 
27
26
  class DeansController < InheritedResources::Base
@@ -123,101 +122,16 @@ class BelongsToErrorsTest < ActiveSupport::TestCase
123
122
  ensure
124
123
  DeansController.send(:parents_symbols=, [:school])
125
124
  end
126
-
127
- def test_belongs_to_raises_an_error_when_multiple_associations_are_given_with_block
128
- assert_raise ArgumentError, "You cannot define multiple associations and give a block to belongs_to." do
129
- DeansController.send(:belongs_to, :school, :another) do
130
- belongs_to :association
131
- end
132
- end
133
- ensure
134
- DeansController.send(:parents_symbols=, [:school])
135
- end
136
125
  end
137
126
 
138
127
  class HasScopeClassMethods < ActiveSupport::TestCase
139
-
140
128
  def test_scope_configuration_is_stored_as_hashes
141
129
  config = SchoolsController.send(:scopes_configuration)
142
- assert config.key?(:school)
143
- assert config.key?(:anything)
144
-
145
- assert config[:school].key?(:by_city)
146
- assert config[:school].key?(:featured)
147
- assert config[:anything].key?(:limit)
148
-
149
- assert_equal config[:school][:by_city], { :key => :by_city, :only => [], :except => [] }
150
- assert_equal config[:school][:featured], { :key => :by_featured, :only => [ :index ], :except => [], :boolean => true }
151
- assert_equal config[:anything][:limit], { :key => :limit, :except => [ :index ], :only => [], :default => 10 }
152
- end
153
-
154
- def test_scope_on_value_is_guessed_inside_belongs_to_blocks
155
- DeansController.send(:has_scope, :limit)
156
- DeansController.send(:belongs_to, :school) do
157
- has_scope :featured
158
- has_scope :another, :on => :dean
159
- end
160
-
161
- config = DeansController.send(:scopes_configuration)
162
- assert config[:school].key?(:featured)
163
- assert config[:dean].key?(:limit)
164
- assert config[:dean].key?(:another)
165
- ensure
166
- DeansController.send(:scopes_configuration=, {})
167
- end
168
-
169
- def test_scope_is_loaded_from_another_controller
170
- DeansController.send(:load_scopes_from, SchoolsController)
171
- config = DeansController.send(:scopes_configuration)
172
130
 
173
- assert config.key?(:school)
174
- assert config.key?(:anything)
131
+ assert config.key?(:by_city)
132
+ assert config.key?(:featured)
175
133
 
176
- assert config[:school].key?(:by_city)
177
- assert config[:school].key?(:featured)
178
- assert config[:anything].key?(:limit)
179
- ensure
180
- DeansController.send(:scopes_configuration=, {})
181
- end
182
-
183
- def test_scope_is_deep_merged_from_another_controller
184
- config = DeansController.send(:scopes_configuration)
185
-
186
- DeansController.send(:has_scope, :featured, :on => :school)
187
- assert_equal config[:school][:featured], { :key => :featured, :only => [ ], :except => [] }
188
-
189
- DeansController.send(:load_scopes_from, SchoolsController)
190
- assert config.key?(:school)
191
- assert config[:school].key?(:by_city)
192
- assert config[:school].key?(:featured)
193
- assert_equal config[:school][:featured], { :key => :by_featured, :only => [ :index ], :except => [], :boolean => true }
194
- end
195
-
196
- def test_scope_is_loaded_from_another_controller_with_on_specified
197
- DeansController.send(:load_scopes_from, SchoolsController, :on => :school)
198
- config = DeansController.send(:scopes_configuration)
199
-
200
- assert config.key?(:school)
201
- assert config[:school].key?(:by_city)
202
- assert config[:school].key?(:featured)
203
-
204
- assert !config.key?(:anything)
205
- ensure
206
- DeansController.send(:scopes_configuration=, {})
207
- end
208
-
209
- def test_scope_is_loaded_from_another_controller_with_on_guessed
210
- DeansController.send(:belongs_to, :school) do
211
- load_scopes_from SchoolsController
212
- end
213
- config = DeansController.send(:scopes_configuration)
214
-
215
- assert config.key?(:school)
216
- assert config[:school].key?(:by_city)
217
- assert config[:school].key?(:featured)
218
-
219
- assert !config.key?(:anything)
220
- ensure
221
- DeansController.send(:scopes_configuration=, {})
134
+ assert_equal config[:by_city], { :key => :by_city, :only => [], :except => [] }
135
+ assert_equal config[:featured], { :key => :by_featured, :only => [ :index ], :except => [], :boolean => true }
222
136
  end
223
137
  end
@@ -4,24 +4,11 @@ class Tree
4
4
  def self.human_name; 'Tree'; end
5
5
  end
6
6
 
7
- class Branch
8
- def self.human_name; 'Branch'; end
9
- end
10
-
11
7
  class TreesController < InheritedResources::Base
12
8
  has_scope :color
13
9
  has_scope :only_tall, :boolean => true, :only => :index
14
10
  has_scope :shadown_range, :default => 10, :except => [ :index, :show, :destroy ]
15
11
  has_scope :root_type, :key => :root
16
- has_scope :another, :on => :anything, :default => 10, :only => :destroy
17
- end
18
-
19
- class BranchesController < InheritedResources::Base
20
- belongs_to :tree do
21
- load_scopes_from TreesController
22
- end
23
-
24
- has_scope :by_size
25
12
  end
26
13
 
27
14
  class HasScopeTest < ActionController::TestCase
@@ -105,15 +92,6 @@ class HasScopeTest < ActionController::TestCase
105
92
  assert_equal({ :root => 'outside' }, assigns(:current_scopes))
106
93
  end
107
94
 
108
- def test_scope_on_another_object_is_never_called
109
- Tree.expects(:another).never
110
- Tree.expects(:find).with('42').returns(mock_tree)
111
- mock_tree.expects(:destroy)
112
- delete :destroy, :id => '42'
113
- assert_equal(mock_tree, assigns(:tree))
114
- assert_equal({ }, assigns(:current_scopes))
115
- end
116
-
117
95
  protected
118
96
 
119
97
  def mock_tree(stubs={})
@@ -122,50 +100,3 @@ class HasScopeTest < ActionController::TestCase
122
100
 
123
101
  end
124
102
 
125
- class NestedHasScopeTest < ActionController::TestCase
126
- tests BranchesController
127
-
128
- def test_scope_is_applied_on_parent
129
- Tree.expects(:only_tall).with().returns(Tree).in_sequence
130
- Tree.expects(:find).with('42').returns(mock_tree).in_sequence
131
- mock_tree.expects(:branches).returns(Branch).in_sequence
132
- Branch.expects(:find).with(:all).returns([mock_branch]).in_sequence
133
- get :index, :only_tall => 'true', :tree_id => '42'
134
- assert_equal(mock_tree, assigns(:tree))
135
- assert_equal([mock_branch], assigns(:branches))
136
- assert_equal({ :only_tall => 'true' }, assigns(:current_scopes))
137
- end
138
-
139
- def test_scope_is_applied_on_resource
140
- Tree.expects(:find).with('42').returns(mock_tree).in_sequence
141
- mock_tree.expects(:branches).returns(Branch).in_sequence
142
- Branch.expects(:by_size).with('10').returns(Branch).in_sequence
143
- Branch.expects(:find).with(:all).returns([mock_branch]).in_sequence
144
- get :index, :by_size => '10', :tree_id => '42'
145
- assert_equal(mock_tree, assigns(:tree))
146
- assert_equal([mock_branch], assigns(:branches))
147
- assert_equal({ :by_size => '10' }, assigns(:current_scopes))
148
- end
149
-
150
- def test_scope_is_applied_on_both_resource_and_parent
151
- Tree.expects(:only_tall).with().returns(Tree).in_sequence
152
- Tree.expects(:find).with('42').returns(mock_tree).in_sequence
153
- mock_tree.expects(:branches).returns(Branch).in_sequence
154
- Branch.expects(:by_size).with('10').returns(Branch).in_sequence
155
- Branch.expects(:find).with(:all).returns([mock_branch]).in_sequence
156
- get :index, :only_tall => 'true', :by_size => '10', :tree_id => '42'
157
- assert_equal(mock_tree, assigns(:tree))
158
- assert_equal([mock_branch], assigns(:branches))
159
- assert_equal({ :by_size => '10', :only_tall => 'true' }, assigns(:current_scopes))
160
- end
161
-
162
- protected
163
-
164
- def mock_tree(stubs={})
165
- @mock_tree ||= mock(stubs)
166
- end
167
-
168
- def mock_branch(stubs={})
169
- @mock_branch ||= mock(stubs)
170
- end
171
- end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: josevalim-inherited_resources
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - "Jos\xC3\xA9 Valim"
@@ -85,10 +85,6 @@ test_files:
85
85
  - test/test_helper.rb
86
86
  - test/url_helpers_test.rb
87
87
  - test/locales/en.yml
88
- - test/views/branches/edit.html.erb
89
- - test/views/branches/index.html.erb
90
- - test/views/branches/new.html.erb
91
- - test/views/branches/show.html.erb
92
88
  - test/views/cities/edit.html.erb
93
89
  - test/views/cities/index.html.erb
94
90
  - test/views/cities/new.html.erb
@@ -1 +0,0 @@
1
- Edit HTML
@@ -1 +0,0 @@
1
- Index HTML
@@ -1 +0,0 @@
1
- New HTML
@@ -1 +0,0 @@
1
- Show HTML