aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/CHANGELOG27
-rw-r--r--activerecord/lib/active_record/base.rb47
-rw-r--r--activerecord/test/cases/relation_scoping_test.rb181
-rw-r--r--activerecord/test/models/bulb.rb5
-rw-r--r--activerecord/test/models/car.rb8
-rw-r--r--activerecord/test/models/categorization.rb5
-rw-r--r--activerecord/test/models/developer.rb68
-rw-r--r--activerecord/test/models/post.rb5
-rw-r--r--activerecord/test/models/reference.rb5
-rw-r--r--activerecord/test/models/without_table.rb4
10 files changed, 85 insertions, 270 deletions
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