`method_missing`不能与`prepend`一起使用

时间:2015-09-18 22:13:04

标签: ruby metaprogramming

我正在尝试将Module#prepend用于某些钩子:

module B
  def method_missing(method, *args, &block)
    p 'B method_missing'
    #if condition
    #  do_something
    #else
      super method, *args, &block #I need to call method from A
    #end
  end
end

class A
  prepend B

  def hello
    p 'A hello'
  end
end

A.new.hello #=> "A hello"

但我想要的是:

#=> "B method_missing"
#=> "A hello"

但是如果我调用一个不存在的方法:

A.new.baybay #=> "B method_missing" 
#and error `method_missing': undefined method `baybay' for #<A:0x0000000182c430> (NoMethodError)

关于祖先:

A.ancestors #=> [B, A, Object, Kernel, BasicObject]
A.new.singleton_class.ancestors #=> [#<Class:#<A:0x000000027b6900>>, B, A, Object, Kernel, BasicObject]

但是如果我创建一个具有相同名称的方法:

module B
  def hello
    p 'B hello'
  end
end

A.new.hello #=> "B hello"

为什么不与method_missing合作?我怎样才能得到我想要的东西?任何解决方案?

1 个答案:

答案 0 :(得分:1)

原因是在方法调用时,ruby将遍历祖先链,搜索具有所述名称的方法。 如果找不到此类方法,则会为method_missing执行第二次遍历。

<小时/> 一种可能的解决方案是在method_missing中编写A

module B
  def method_missing(method, *args, &block)
    p 'B method_missing'
    super method, *args, &block
  end
end

class A
  prepend B

  def method_missing(method, *args, &block)
    p 'A hello' if method == :hello
  end
end

A.new.hello # => "B method_missing"
            # => "A hello"

<小时/> 另一种不需要您更改A类的方法是使用Module#prepended挂钩。然后,您可以检查班级A中的所有方法并使用Module#define_method

然而,这将是不必要的复杂,我认为这样做没有多大价值,除非你正在编写某种框架。为什么不把这个包裹起来呢?