I wrote a simple Cacheable module that makes it simple to cache aggregate fields in a parent model. The module requires that the parent object implement the cacheable<
Thanks to Brandon for the answer that helped me write the solution.
Add the following to your model. You can cacheback multiple parent relationships per model. You can also specify different attribute names for the parent and child tables by passing in a hash instead of a string for a particular field.
include Cacheable
cacheback(parent: :quotes, fields: %w(weight pallet_spots value equipment_type_id))
This module extends ActiveSupport::Concern and adds the callbacks and performs the cacheing. Your parent classes will need to implement calc_field methods to do the caching work.
module Cacheable
extend ActiveSupport::Concern
module ClassMethods
def cacheback(options)
fields = Cacheable.normalize_fields(options[:fields])
[:after_save, :after_destroy].each do |callback|
self.send(callback, proc { cache!(fields, self.send(options[:parent])) })
end
end
end
def cache!(fields, objects)
objects = objects.respond_to?(:to_a) ? objects.to_a : [objects]
objects.each do |object|
if object.cacheable?
calc(fields, objects)
save!(objects)
end
end
end
def calc(fields, objects)
fields.each do |parent_field, child_field|
objects.each(&:"calc_#{parent_field}") if self.send("#{child_field}_changed?".to_sym)
end
end
def save!(objects)
objects.each { |object| object.save! if object.changed? }
end
def self.normalize_fields(fields)
Hash[fields.collect { |f| f.is_a?(Hash) ? f.to_a : [f, f] }]
end
end