mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Fix infinite recursion where a lazy default scope references a scope. Fixes #1264.
This commit is contained in:
parent
d21fef324b
commit
c69111ba5f
4 changed files with 36 additions and 4 deletions
|
@ -428,6 +428,10 @@ module ActiveRecord #:nodoc:
|
||||||
class_attribute :default_scopes, :instance_writer => false
|
class_attribute :default_scopes, :instance_writer => false
|
||||||
self.default_scopes = []
|
self.default_scopes = []
|
||||||
|
|
||||||
|
# Boolean flag to prevent infinite recursion when evaluating default scopes
|
||||||
|
class_attribute :apply_default_scope, :instance_writer => false
|
||||||
|
self.apply_default_scope = true
|
||||||
|
|
||||||
# Returns a hash of all the attributes that have been specified for serialization as
|
# Returns a hash of all the attributes that have been specified for serialization as
|
||||||
# keys and their class restriction as values.
|
# keys and their class restriction as values.
|
||||||
class_attribute :serialized_attributes
|
class_attribute :serialized_attributes
|
||||||
|
@ -1261,11 +1265,14 @@ MSG
|
||||||
self.default_scopes = default_scopes + [scope]
|
self.default_scopes = default_scopes + [scope]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# The apply_default_scope flag is used to prevent an infinite recursion situation where
|
||||||
|
# a default scope references a scope which has a default scope which references a scope...
|
||||||
def build_default_scope #:nodoc:
|
def build_default_scope #:nodoc:
|
||||||
|
return unless apply_default_scope
|
||||||
|
self.apply_default_scope = false
|
||||||
|
|
||||||
if method(:default_scope).owner != Base.singleton_class
|
if method(:default_scope).owner != Base.singleton_class
|
||||||
# Use relation.scoping to ensure we ignore whatever the current value of
|
default_scope
|
||||||
# self.current_scope may be.
|
|
||||||
relation.scoping { default_scope }
|
|
||||||
elsif default_scopes.any?
|
elsif default_scopes.any?
|
||||||
default_scopes.inject(relation) do |default_scope, scope|
|
default_scopes.inject(relation) do |default_scope, scope|
|
||||||
if scope.is_a?(Hash)
|
if scope.is_a?(Hash)
|
||||||
|
@ -1277,6 +1284,8 @@ MSG
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
ensure
|
||||||
|
self.apply_default_scope = true
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the class type of the record using the current module as a prefix. So descendants of
|
# Returns the class type of the record using the current module as a prefix. So descendants of
|
||||||
|
|
|
@ -424,7 +424,7 @@ module ActiveRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def with_default_scope #:nodoc:
|
def with_default_scope #:nodoc:
|
||||||
if default_scoped? && default_scope = @klass.send(:build_default_scope)
|
if default_scoped? && default_scope = klass.send(:build_default_scope)
|
||||||
default_scope = default_scope.merge(self)
|
default_scope = default_scope.merge(self)
|
||||||
default_scope.default_scoped = false
|
default_scope.default_scoped = false
|
||||||
default_scope
|
default_scope
|
||||||
|
|
|
@ -312,6 +312,14 @@ class DefaultScopingTest < ActiveRecord::TestCase
|
||||||
assert_equal [developers(:david).becomes(ClassMethodDeveloperCalledDavid)], ClassMethodDeveloperCalledDavid.all
|
assert_equal [developers(:david).becomes(ClassMethodDeveloperCalledDavid)], ClassMethodDeveloperCalledDavid.all
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_default_scope_as_class_method_referencing_scope
|
||||||
|
assert_equal [developers(:david).becomes(ClassMethodReferencingScopeDeveloperCalledDavid)], ClassMethodReferencingScopeDeveloperCalledDavid.all
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_default_scope_as_block_referencing_scope
|
||||||
|
assert_equal [developers(:david).becomes(LazyBlockReferencingScopeDeveloperCalledDavid)], LazyBlockReferencingScopeDeveloperCalledDavid.all
|
||||||
|
end
|
||||||
|
|
||||||
def test_default_scope_with_lambda
|
def test_default_scope_with_lambda
|
||||||
assert_equal [developers(:david).becomes(LazyLambdaDeveloperCalledDavid)], LazyLambdaDeveloperCalledDavid.all
|
assert_equal [developers(:david).becomes(LazyLambdaDeveloperCalledDavid)], LazyLambdaDeveloperCalledDavid.all
|
||||||
end
|
end
|
||||||
|
|
|
@ -127,6 +127,21 @@ class ClassMethodDeveloperCalledDavid < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class ClassMethodReferencingScopeDeveloperCalledDavid < ActiveRecord::Base
|
||||||
|
self.table_name = 'developers'
|
||||||
|
scope :david, where(:name => 'David')
|
||||||
|
|
||||||
|
def self.default_scope
|
||||||
|
david
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class LazyBlockReferencingScopeDeveloperCalledDavid < ActiveRecord::Base
|
||||||
|
self.table_name = 'developers'
|
||||||
|
scope :david, where(:name => 'David')
|
||||||
|
default_scope { david }
|
||||||
|
end
|
||||||
|
|
||||||
class DeveloperCalledJamis < ActiveRecord::Base
|
class DeveloperCalledJamis < ActiveRecord::Base
|
||||||
self.table_name = 'developers'
|
self.table_name = 'developers'
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue