Skip to content
Permalink
Browse files

Fix bug where reset_counters resets the wrong counter cache.

If a model belongs_to two associations with the same class, then reset_counters
will reset the wrong counter cache.

Finding the right reflection should use the foreign_key instead, which should
be unique.
  • Loading branch information...
sarenji authored and David Peter committed Jan 17, 2012
1 parent e9474c8 commit 27d327e63a3ac661cd22b207d9022f8661163853
@@ -25,9 +25,10 @@ def reset_counters(id, *counters)
self.name
end

foreign_key = has_many_association.foreign_key.to_s
child_class = has_many_association.klass
belongs_to = child_class.reflect_on_all_associations(:belongs_to)
reflection = belongs_to.find { |e| e.class_name == expected_name }
reflection = belongs_to.find { |e| e.foreign_key.to_s == foreign_key }
counter_name = reflection.counter_cache_column

stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
@@ -6,9 +6,11 @@
require 'models/reply'
require 'models/category'
require 'models/categorization'
require 'models/dog'
require 'models/dog_lover'

class CounterCacheTest < ActiveRecord::TestCase
fixtures :topics, :categories, :categorizations, :cars
fixtures :topics, :categories, :categorizations, :cars, :dogs, :dog_lovers

class ::SpecialTopic < ::Topic
has_many :special_replies, :foreign_key => 'parent_id'
@@ -61,7 +63,7 @@ class ::SpecialReply < ::Reply
end
end

test "reset counter should with belongs_to which has class_name" do
test "reset counter with belongs_to which has class_name" do
car = cars(:honda)
assert_nothing_raised do
Car.reset_counters(car.id, :engines)
@@ -71,6 +73,20 @@ class ::SpecialReply < ::Reply
end
end

test "reset the right counter if two have the same class_name" do
david = dog_lovers(:david)

DogLover.increment_counter(:bred_dogs_count, david.id)
DogLover.increment_counter(:trained_dogs_count, david.id)

assert_difference 'david.reload.bred_dogs_count', -1 do
DogLover.reset_counters(david.id, :bred_dogs)
end
assert_difference 'david.reload.trained_dogs_count', -1 do
DogLover.reset_counters(david.id, :trained_dogs)
end
end

test "update counter with initial null value" do
category = categories(:general)
assert_equal 2, category.categorizations.count
@@ -0,0 +1,4 @@
david:
id: 1
bred_dogs_count: 0
trained_dogs_count: 1
@@ -0,0 +1,3 @@
sophie:
id: 1
trainer_id: 1
@@ -0,0 +1,4 @@
class Dog < ActiveRecord::Base
belongs_to :breeder, :class_name => "DogLover", :counter_cache => :bred_dogs_count
belongs_to :trainer, :class_name => "DogLover", :counter_cache => :trained_dogs_count
end
@@ -0,0 +1,4 @@
class DogLover < ActiveRecord::Base
has_many :trained_dogs, :class_name => "Dog", :foreign_key => :trainer_id
has_many :bred_dogs, :class_name => "Dog", :foreign_key => :breeder_id
end
@@ -209,6 +209,16 @@ def create_table(*args, &block)
t.integer :access_level, :default => 1
end

create_table :dog_lovers, :force => true do |t|
t.integer :trained_dogs_count, :default => 0
t.integer :bred_dogs_count, :default => 0
end

create_table :dogs, :force => true do |t|
t.integer :trainer_id
t.integer :breeder_id
end

create_table :edges, :force => true, :id => false do |t|
t.column :source_id, :integer, :null => false
t.column :sink_id, :integer, :null => false

0 comments on commit 27d327e

Please sign in to comment.
You can’t perform that action at this time.