是否有任何干净的方法来初始化要用作Mixin的模块中的实例变量?例如,我有以下内容:
module Example
def on(...)
@handlers ||= {}
# do something with @handlers
end
def all(...)
@all_handlers ||= []
# do something with @all_handlers
end
def unhandled(...)
@unhandled ||= []
# do something with unhandled
end
def do_something(..)
@handlers ||= {}
@unhandled ||= []
@all_handlers ||= []
# potentially do something with any of the 3 above
end
end
请注意,我必须一次又一次地检查每个@member
是否已在每个功能中正确初始化 - 这有点令人恼火。我宁愿写:
module Example
def initialize
@handlers = {}
@unhandled = []
@all_handlers = []
end
# or
@handlers = {}
@unhandled = []
# ...
end
并且不必反复确保正确初始化事物。但是,据我所知,这是不可能的。除了向initialize_me
添加Example
方法并从扩展类调用initialize_me
之外,还有什么方法吗?我确实看到了this example,但是为了实现这一点,我无法将事情修改为Class
。
答案 0 :(得分:14)
module Example
def self.included(base)
base.instance_variable_set :@example_ivar, :foo
end
end
编辑:请注意,这是设置类实例变量。当模块混合到类中时,无法创建实例上的实例变量,因为尚未创建这些实例。但是,您可以在mixin中创建一个初始化方法,例如:
module Example
def self.included(base)
base.class_exec do
def initialize
@example_ivar = :foo
end
end
end
end
在调用包含类的初始化方法(任何人?)时,可能有一种方法可以做到这一点。不确定。但这是另一种选择:
class Foo
include Example
def initialize
@foo = :bar
after_initialize
end
end
module Example
def after_initialize
@example_ivar = :foo
end
end
答案 1 :(得分:2)
modules
提供了挂钩,Module#included
。我建议你查看关于这个主题的ruby doc,或者使用ActiveSupport::Concern
,它提供了一些关于模块的帮助。
答案 2 :(得分:1)
也许这有点hacky,但您可以使用prepend
来获得所需的行为:
module Foo
def initialize(*args)
@instance_var = []
super
end
end
class A
prepend Foo
end
以下是控制台的输出:
2.1.1 :011 > A.new
=> #<A:0x00000101131788 @instance_var=[]>
答案 3 :(得分:0)
我认为可能有一个更简单的答案。该模块应该有一个初始化程序,可以像往常一样初始化变量。在包含模块的类的初始化程序中,调用super()以在包含的模块中调用初始化程序。这只是遵循Ruby中的方法调度规则。
反思,如果包含模块的类也有一个需要初始化的超类,这将不会很好。模块中的初始化程序需要接受变量参数列表并将其传递给超类。看起来这是一个很好的探索途径。