Using Rails 3.1.3 and I\'m trying to figure out why our counter caches aren\'t being updated correctly when changing the parent record id via update_attributes.
I recently came across this same problem (Rails 3.2.3). Looks like it has yet to be fixed, so I had to go ahead and make a fix. Below is how I amended ActiveRecord::Base and utilize after_update callback to keep my counter_caches in sync.
Extend ActiveRecord::Base
Create a new file lib/fix_counters_update.rb
with the following:
module FixUpdateCounters
def fix_updated_counters
self.changes.each {|key, value|
# key should match /master_files_id/ or /bibls_id/
# value should be an array ['old value', 'new value']
if key =~ /_id/
changed_class = key.sub(/_id/, '')
changed_class.camelcase.constantize.decrement_counter(:"#{self.class.name.underscore.pluralize}_count", value[0]) unless value[0] == nil
changed_class.camelcase.constantize.increment_counter(:"#{self.class.name.underscore.pluralize}_count", value[1]) unless value[1] == nil
end
}
end
end
ActiveRecord::Base.send(:include, FixUpdateCounters)
The above code uses the ActiveModel::Dirty method changes
which returns a hash containing the attribute changed and an array of both the old value and new value. By testing the attribute to see if it is a relationship (i.e. ends with /_id/), you can conditionally determine whether decrement_counter
and/or increment_counter
need be run. It is essnetial to test for the presence of nil
in the array, otherwise errors will result.
Add to Initializers
Create a new file config/initializers/active_record_extensions.rb
with the following:
require 'fix_update_counters'
Add to models
For each model you want the counter caches updated add the callback:
class Comment < ActiveRecord::Base
after_update :fix_updated_counters
....
end