How do I use Ruby metaprogramming to add callbacks to a Rails model?

后端 未结 3 650
终归单人心
终归单人心 2020-12-30 11:12

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<

3条回答
  •  南笙
    南笙 (楼主)
    2020-12-30 11:42

    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
    

提交回复
热议问题