Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Override method_missing for class and instance methods?

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....

like image 888
Peter Bratton Avatar asked Apr 03 '14 19:04

Peter Bratton


1 Answers

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
like image 160
samuil Avatar answered Sep 29 '22 01:09

samuil