mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
1152219fa2
When we made sure that the counter gets updated in memory, we only did it on the has many side. The has many side only does the update if the belongs to cannot. The belongs to side was updated to update the counter cache (if it is able). This means that we need to check if the belongs_to is able to update in memory on the has_many side. We also found an inconsistency where the reflection names were used to grab the association which should update the counter cache. Since reflection names are now strings, this means it was using a different instance than the one which would have the inverse instance set. Fixes #18689 [Sean Griffin & anthonynavarre]
201 lines
6.2 KiB
Ruby
201 lines
6.2 KiB
Ruby
require 'cases/helper'
|
|
require 'models/topic'
|
|
require 'models/car'
|
|
require 'models/wheel'
|
|
require 'models/engine'
|
|
require 'models/reply'
|
|
require 'models/category'
|
|
require 'models/categorization'
|
|
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, :subscribers, :subscriptions, :books
|
|
|
|
class ::SpecialTopic < ::Topic
|
|
has_many :special_replies, :foreign_key => 'parent_id'
|
|
has_many :lightweight_special_replies, -> { select('topics.id, topics.title') }, :foreign_key => 'parent_id', :class_name => 'SpecialReply'
|
|
end
|
|
|
|
class ::SpecialReply < ::Reply
|
|
belongs_to :special_topic, :foreign_key => 'parent_id', :counter_cache => 'replies_count'
|
|
end
|
|
|
|
setup do
|
|
@topic = Topic.find(1)
|
|
end
|
|
|
|
test "increment counter" do
|
|
assert_difference '@topic.reload.replies_count' do
|
|
Topic.increment_counter(:replies_count, @topic.id)
|
|
end
|
|
end
|
|
|
|
test "decrement counter" do
|
|
assert_difference '@topic.reload.replies_count', -1 do
|
|
Topic.decrement_counter(:replies_count, @topic.id)
|
|
end
|
|
end
|
|
|
|
test "reset counters" do
|
|
# throw the count off by 1
|
|
Topic.increment_counter(:replies_count, @topic.id)
|
|
|
|
# check that it gets reset
|
|
assert_difference '@topic.reload.replies_count', -1 do
|
|
Topic.reset_counters(@topic.id, :replies)
|
|
end
|
|
end
|
|
|
|
test "reset counters by counter name" do
|
|
# throw the count off by 1
|
|
Topic.increment_counter(:replies_count, @topic.id)
|
|
|
|
# check that it gets reset
|
|
assert_difference '@topic.reload.replies_count', -1 do
|
|
Topic.reset_counters(@topic.id, :replies_count)
|
|
end
|
|
end
|
|
|
|
test 'reset multiple counters' do
|
|
Topic.update_counters @topic.id, replies_count: 1, unique_replies_count: 1
|
|
assert_difference ['@topic.reload.replies_count', '@topic.reload.unique_replies_count'], -1 do
|
|
Topic.reset_counters(@topic.id, :replies, :unique_replies)
|
|
end
|
|
end
|
|
|
|
test "reset counters with string argument" do
|
|
Topic.increment_counter('replies_count', @topic.id)
|
|
|
|
assert_difference '@topic.reload.replies_count', -1 do
|
|
Topic.reset_counters(@topic.id, 'replies')
|
|
end
|
|
end
|
|
|
|
test "reset counters with modularized and camelized classnames" do
|
|
special = SpecialTopic.create!(:title => 'Special')
|
|
SpecialTopic.increment_counter(:replies_count, special.id)
|
|
|
|
assert_difference 'special.reload.replies_count', -1 do
|
|
SpecialTopic.reset_counters(special.id, :special_replies)
|
|
end
|
|
end
|
|
|
|
test "reset counter with belongs_to which has class_name" do
|
|
car = cars(:honda)
|
|
assert_nothing_raised do
|
|
Car.reset_counters(car.id, :engines)
|
|
end
|
|
assert_nothing_raised do
|
|
Car.reset_counters(car.id, :wheels)
|
|
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
|
|
assert_nil category.categorizations_count
|
|
|
|
Category.update_counters(category.id, :categorizations_count => category.categorizations.count)
|
|
assert_equal 2, category.reload.categorizations_count
|
|
end
|
|
|
|
test "update counter for decrement" do
|
|
assert_difference '@topic.reload.replies_count', -3 do
|
|
Topic.update_counters(@topic.id, :replies_count => -3)
|
|
end
|
|
end
|
|
|
|
test "update counters of multiple records" do
|
|
t1, t2 = topics(:first, :second)
|
|
|
|
assert_difference ['t1.reload.replies_count', 't2.reload.replies_count'], 2 do
|
|
Topic.update_counters([t1.id, t2.id], :replies_count => 2)
|
|
end
|
|
end
|
|
|
|
test 'update multiple counters' do
|
|
assert_difference ['@topic.reload.replies_count', '@topic.reload.unique_replies_count'], 2 do
|
|
Topic.update_counters @topic.id, replies_count: 2, unique_replies_count: 2
|
|
end
|
|
end
|
|
|
|
test "update other counters on parent destroy" do
|
|
david, joanna = dog_lovers(:david, :joanna)
|
|
joanna = joanna # squelch a warning
|
|
|
|
assert_difference 'joanna.reload.dogs_count', -1 do
|
|
david.destroy
|
|
end
|
|
end
|
|
|
|
test "reset the right counter if two have the same foreign key" do
|
|
michael = people(:michael)
|
|
assert_nothing_raised(ActiveRecord::StatementInvalid) do
|
|
Person.reset_counters(michael.id, :friends_too)
|
|
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
|
|
|
|
test "the passed symbol needs to be an association name or counter name" do
|
|
e = assert_raises(ArgumentError) do
|
|
Topic.reset_counters(@topic.id, :undefined_count)
|
|
end
|
|
assert_equal "'Topic' has no association called 'undefined_count'", e.message
|
|
end
|
|
|
|
test "reset counter works with select declared on association" do
|
|
special = SpecialTopic.create!(:title => 'Special')
|
|
SpecialTopic.increment_counter(:replies_count, special.id)
|
|
|
|
assert_difference 'special.reload.replies_count', -1 do
|
|
SpecialTopic.reset_counters(special.id, :lightweight_special_replies)
|
|
end
|
|
end
|
|
|
|
test "counters are updated both in memory and in the database on create" do
|
|
car = Car.new(engines_count: 0)
|
|
car.engines = [Engine.new, Engine.new]
|
|
car.save!
|
|
|
|
assert_equal 2, car.engines_count
|
|
assert_equal 2, car.reload.engines_count
|
|
end
|
|
|
|
test "counter caches are updated in memory when the default value is nil" do
|
|
car = Car.new(engines_count: nil)
|
|
car.engines = [Engine.new, Engine.new]
|
|
car.save!
|
|
|
|
assert_equal 2, car.engines_count
|
|
assert_equal 2, car.reload.engines_count
|
|
end
|
|
end
|