From 6e56a03f834202af4e57c50a71cc74ea08303bfd Mon Sep 17 00:00:00 2001 From: lulalala Date: Tue, 2 Oct 2012 22:30:06 +0800 Subject: Fix reset_counters() crashing on has_many :through associations. The counter column name in the intermediate model need to be access via the through reflection. --- activerecord/CHANGELOG.md | 5 +++++ activerecord/lib/active_record/counter_cache.rb | 9 +++++++-- activerecord/test/cases/counter_cache_test.rb | 15 ++++++++++++++- activerecord/test/models/subscription.rb | 2 +- activerecord/test/schema/schema.rb | 1 + 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 67bec5f38e..8ff4c4706c 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,5 +1,10 @@ ## Rails 4.0.0 (unreleased) ## +* Fix `reset_counters` crashing on `has_many :through` associations. + Fix #7822. + + *lulalala* + * Support for partial inserts. When inserting new records, only the fields which have been changed diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index c877079b25..d28cd560d9 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -22,8 +22,13 @@ module ActiveRecord counters.each do |association| has_many_association = reflect_on_association(association.to_sym) - foreign_key = has_many_association.foreign_key.to_s - child_class = has_many_association.klass + if has_many_association.is_a? ActiveRecord::Reflection::ThroughReflection + foreign_key = has_many_association.through_reflection.foreign_key.to_s + child_class = has_many_association.through_reflection.klass + else + foreign_key = has_many_association.foreign_key.to_s + child_class = has_many_association.klass + end belongs_to = child_class.reflect_on_all_associations(:belongs_to) reflection = belongs_to.find { |e| e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? } counter_name = reflection.counter_cache_column diff --git a/activerecord/test/cases/counter_cache_test.rb b/activerecord/test/cases/counter_cache_test.rb index ee443741ca..fc46a249c8 100644 --- a/activerecord/test/cases/counter_cache_test.rb +++ b/activerecord/test/cases/counter_cache_test.rb @@ -10,9 +10,12 @@ require 'models/dog' require 'models/dog_lover' require 'models/person' require 'models/friendship' +require 'models/subscriber' +require 'models/subscription' +require 'models/book' class CounterCacheTest < ActiveRecord::TestCase - fixtures :topics, :categories, :categorizations, :cars, :dogs, :dog_lovers, :people, :friendships + fixtures :topics, :categories, :categorizations, :cars, :dogs, :dog_lovers, :people, :friendships, :subscribers, :subscriptions, :books class ::SpecialTopic < ::Topic has_many :special_replies, :foreign_key => 'parent_id' @@ -118,4 +121,14 @@ class CounterCacheTest < ActiveRecord::TestCase Person.reset_counters(michael.id, :followers) end end + + test "reset counter of has_many :through association" do + subscriber = subscribers('second') + Subscriber.reset_counters(subscriber.id, 'books') + Subscriber.increment_counter('books_count', subscriber.id) + + assert_difference 'subscriber.reload.books_count', -1 do + Subscriber.reset_counters(subscriber.id, 'books') + end + end end diff --git a/activerecord/test/models/subscription.rb b/activerecord/test/models/subscription.rb index 4bdb36ea46..bcac4738a3 100644 --- a/activerecord/test/models/subscription.rb +++ b/activerecord/test/models/subscription.rb @@ -1,4 +1,4 @@ class Subscription < ActiveRecord::Base - belongs_to :subscriber + belongs_to :subscriber, :counter_cache => :books_count belongs_to :book end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 798ea20efc..2e4ec96933 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -621,6 +621,7 @@ ActiveRecord::Schema.define do create_table :subscribers, :force => true, :id => false do |t| t.string :nick, :null => false t.string :name + t.column :books_count, :integer, :null => false, :default => 0 end add_index :subscribers, :nick, :unique => true -- cgit v1.2.3