How do I use Ruby's define_method to create class methods?

January 13, 2013 Link to post  Permalink

I’m building some code that may become a Gem that I release soon. I’ve extracted my code to a ActiveSupport::Concern, but I need to define a combination of instance methods and static methods (or class methods) on the parent object. This is complicated because I need to be able to define the names of these methods depending on a parameter, and I want to have multiple instances of this code on any particular model. This last requirement precludes me using the easy example code that is all over Google search. Here’s the equivalent code before I extracted to a ActiveSupport::Concern module

class Foo < ActiveRecord::Base

  after_commit :clear_id_cache

  # class method
  def self.lookup_by_id(id)
    Rails.cache.fetch("Foo:id:#{id}") do
      where(:id => id).first
    end
  end
  
  # instance method
  def clear_id_cache
    Rails.cache.delete "Foo:id:#{id}"
  end
end

My Concern module looks like this

require 'active_support/core_ext'

module CacheLookup 
  extend ActiveSupport::Concern 

  module ClassMethods 
    def cache_lookup(attribute) 

       # a_foo.clear_id_cache 
       define_method("clear_#{attribute}_cache") do 
         Rails.cache.delete "#{self.class.name}:#{attribute}:" + send("#{attribute}").to_s
       end 

       # ActiveRecord callbacks on Save and Destroy to clear the cache 
      after_commit "clear_#{attribute}_cache"
      after_destroy "clear_#{attribute}_cache"

      # Foo.lookup_by_id
      define_singleton_method("lookup_by_#{attribute}") do |param| 
        Rails.cache.fetch("#{name}:#{attribute}:" + param.to_s) do 
          where(attribute => param).first 
        end 
      end
    end
  end
end

The magic here is the define_singleton_method that is new in Ruby 1.9.1 - it works the same as define_method, but for class methods instead of instance methods.

It took me 3 hours to find this on the Googles, hopefully it will be easier to find next time