josevalim-inherited_resources 0.7.0 → 0.7.1

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