为类和实例方法覆盖 method_missing 方法?

6

我正在尝试编写一个通用模块,将method_missing模式应用于一些Rails模型的动态方法创建。这些模型既有类方法也有实例方法。虽然我可以相当简单地为类情况编写一个模块:

  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

例如这个案例:
  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

我似乎无法在同一个类中同时支持这两种情况。有没有更好的方法来重写method_missing以支持这两种情况?我使用的是Rails 3.2。

2个回答

8
你想要实现的目标非常简单,但同时也很不寻常。 ActiveSupport::Concern 可以让你定义嵌套的 ClassMethods 模块,该模块会在 included 模块钩子上扩展基类。通常这很方便,因为你不想使用相同的方法增强类和它的实例。

你需要做的是停止使用 ActiveSupport::Concern 并编写 included 钩子,以满足你的特定需求:

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

简单而优雅。谢谢! - Peter Bratton
看起来关键是:如果通过模块向类添加 method_missing,则还必须提供模块的 respond_to_missing?,否则 method_missing 就不会生效。 - Jeff Gran

0

你尝试过以下方法吗?

  module ClassAndInstanceVersion
    extend ActiveSupport::Concern

    def method_missing(meth, *args, &block)
      self.class.method_missing(meth, *args, &block)
    end

    def respond_to_missing?(method_name, include_private = false)
      self.class.respond_to_missing?(method_name, include_private)
    end

    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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接