Skip to content

Commit 77e780e

Browse files
committed
Keep the :fallback null object out of counter cache callbacks
The counter cache after_create/after_update/before_destroy callbacks read the parent with __send__(name) to adjust its counter. With a :fallback that returned the null object when no parent was set, and the callbacks then indexed into it (record[cache_column]), raising NoMethodError on create, update, or destroy. Read through without_autobuild so the callbacks operate on the real parent and no-op when there is none, matching how the cascade helpers in Depending read associations.
1 parent d775da3 commit 77e780e

2 files changed

Lines changed: 26 additions & 3 deletions

File tree

lib/mongoid/association/referenced/counter_cache.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def self.define_callbacks!(association)
110110
end
111111
end
112112

113-
if (record = __send__(name)) && !current.nil?
113+
if (record = without_autobuild { __send__(name) }) && !current.nil?
114114
record[cache_column] = (record[cache_column] || 0) + 1
115115
record.class.with(record.persistence_context) do |_class|
116116
_class.increment_counter(cache_column, current) if record.persisted?
@@ -120,7 +120,7 @@ def self.define_callbacks!(association)
120120
end
121121

122122
klass.after_create do
123-
if record = __send__(name)
123+
if record = without_autobuild { __send__(name) }
124124
record[cache_column] = (record[cache_column] || 0) + 1
125125

126126
if record.persisted?
@@ -133,7 +133,7 @@ def self.define_callbacks!(association)
133133
end
134134

135135
klass.before_destroy do
136-
if record = __send__(name)
136+
if record = without_autobuild { __send__(name) }
137137
record[cache_column] = (record[cache_column] || 0) - 1 unless record.frozen?
138138

139139
if record.persisted?

spec/mongoid/association/fallback_spec.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,29 @@ class Symphony
378378
end
379379
end
380380

381+
context 'with a counter cache' do
382+
before(:all) do
383+
class Composer
384+
include Mongoid::Document
385+
end
386+
387+
class Symphony
388+
include Mongoid::Document
389+
390+
belongs_to :composer, counter_cache: true, fallback: -> { Anonymous.new }
391+
end
392+
end
393+
394+
after(:all) do
395+
Object.send(:remove_const, :Symphony)
396+
Object.send(:remove_const, :Composer)
397+
end
398+
399+
it 'does not update the counter on the fallback' do
400+
expect { Symphony.create! }.not_to raise_error
401+
end
402+
end
403+
381404
context 'validation of the :fallback option' do
382405
it 'rejects a declaration that combines :fallback with :autobuild' do
383406
expect do

0 commit comments

Comments
 (0)