Turning extension of ActiveRecord in gem

2019-04-02 16:48发布

问题:

This extension create cache_find method for all models of app (I've create this using this post).

config/active_record_extension.rb

require 'active_support/concern'

module ActiveRecordExtension

  extend ActiveSupport::Concern

  # add your instance methods here
  def flush_find
    Rails.cache.delete([self.class.name, :cached_find, id])
  end

  included do
    after_commit :flush_find
  end

  module ClassMethods
    def cached_find id
      Rails.cache.fetch([self.name, :cached_find, id]) { self.find(id) }
    end
  end
end

# include the extension
ActiveRecord::Base.send(:include, ActiveRecordExtension)

I turned this code into a gem and added to this repo.

So I want to add this methods dynamically, something like this:

class User << ActiveRecord::Base
  # id, name, email, age...

  cached :find, :find_by_name, :find_by_email
end

and the above code should generate cached_find, flush_find, cached_find_by_name, flush_find_by_name... You get it.

I need help to:

  1. Test Rails.cache methods in model_caching gem.
  2. Create code to dynamically add methods to app models based on cached method arguments.

Some links that helped me but do not meet all:

https://github.com/radar/guides/blob/master/extending-active-record.md

http://railscasts.com/episodes/245-new-gem-with-bundler

http://guides.rubyonrails.org/plugins.html

Fell free to clone and improve gem code.

回答1:

You don't have to hack ActiveRecord::Base. You can add what Marc-Alexandre said right into your concern, like so:

module ActiveRecordExtension
  extend ActiveSupport::Concern

  ...

  module ClassMethods
    def cached(*args)
      define_method "cached_#{arg.to_s}" do 
        # do whatever you want to do inside cached_xx
      end

      define_method "flush_#{arg.to_s}" do
        # do whatever you want to to inside flush_xx
      end
    end
  end
end

Also, I would not auto include the extension directly in ActiveRecord, I think it's better to explicitly include it in the models you are going to use it.



回答2:

To add code dynamically you need to hack the ActiveRecord::Base class. In another file (you usually put in lib/core_ext) you could do as follow :

ActiveRecord::Base.class_eval do
  def self.cached(*args)
    args.each do |arg|
      define_method "cached_#{arg.to_s}" do 
        # do whatever you want to do inside cached_xx
      end

      define_method "flush_#{arg.to_s}" do
        # do whatever you want to to inside flush_xx
      end
    end
  end
end

What it does basically is takes all your arguments for cached (:find, :find_by_name, etc) and define the two methods (cache_find, cache_find_by_name) and flush_find, .. etc)

Hope this helps !