1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Refactor counter cache to extract decrement_counters_before_last_save on the belongs_to association

This commit is contained in:
Ryuta Kamizono 2018-09-20 16:02:12 +09:00
parent 32da8c107a
commit 688c27c894
2 changed files with 31 additions and 48 deletions

View file

@ -34,14 +34,28 @@ module ActiveRecord
@updated
end
def decrement_counters # :nodoc:
def decrement_counters
update_counters(-1)
end
def increment_counters # :nodoc:
def increment_counters
update_counters(1)
end
def decrement_counters_before_last_save
if reflection.polymorphic?
model_was = owner.attribute_before_last_save(reflection.foreign_type).try(:constantize)
else
model_was = klass
end
foreign_key_was = owner.attribute_before_last_save(reflection.foreign_key)
if foreign_key_was && model_was < ActiveRecord::Base
update_counters_via_scope(model_was, foreign_key_was, -1)
end
end
def target_changed?
owner.saved_change_to_attribute?(reflection.foreign_key)
end
@ -64,11 +78,16 @@ module ActiveRecord
if target && !stale_target?
target.increment!(reflection.counter_cache_column, by, touch: reflection.options[:touch])
else
counter_cache_target.update_counters(reflection.counter_cache_column => by, touch: reflection.options[:touch])
update_counters_via_scope(klass, owner._read_attribute(reflection.foreign_key), by)
end
end
end
def update_counters_via_scope(klass, foreign_key, by)
scope = klass.unscoped.where!(primary_key(klass) => foreign_key)
scope.update_counters(reflection.counter_cache_column => by, touch: reflection.options[:touch])
end
def find_target?
!loaded? && foreign_key_present? && klass
end
@ -78,11 +97,11 @@ module ActiveRecord
end
def replace_keys(record)
owner[reflection.foreign_key] = record ? record._read_attribute(primary_key(record)) : nil
owner[reflection.foreign_key] = record ? record._read_attribute(primary_key(record.class)) : nil
end
def primary_key(record)
reflection.association_primary_key(record.class)
def primary_key(klass)
reflection.association_primary_key(klass)
end
def foreign_key_present?
@ -96,11 +115,6 @@ module ActiveRecord
inverse && inverse.has_one?
end
def counter_cache_target
primary_key = reflection.association_primary_key(klass)
klass.unscoped.where!(primary_key => owner._read_attribute(reflection.foreign_key))
end
def stale_state
result = owner._read_attribute(reflection.foreign_key) { |n| owner.send(:missing_attribute, n, caller) }
result && result.to_s

View file

@ -21,47 +21,16 @@ module ActiveRecord::Associations::Builder # :nodoc:
add_default_callbacks(model, reflection) if reflection.options[:default]
end
def self.define_accessors(mixin, reflection)
super
add_counter_cache_methods mixin
end
def self.add_counter_cache_methods(mixin)
return if mixin.method_defined? :belongs_to_counter_cache_after_update
mixin.class_eval do
def belongs_to_counter_cache_after_update(reflection)
if association(reflection.name).target_changed?
if reflection.polymorphic?
model_was = attribute_before_last_save(reflection.foreign_type).try(:constantize)
else
model_was = reflection.klass
end
foreign_key_was = attribute_before_last_save(reflection.foreign_key)
cache_column = reflection.counter_cache_column
association(reflection.name).increment_counters
if foreign_key_was && model_was < ActiveRecord::Base
counter_cache_target(reflection, model_was, foreign_key_was).update_counters(cache_column => -1)
end
end
end
private
def counter_cache_target(reflection, model, foreign_key)
primary_key = reflection.association_primary_key(model)
model.unscoped.where!(primary_key => foreign_key)
end
end
end
def self.add_counter_cache_callbacks(model, reflection)
cache_column = reflection.counter_cache_column
model.after_update lambda { |record|
record.belongs_to_counter_cache_after_update(reflection)
association = association(reflection.name)
if association.target_changed?
association.increment_counters
association.decrement_counters_before_last_save
end
}
klass = reflection.class_name.safe_constantize