我一直在尝试想出如何扩展模块中initialize
的行为。我希望在被混入的类的initialize
中不调用super的情况下完成。我想支持通常的include
调用模式,但我无法弄清楚。我已经阅读了所有可以找到的关于此问题的资料,虽然有人给出了建议,但它们似乎都无法正常工作(至少在我的手上)。
以下是我所了解的(我想):
- 如果可能的话,必须使用
include
钩子(即Module.included(base)
)来实现。 include
钩子将在包含类定义initialize
之前执行,因此简单地尝试使用base.instance_eval
定义initialize
没有意义,因为它将被覆盖。有人建议利用
method_added
钩子并在其中处理。我正在尝试这样做,但似乎钩子在方法定义的开始处执行,因此你最终得到的是下面看到的内容。
module Mo
def self.included(klass)
klass.instance_eval do
def method_added(method)
puts "Starting creation of #{method} for #{self.name}"
case method
when :initialize
alias_method :original_initialize, :initialize
puts "About to define initialize in Mo"
def initialize
original_initialize
puts "Hello from Mo#initialize"
end
puts "Finished defining initialize in Mo"
end
puts "Finishing creation of #{method} for #{self.name}"
end
end
end
end
class Foo
include Mo
def initialize
puts "Hello from Foo#initialize"
end
end
foo = Foo.new
这会产生以下输出:
Starting creation of initialize for Foo
Starting creation of original_initialize for Foo
Finishing creation of original_initialize for Foo
About to define initialize in Mo
Finished defining initialize in Mo
Finishing creation of initialize for Foo
Hello from Foo#initialize
在我看来,来自Foo类的initialize
仍然覆盖了模块的定义。我猜测这是因为该定义仍然处于打开状态,这表明它不是由最后启动的块获胜,而是由最后结束的块获胜。
如果有人真正知道如何做到这一点并使其正常工作,请启迪我。
顺便说一句,是的,我认为有一个充分的理由想要这样做。
prepend
。我很喜欢它!但我仍然好奇是否有一种方法可以在1.9.*中实现。 - Huliaxklass.send:prepend
而不是简单地使用klass.prepend
,是否有什么明显的遗漏? - NobodysNightmare