diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index b0c3a3179e196d7b0327be45c5f536a5189bef8d..9372adb583d2d0320f6b087c6bb4cc1171703e83 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *2.0.0 [Preview Release]* (September 29th, 2007) [Includes duplicates of changes from 1.14.2 - 1.15.3] +* Make size for has_many :through use counter cache if it exists. Closes #9734 [xaviershay] + * Remove DB2 adapter since IBM chooses to maintain their own adapter instead. [Jeremy Kemper] * Extract Oracle, SQLServer, and Sybase adapters into gems. [Jeremy Kemper] diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index de0d7cc8a9a237f2b798477426e129ae91d64aa2..62839c46378f3076d0b1bc5f8b71b1d5eb8d94bb 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -100,7 +100,9 @@ def create!(attrs = nil) # calling collection.size if it has. If it's more likely than not that the collection does have a size larger than zero # and you need to fetch that collection afterwards, it'll take one less SELECT query if you use length. def size - loaded? ? @target.size : count + return @owner.send(:read_attribute, cached_counter_attribute_name) if has_cached_counter? + return @target.size if loaded? + return count end # Calculate sum using SQL, not Enumerable @@ -258,6 +260,14 @@ def conditions end alias_method :sql_conditions, :conditions + + def has_cached_counter? + @owner.attribute_present?(cached_counter_attribute_name) + end + + def cached_counter_attribute_name + "#{@reflection.name}_count" + end end end end diff --git a/activerecord/test/associations/join_model_test.rb b/activerecord/test/associations/join_model_test.rb index d18c878733cd1eb42c1cf8cbc6c02b1ee981308d..d92a0c760a1783b9cafbe6f0086c051d434c5eda 100644 --- a/activerecord/test/associations/join_model_test.rb +++ b/activerecord/test/associations/join_model_test.rb @@ -457,6 +457,15 @@ def test_has_many_through_collection_size_doesnt_load_target_if_not_loaded assert !author.comments.loaded? end + uses_mocha('has_many_through_collection_size_uses_counter_cache_if_it_exists') do + def test_has_many_through_collection_size_uses_counter_cache_if_it_exists + author = authors(:david) + author.stubs(:read_attribute).with('comments_count').returns(100) + assert_equal 100, author.comments.size + assert !author.comments.loaded? + end + end + def test_adding_junk_to_has_many_through_should_raise_type_mismatch assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:thinking).tags << "Uhh what now?" } end