I have been trying to figure out how to extend the behavior of initialize
from a module. I want to do it without calling super in initialize
of the class that is being mixed into. I want to support the normal pattern of calling include
I can't figure it out. I've read everything I can find on the matter and, while people to have suggestions, none of them actually seem to work (in my hands at least).
我一直试图弄清楚如何扩展模块初始化的行为。我想这样做,而不是在正在混合的类的初始化中调用super。我想支持正常的调用模式包括我无法弄明白。我已经阅读了关于这个问题我能找到的所有内容,虽然有人提出建议,但实际上它们似乎都没有(至少在我手中)。
Here is what I (think) I know:
这是我(我想)我知道的:
- If it can be done at all, it has to be done using the hook on
include
(i.e.Module.included(base)
). - 如果它可以完成,则必须使用include on hook(即Module.included(base))来完成。
- The
include
hook will execute before the including class definesinitialize
so there is no point to simply trying to defineinitialize
withbase.instance_eval
because it will get overwritten. - include钩子将在include类定义initialize之前执行,因此没有必要简单地尝试使用base.instance_eval定义initialize,因为它会被覆盖。
-
A suggestion was made to make use of the
method_added
hook and deal with it in there. That is what I'm trying now but it appears that the hook executes at the beginning of method definition so you end up with what you see below.建议使用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
This results in the following output:
这导致以下输出:
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
It looks to me like initialize
from class Foo is still overwriting the definition from the module. I'm guessing that this is because the definition is still open, suggesting that it isn't a matter of which block is started last be which is finished last that "wins".
在我看来,从类Foo初始化仍然会覆盖模块中的定义。我猜这是因为定义仍然是开放的,这表明不是最后一个块启动的问题,最后是“胜利”。
If anyone out there really knows how to do this and have it work please enlighten me.
如果有人真的知道如何做到这一点并让它工作,请启发我。
FWIW, yes, I think I have a good reason for wanting to do this.
FWIW,是的,我认为我有充分的理由想要这样做。
3 个解决方案
#1
26
If you're on Ruby 2.0 or later, you can just use prepend
. Either require the user to prepend
rather than include
, or do:
如果您使用的是Ruby 2.0或更高版本,则可以使用prepend。要么用户要先添加而不是包含,要么执行:
module Mo
module Initializer
def initialize
puts "Hello from Mo#initialize"
super
end
end
def self.included(klass)
klass.send :prepend, Initializer
end
end
#2
6
Ok, well in Ruby 1.9 you could add functionality to the new
class method...
好的,在Ruby 1.9中你可以为新的类方法添加功能......
module Mo
def new(*var)
additional_initialize(*var)
super(*var)
end
def additional_initialize(*var)
puts "Hello from Mo"
end
end
class Foo
extend Mo
def initialize
puts "Hello from Foo"
end
end
foo = Foo.new
That returns...
那回来......
Hello from Mo
Hello from Foo
#3
0
Would it satisfy to have a conditional call in Foo's initialize that only calls an included method if it's present?
在Foo的初始化中有一个条件调用是否满足,如果它存在,只调用一个包含的方法?
module Mo
def initialize_extra
puts "Hello from Mo"
end
end
class Foo
include Mo
def initialize
if defined? initialize_extra
initialize_extra
else
puts "Hello from Foo"
end
end
end
x = Foo.new
#1
26
If you're on Ruby 2.0 or later, you can just use prepend
. Either require the user to prepend
rather than include
, or do:
如果您使用的是Ruby 2.0或更高版本,则可以使用prepend。要么用户要先添加而不是包含,要么执行:
module Mo
module Initializer
def initialize
puts "Hello from Mo#initialize"
super
end
end
def self.included(klass)
klass.send :prepend, Initializer
end
end
#2
6
Ok, well in Ruby 1.9 you could add functionality to the new
class method...
好的,在Ruby 1.9中你可以为新的类方法添加功能......
module Mo
def new(*var)
additional_initialize(*var)
super(*var)
end
def additional_initialize(*var)
puts "Hello from Mo"
end
end
class Foo
extend Mo
def initialize
puts "Hello from Foo"
end
end
foo = Foo.new
That returns...
那回来......
Hello from Mo
Hello from Foo
#3
0
Would it satisfy to have a conditional call in Foo's initialize that only calls an included method if it's present?
在Foo的初始化中有一个条件调用是否满足,如果它存在,只调用一个包含的方法?
module Mo
def initialize_extra
puts "Hello from Mo"
end
end
class Foo
include Mo
def initialize
if defined? initialize_extra
initialize_extra
else
puts "Hello from Foo"
end
end
end
x = Foo.new