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

后端 未结 3 643
终归单人心
终归单人心 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:50

    This looks like a good case for ActiveSupport::Concern. You can tweak your cachebacks method slightly to add it as a class method on the including class:

    module Cacheable
      extend ActiveSupport::Concern
    
      module ClassMethods
        def cachebacks(&block)
          klass = self
          [:after_save, :after_destroy].each do |callback|
            self.send(callback, proc { cache!(CACHEABLE[klass], *klass.instance_eval(&block)) })
          end
        end
      end
    
      def cache!(fields, *objects)
        # ...
      end
    
      # ...
    end
    

    To use it:

    class Example < ActiveRecord::Base
      include Cacheable
      cachebacks { all }
    end
    

    The block you pass to cachebacks will be executed in the context of the class that's calling it. In this example, { all } is equivalent to calling Example.all and passing the results into your cache! method.


    To answer your question in the comments, Concern encapsulates a common pattern and establishes a convention in Rails. The syntax is slightly more elegant:

    included do
      # behaviors
    end
    
    # instead of
    
    def self.included(base)
      base.class_eval do
        # behaviors
      end
    end
    

    It also takes advantage of another convention to automatically and correctly include class and instance methods. If you namespace those methods in modules named ClassMethods and InstanceMethods (although as you've seen, InstanceMethods is optional), then you're done.

    Last of all, it handles module dependencies. The documentation gives a good example of this, but in essence, it prevents the including class from having to explicitly include dependent modules in addition to the module it's actually interested in.

提交回复
热议问题