Skip to content

Loading…

Removes caching from ActiveRecord::Core::ClassMethods#relation #5718

Merged
merged 2 commits into from

2 participants

@benedikt

The #relation method gets called in four places and the return value was instantly cloned in three of them. The only place that did not clone was ActiveRecord::Scoping::Default::ClassMethods#unscoped. This introduced a bug described in #5667 and should really clone the relation, too. This means all four places would clone the relation, so it doesn't make a lot of sense caching it in the first place.

The four places with calls to relations are:

activerecord/lib/active_record/scoping/default.rb:110:in `block in build_default_scope'"
activerecord/lib/active_record/scoping/default.rb:42:in `unscoped'"
activerecord/lib/active_record/scoping/named.rb:38:in `scoped'"
activerecord/lib/active_record/scoping/named.rb:52:in `scope_attributes'"
@benedikt benedikt Removes caching from ActiveRecord::Core::ClassMethods#relation
The #relation method gets called in four places and the return value was instantly cloned in three of them. The only place that did not clone was ActiveRecord::Scoping::Default::ClassMethods#unscoped. This introduced a bug described in #5667 and should really clone the relation, too. This means all four places would clone the relation, so it doesn't make a lot of sense caching it in the first place.

The four places with calls to relations are:

activerecord/lib/active_record/scoping/default.rb:110:in `block in build_default_scope'"
activerecord/lib/active_record/scoping/default.rb:42:in `unscoped'"
activerecord/lib/active_record/scoping/named.rb:38:in `scoped'"
activerecord/lib/active_record/scoping/named.rb:52:in `scope_attributes'"
68677ff
@jeremy jeremy commented on an outdated diff
activerecord/test/cases/relations_test.rb
@@ -1099,6 +1099,10 @@ def test_unscoped_block_style
assert_equal 'honda', FastCar.unscoped { FastCar.order_using_old_style.limit(1).first.name}
end
+ def test_unscoped_relation_clones
+ assert_not_equal CoolCar.unscoped.object_id, CoolCar.unscoped.object_id
@jeremy Ruby on Rails member
jeremy added a note

This feels like testing an implementation detail. Got a test from #5667 that fails when the relation isn't cloned?

@benedikt
benedikt added a note

You're right. I'll change it to a test that resembles the #5667 issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@benedikt

@jeremy I changed the test to resemble the remaining issue described in #5667. Is it okay or do you want me to change anything else?

/cc @jonleighton

@jeremy jeremy merged commit 9f37f33 into rails:master
@jeremy
Ruby on Rails member

Thanks @benedikt !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 3, 2012
  1. @benedikt

    Removes caching from ActiveRecord::Core::ClassMethods#relation

    benedikt committed
    The #relation method gets called in four places and the return value was instantly cloned in three of them. The only place that did not clone was ActiveRecord::Scoping::Default::ClassMethods#unscoped. This introduced a bug described in #5667 and should really clone the relation, too. This means all four places would clone the relation, so it doesn't make a lot of sense caching it in the first place.
    
    The four places with calls to relations are:
    
    activerecord/lib/active_record/scoping/default.rb:110:in `block in build_default_scope'"
    activerecord/lib/active_record/scoping/default.rb:42:in `unscoped'"
    activerecord/lib/active_record/scoping/named.rb:38:in `scoped'"
    activerecord/lib/active_record/scoping/named.rb:52:in `scope_attributes'"
  2. @benedikt
View
7 activerecord/lib/active_record/core.rb
@@ -137,12 +137,12 @@ def arel_engine
private
def relation #:nodoc:
- @relation ||= Relation.new(self, arel_table)
+ relation = Relation.new(self, arel_table)
if finder_needs_type_condition?
- @relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
+ relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
else
- @relation
+ relation
end
end
end
@@ -351,7 +351,6 @@ def init_internals
@attributes[pk] = nil unless @attributes.key?(pk)
- @relation = nil
@aggregation_cache = {}
@association_cache = {}
@attributes_cache = {}
View
4 activerecord/lib/active_record/scoping/named.rb
@@ -35,7 +35,7 @@ def scoped(options = nil)
if current_scope
current_scope.clone
else
- scope = relation.clone
+ scope = relation
scope.default_scoped = true
scope
end
@@ -49,7 +49,7 @@ def scope_attributes # :nodoc:
if current_scope
current_scope.scope_for_create
else
- scope = relation.clone
+ scope = relation
scope.default_scoped = true
scope.scope_for_create
end
View
6 activerecord/test/cases/associations/eager_test.rb
@@ -1174,6 +1174,12 @@ def test_deep_including_through_habtm
assert_equal Comment.find(1), Comment.preload(:post => :comments).scoping { Comment.find(1) }
end
+ test "circular preload does not modify unscoped" do
+ expected = FirstPost.unscoped.find(2)
+ FirstPost.preload(:comments => :first_post).find(1)
+ assert_equal expected, FirstPost.unscoped.find(2)
+ end
+
test "preload ignores the scoping" do
assert_equal(
Comment.find(1).post,
View
2 activerecord/test/models/comment.rb
@@ -9,6 +9,8 @@ class Comment < ActiveRecord::Base
belongs_to :post, :counter_cache => true
has_many :ratings
+ belongs_to :first_post, :foreign_key => :post_id
+
has_many :children, :class_name => 'Comment', :foreign_key => :parent_id
belongs_to :parent, :class_name => 'Comment', :counter_cache => :children_count
Something went wrong with that request. Please try again.