From 6f84c73dc48538202766cff3d973a53d3c30848e Mon Sep 17 00:00:00 2001 From: Jon Leighton Date: Mon, 18 Apr 2011 23:15:38 +0100 Subject: Un-deprecate using 'default_scope' as a macro, but if you are calling the macro multiple times that will give deprecation warnings, and in 3.2 we will simply overwrite the default scope when you call the macro multiple times. --- activerecord/CHANGELOG | 27 ++-- activerecord/lib/active_record/base.rb | 47 +++--- activerecord/test/cases/relation_scoping_test.rb | 181 +++-------------------- activerecord/test/models/bulb.rb | 5 +- activerecord/test/models/car.rb | 8 +- activerecord/test/models/categorization.rb | 5 +- activerecord/test/models/developer.rb | 68 ++------- activerecord/test/models/post.rb | 5 +- activerecord/test/models/reference.rb | 5 +- activerecord/test/models/without_table.rb | 4 +- 10 files changed, 85 insertions(+), 270 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 6b3d408720..4ae82a6419 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -11,25 +11,34 @@ [Jon Leighton] -* Deprecated support for passing hashes and relations to 'default_scope'. Please create a class - method for your scope instead. For example, change this: +* Calling 'default_scope' multiple times in a class (including when a superclass calls + 'default_scope') is deprecated. The current behavior is that this will merge the default + scopes together: - class Post < ActiveRecord::Base + class Post < ActiveRecord::Base # Rails 3.1 + default_scope where(:published => true) + default_scope where(:hidden => false) + # The default scope is now: where(:published => true, :hidden => false) + end + + In Rails 3.2, the behavior will be changed to overwrite previous scopes: + + class Post < ActiveRecord::Base # Rails 3.2 default_scope where(:published => true) + default_scope where(:hidden => false) + # The default scope is now: where(:hidden => false) end - To this: + If you wish to merge default scopes in special ways, it is recommended to define your default + scope as a class method and use the standard techniques for sharing code (inheritance, mixins, + etc.): class Post < ActiveRecord::Base def self.default_scope - where(:published => true) + where(:published => true).where(:hidden => false) end end - Rationale: It will make the implementation simpler because we can simply use inheritance to - handle inheritance scenarios, rather than trying to make up our own rules about what should - happen when you call default_scope multiple times or in subclasses. - [Jon Leighton] * PostgreSQL adapter only supports PostgreSQL version 8.2 and higher. diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 08a41e2d8b..5b316c17be 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1177,13 +1177,11 @@ MSG Thread.current[:"#{self}_current_scope"] = scope end - # Implement this method in your model to set a default scope for all operations on + # Use this macro in your model to set a default scope for all operations on # the model. # # class Person < ActiveRecord::Base - # def self.default_scope - # order('last_name, first_name') - # end + # default_scope order('last_name, first_name') # end # # Person.all # => SELECT * FROM people ORDER BY last_name, first_name @@ -1192,39 +1190,48 @@ MSG # applied while updating a record. # # class Article < ActiveRecord::Base - # def self.default_scope - # where(:published => true) - # end + # default_scope where(:published => true) # end # # Article.new.published # => true # Article.create.published # => true # - # === Deprecation warning + # If you need to do more complex things with a default scope, you can alternatively + # define it as a class method: # - # There is an alternative syntax as follows: - # - # class Person < ActiveRecord::Base - # default_scope order('last_name, first_name') + # class Article < ActiveRecord::Base + # def self.default_scope + # # Should return a scope, you can call 'super' here etc. + # end # end - # - # This is now deprecated and will be removed in Rails 3.2. def default_scope(scope = {}) - ActiveSupport::Deprecation.warn <<-WARN -Passing a hash or scope to default_scope is deprecated and will be removed in Rails 3.2. You should create a class method for your scope instead. For example, change this: + if default_scopes.length != 0 + ActiveSupport::Deprecation.warn <<-WARN +Calling 'default_scope' multiple times in a class (including when a superclass calls 'default_scope') is deprecated. The current behavior is that this will merge the default scopes together: -class Post < ActiveRecord::Base +class Post < ActiveRecord::Base # Rails 3.1 default_scope where(:published => true) + default_scope where(:hidden => false) + # The default scope is now: where(:published => true, :hidden => false) end -To this: +In Rails 3.2, the behavior will be changed to overwrite previous scopes: + +class Post < ActiveRecord::Base # Rails 3.2 + default_scope where(:published => true) + default_scope where(:hidden => false) + # The default scope is now: where(:hidden => false) +end + +If you wish to merge default scopes in special ways, it is recommended to define your default scope as a class method and use the standard techniques for sharing code (inheritance, mixins, etc.): class Post < ActiveRecord::Base def self.default_scope - where(:published => true) + where(:published => true).where(:hidden => false) end end -WARN + WARN + end self.default_scopes = default_scopes.dup << scope end diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb index 5079aec9ba..e231371cf8 100644 --- a/activerecord/test/cases/relation_scoping_test.rb +++ b/activerecord/test/cases/relation_scoping_test.rb @@ -308,6 +308,10 @@ class DefaultScopingTest < ActiveRecord::TestCase assert_equal expected, received end + def test_default_scope_as_class_method + assert_equal [developers(:david).becomes(ClassMethodDeveloperCalledDavid)], ClassMethodDeveloperCalledDavid.all + end + def test_default_scope_is_unscoped_on_find assert_equal 1, DeveloperCalledDavid.count assert_equal 11, DeveloperCalledDavid.unscoped.count @@ -339,6 +343,12 @@ class DefaultScopingTest < ActiveRecord::TestCase assert_equal 50000, wheres[:salary] end + def test_default_scope_with_multiple_calls + wheres = MultiplePoorDeveloperCalledJamis.scoped.where_values_hash + assert_equal "Jamis", wheres[:name] + assert_equal 50000, wheres[:salary] + end + def test_method_scope expected = Developer.find(:all, :order => 'salary DESC, name DESC').collect { |dev| dev.salary } received = DeveloperOrderedBySalary.all_ordered_by_name.collect { |dev| dev.salary } @@ -434,175 +444,18 @@ class DefaultScopingTest < ActiveRecord::TestCase assert DeveloperCalledJamis.unscoped.poor.include?(developers(:david).becomes(DeveloperCalledJamis)) assert_equal 10, DeveloperCalledJamis.unscoped.poor.length end -end - -class DeprecatedDefaultScopingTest < ActiveRecord::TestCase - fixtures :developers, :posts - - def test_default_scope - expected = Developer.find(:all, :order => 'salary DESC').collect { |dev| dev.salary } - received = DeprecatedDeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary } - assert_equal expected, received - end - def test_default_scope_is_unscoped_on_find - assert_equal 1, DeprecatedDeveloperCalledDavid.count - assert_equal 11, DeprecatedDeveloperCalledDavid.unscoped.count - end + def test_multiple_default_scope_calls_are_deprecated + klass = Class.new(ActiveRecord::Base) - def test_default_scope_is_unscoped_on_create - assert_nil DeprecatedDeveloperCalledJamis.unscoped.create!.name - end - - def test_default_scope_with_conditions_string - assert_equal Developer.find_all_by_name('David').map(&:id).sort, DeprecatedDeveloperCalledDavid.find(:all).map(&:id).sort - assert_equal nil, DeprecatedDeveloperCalledDavid.create!.name - end - - def test_default_scope_with_conditions_hash - assert_equal Developer.find_all_by_name('Jamis').map(&:id).sort, DeprecatedDeveloperCalledJamis.find(:all).map(&:id).sort - assert_equal 'Jamis', DeprecatedDeveloperCalledJamis.create!.name - end - - def test_default_scoping_with_threads - 2.times do - Thread.new { assert DeprecatedDeveloperOrderedBySalary.scoped.to_sql.include?('salary DESC') }.join + assert_not_deprecated do + klass.send(:default_scope, :foo => :bar) end - end - - def test_default_scoping_with_inheritance - # Inherit a class having a default scope and define a new default scope - klass = Class.new(DeprecatedDeveloperOrderedBySalary) - ActiveSupport::Deprecation.silence { klass.send :default_scope, :limit => 1 } - - # Scopes added on children should append to parent scope - assert_equal [developers(:jamis).id], klass.all.map(&:id) - - # Parent should still have the original scope - assert_equal Developer.order('salary DESC').map(&:id), DeprecatedDeveloperOrderedBySalary.all.map(&:id) - end - - def test_default_scope_called_twice_merges_conditions - Developer.destroy_all - Developer.create!(:name => "David", :salary => 80000) - Developer.create!(:name => "David", :salary => 100000) - Developer.create!(:name => "Brian", :salary => 100000) - klass = Class.new(Developer) - ActiveSupport::Deprecation.silence do - klass.__send__ :default_scope, :conditions => { :name => "David" } - klass.__send__ :default_scope, :conditions => { :salary => 100000 } + assert_deprecated do + klass.send(:default_scope, :foo => :bar) end - assert_equal 1, klass.count - assert_equal "David", klass.first.name - assert_equal 100000, klass.first.salary - end - - def test_default_scope_called_twice_in_different_place_merges_where_clause - Developer.destroy_all - Developer.create!(:name => "David", :salary => 80000) - Developer.create!(:name => "David", :salary => 100000) - Developer.create!(:name => "Brian", :salary => 100000) - - klass = Class.new(Developer) - ActiveSupport::Deprecation.silence do - klass.class_eval do - default_scope where("name = 'David'") - default_scope where("salary = 100000") - end - end - - assert_equal 1, klass.count - assert_equal "David", klass.first.name - assert_equal 100000, klass.first.salary - end - def test_method_scope - expected = Developer.find(:all, :order => 'salary DESC, name DESC').collect { |dev| dev.salary } - received = DeprecatedDeveloperOrderedBySalary.all_ordered_by_name.collect { |dev| dev.salary } - assert_equal expected, received - end - - def test_nested_scope - expected = Developer.find(:all, :order => 'salary DESC, name DESC').collect { |dev| dev.salary } - received = DeprecatedDeveloperOrderedBySalary.send(:with_scope, :find => { :order => 'name DESC'}) do - DeprecatedDeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary } - end - assert_equal expected, received - end - - def test_scope_overwrites_default - expected = Developer.find(:all, :order => 'salary DESC, name DESC').collect { |dev| dev.name } - received = DeprecatedDeveloperOrderedBySalary.by_name.find(:all).collect { |dev| dev.name } - assert_equal expected, received - end - - def test_reorder_overrides_default_scope_order - expected = Developer.order('name DESC').collect { |dev| dev.name } - received = DeprecatedDeveloperOrderedBySalary.reorder('name DESC').collect { |dev| dev.name } - assert_equal expected, received - end - - def test_nested_exclusive_scope - expected = Developer.find(:all, :limit => 100).collect { |dev| dev.salary } - received = DeprecatedDeveloperOrderedBySalary.send(:with_exclusive_scope, :find => { :limit => 100 }) do - DeprecatedDeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary } - end - assert_equal expected, received - end - - def test_order_in_default_scope_should_prevail - expected = Developer.find(:all, :order => 'salary desc').collect { |dev| dev.salary } - received = DeprecatedDeveloperOrderedBySalary.find(:all, :order => 'salary').collect { |dev| dev.salary } - assert_equal expected, received - end - - def test_default_scope_using_relation - posts = DeprecatedPostWithComment.scoped - assert_equal 2, posts.to_a.length - assert_equal posts(:thinking), posts.first - end - - def test_create_attribute_overwrites_default_scoping - assert_equal 'David', DeprecatedPoorDeveloperCalledJamis.create!(:name => 'David').name - assert_equal 200000, DeprecatedPoorDeveloperCalledJamis.create!(:name => 'David', :salary => 200000).salary - end - - def test_create_attribute_overwrites_default_values - assert_equal nil, DeprecatedPoorDeveloperCalledJamis.create!(:salary => nil).salary - assert_equal 50000, DeprecatedPoorDeveloperCalledJamis.create!(:name => 'David').salary - end - - def test_default_scope_attribute - jamis = DeprecatedPoorDeveloperCalledJamis.new(:name => 'David') - assert_equal 50000, jamis.salary - end - - def test_where_attribute - aaron = DeprecatedPoorDeveloperCalledJamis.where(:salary => 20).new(:name => 'Aaron') - assert_equal 20, aaron.salary - assert_equal 'Aaron', aaron.name - end - - def test_where_attribute_merge - aaron = DeprecatedPoorDeveloperCalledJamis.where(:name => 'foo').new(:name => 'Aaron') - assert_equal 'Aaron', aaron.name - end - - def test_create_with_merge - aaron = DeprecatedPoorDeveloperCalledJamis.create_with(:name => 'foo', :salary => 20).merge( - DeprecatedPoorDeveloperCalledJamis.create_with(:name => 'Aaron')).new - assert_equal 20, aaron.salary - assert_equal 'Aaron', aaron.name - - aaron = DeprecatedPoorDeveloperCalledJamis.create_with(:name => 'foo', :salary => 20). - create_with(:name => 'Aaron').new - assert_equal 20, aaron.salary - assert_equal 'Aaron', aaron.name - end - - def test_create_with_reset - jamis = DeprecatedPoorDeveloperCalledJamis.create_with(:name => 'Aaron').create_with(nil).new - assert_equal 'Jamis', jamis.name + assert_equal 2, klass.default_scopes.length end end diff --git a/activerecord/test/models/bulb.rb b/activerecord/test/models/bulb.rb index 89ee5416bf..c68d008c26 100644 --- a/activerecord/test/models/bulb.rb +++ b/activerecord/test/models/bulb.rb @@ -1,8 +1,5 @@ class Bulb < ActiveRecord::Base - def self.default_scope - where :name => 'defaulty' - end - + default_scope where(:name => 'defaulty') belongs_to :car attr_reader :scope_after_initialize diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb index a978debb58..b036f0f5c9 100644 --- a/activerecord/test/models/car.rb +++ b/activerecord/test/models/car.rb @@ -15,13 +15,9 @@ class Car < ActiveRecord::Base end class CoolCar < Car - def self.default_scope - order 'name desc' - end + default_scope :order => 'name desc' end class FastCar < Car - def self.default_scope - order 'name desc' - end + default_scope :order => 'name desc' end diff --git a/activerecord/test/models/categorization.rb b/activerecord/test/models/categorization.rb index 39441e8610..4bd980e606 100644 --- a/activerecord/test/models/categorization.rb +++ b/activerecord/test/models/categorization.rb @@ -12,10 +12,7 @@ end class SpecialCategorization < ActiveRecord::Base self.table_name = 'categorizations' - - def self.default_scope - where(:special => true) - end + default_scope where(:special => true) belongs_to :author belongs_to :category diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index 10385ba899..93363f47c5 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -86,10 +86,7 @@ end class DeveloperOrderedBySalary < ActiveRecord::Base self.table_name = 'developers' - - def self.default_scope - order('salary DESC') - end + default_scope :order => 'salary DESC' scope :by_name, order('name DESC') @@ -102,74 +99,41 @@ end class DeveloperCalledDavid < ActiveRecord::Base self.table_name = 'developers' - - def self.default_scope - where "name = 'David'" - end + default_scope where("name = 'David'") end -class DeveloperCalledJamis < ActiveRecord::Base +class ClassMethodDeveloperCalledDavid < ActiveRecord::Base self.table_name = 'developers' def self.default_scope - where :name => 'Jamis' + where(:name => 'David') end - - scope :poor, where('salary < 150000') end -class AbstractDeveloperCalledJamis < ActiveRecord::Base - self.abstract_class = true - - def self.default_scope - where :name => 'Jamis' - end +class DeveloperCalledJamis < ActiveRecord::Base + self.table_name = 'developers' + default_scope where(:name => 'Jamis') + scope :poor, where('salary < 150000') end class PoorDeveloperCalledJamis < ActiveRecord::Base self.table_name = 'developers' - - def self.default_scope - where :name => 'Jamis', :salary => 50000 - end + default_scope where(:name => 'Jamis', :salary => 50000) end class InheritedPoorDeveloperCalledJamis < DeveloperCalledJamis self.table_name = 'developers' - def self.default_scope - super.where :salary => 50000 + ActiveSupport::Deprecation.silence do + default_scope where(:salary => 50000) end end -ActiveSupport::Deprecation.silence do - class DeprecatedDeveloperOrderedBySalary < ActiveRecord::Base - self.table_name = 'developers' - default_scope :order => 'salary DESC' - - def self.by_name - order('name DESC') - end - - def self.all_ordered_by_name - with_scope(:find => { :order => 'name DESC' }) do - find(:all) - end - end - end - - class DeprecatedDeveloperCalledDavid < ActiveRecord::Base - self.table_name = 'developers' - default_scope :conditions => "name = 'David'" - end - - class DeprecatedDeveloperCalledJamis < ActiveRecord::Base - self.table_name = 'developers' - default_scope :conditions => { :name => 'Jamis' } - end +class MultiplePoorDeveloperCalledJamis < ActiveRecord::Base + self.table_name = 'developers' + default_scope where(:name => 'Jamis') - class DeprecatedPoorDeveloperCalledJamis < ActiveRecord::Base - self.table_name = 'developers' - default_scope :conditions => { :name => 'Jamis', :salary => 50000 } + ActiveSupport::Deprecation.silence do + default_scope where(:salary => 50000) end end diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index 34cea60053..80296032bb 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -157,10 +157,7 @@ end class FirstPost < ActiveRecord::Base self.table_name = 'posts' - - def self.default_scope - where(:id => 1) - end + default_scope where(:id => 1) has_many :comments, :foreign_key => :post_id has_one :comment, :foreign_key => :post_id diff --git a/activerecord/test/models/reference.rb b/activerecord/test/models/reference.rb index 76c0a1a32e..c5af0b5d5f 100644 --- a/activerecord/test/models/reference.rb +++ b/activerecord/test/models/reference.rb @@ -19,8 +19,5 @@ end class BadReference < ActiveRecord::Base self.table_name = 'references' - - def self.default_scope - where :favourite => false - end + default_scope where(:favourite => false) end diff --git a/activerecord/test/models/without_table.rb b/activerecord/test/models/without_table.rb index 1a63d6ceb6..184ab1649e 100644 --- a/activerecord/test/models/without_table.rb +++ b/activerecord/test/models/without_table.rb @@ -1,5 +1,3 @@ class WithoutTable < ActiveRecord::Base - def self.default_scope - where(:published => true) - end + default_scope where(:published => true) end -- cgit v1.2.3