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
|
||||
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
|
||||
# keys and their class restriction as values.
|
||||
class_attribute :serialized_attributes
|
||||
|
@ -1261,11 +1265,14 @@ MSG
|
|||
self.default_scopes = default_scopes + [scope]
|
||||
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:
|
||||
return unless apply_default_scope
|
||||
self.apply_default_scope = false
|
||||
|
||||
if method(:default_scope).owner != Base.singleton_class
|
||||
# Use relation.scoping to ensure we ignore whatever the current value of
|
||||
# self.current_scope may be.
|
||||
relation.scoping { default_scope }
|
||||
default_scope
|
||||
elsif default_scopes.any?
|
||||
default_scopes.inject(relation) do |default_scope, scope|
|
||||
if scope.is_a?(Hash)
|
||||
|
@ -1277,6 +1284,8 @@ MSG
|
|||
end
|
||||
end
|
||||
end
|
||||
ensure
|
||||
self.apply_default_scope = true
|
||||
end
|
||||
|
||||
# Returns the class type of the record using the current module as a prefix. So descendants of
|
||||
|
|
|
@ -424,7 +424,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
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_scoped = false
|
||||
default_scope
|
||||
|
|
|
@ -312,6 +312,14 @@ class DefaultScopingTest < ActiveRecord::TestCase
|
|||
assert_equal [developers(:david).becomes(ClassMethodDeveloperCalledDavid)], ClassMethodDeveloperCalledDavid.all
|
||||
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
|
||||
assert_equal [developers(:david).becomes(LazyLambdaDeveloperCalledDavid)], LazyLambdaDeveloperCalledDavid.all
|
||||
end
|
||||
|
|
|
@ -127,6 +127,21 @@ class ClassMethodDeveloperCalledDavid < ActiveRecord::Base
|
|||
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
|
||||
self.table_name = 'developers'
|
||||
|
||||
|
|
Loading…
Reference in a new issue