I'm trying to write a general purpose module to apply the method_missing pattern for dynamic method creation to some of my Rails models. These models have both class methods and instance methods. While I can write a module fairly straightforwardly for either the class case:
  module ClassVersion
    extend ActiveSupport::Concern
    module ClassMethods
      def method_missing(meth, *args, &block)
        if meth.to_s =~ /^(.+)_async$/
          Async::handle_async self, $1, *args, &block
        else
          super meth, *args, &block
        end
      end
      # Logic for this method MUST match that of the detection in method_missing
      def respond_to_missing?(method_name, include_private = false)
        Async::async?(method_name) || super
      end
    end
  end
or the instance case:
  module InstanceVersion
    extend ActiveSupport::Concern
    def method_missing(meth, *args, &block)
      if meth.to_s =~ /^(.+)_async$/
        Async::handle_async self, $1, *args, &block
      else
        super meth, *args, &block
      end
    end
    # Logic for this method MUST match that of the detection in method_missing
    def respond_to_missing?(method_name, include_private = false)
      Async::async?(method_name) || super
    end
  end
... I can't seem to support both cases in the same class. Is there a better way to override method_missing such that both cases are supported? I'm on Rails 3.2....
What you are trying to achieve is pretty straightforward, and unusual at the same time. ActiveSupport::Concern is giving you possibility of defining nested ClassMethods module that is extending base class on included module hook. Usually this is handy, because you don't want to augment both class and its instances with the same methods.
What you need to do is stop using ActiveSupport::Concern and write included hook to meet your particular needs:
module AsyncModule
  def method_missing(meth, *args, &block)
    if meth.to_s =~ /^(.+)_async$/
      Async::handle_async self, $1, *args, &block
    else
      super meth, *args, &block
    end
  end
  # Logic for this method MUST match that of the detection in method_missing
  def respond_to_missing?(method_name, include_private = false)
    Async::async?(method_name) || super
  end
  private
  def self.included(base)
    base.extend self
  end
end
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With