问题
Is there any clean way to initialize instance variables in a Module intended to be used as Mixin? For example, I have the following:
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
Notice that I have to check again and again if each @member
has been properly initialized in each function -- this is mildly irritating. I would much rather write:
module Example
def initialize
@handlers = {}
@unhandled = []
@all_handlers = []
end
# or
@handlers = {}
@unhandled = []
# ...
end
And not have to repeatedly make sure things are initialized correctly. However, from what I can tell this is not possible. Is there any way around this, besides adding a initialize_me
method to Example
and calling initialize_me
from the extended Class? I did see this example, but there's no way I'm monkey-patching things into Class
just to accomplish this.
回答1:
module Example
def self.included(base)
base.instance_variable_set :@example_ivar, :foo
end
end
Edit: Note that this is setting a class instance variable. Instance variables on the instance can't be created when the module is mixed into the class, since those instances haven't been created yet. You can, though, create an initialize method in the mixin, e.g.:
module Example
def self.included(base)
base.class_exec do
def initialize
@example_ivar = :foo
end
end
end
end
There may be a way to do this while calling the including class's initialize method (anybody?). Not sure. But here's an alternative:
class Foo
include Example
def initialize
@foo = :bar
after_initialize
end
end
module Example
def after_initialize
@example_ivar = :foo
end
end
回答2:
Perhaps this is a little hacky, but you can use prepend
to get the desired behavior:
module Foo
def initialize(*args)
@instance_var = []
super
end
end
class A
prepend Foo
end
Here is the output from the console:
2.1.1 :011 > A.new
=> #<A:0x00000101131788 @instance_var=[]>
回答3:
modules
provides hooks, as Module#included
. I suggest you check out ruby doc on the topic, or use ActiveSupport::Concern
, which provides some helpers on modules.
回答4:
I think there may be a simpler answer to this. The module should have an initializer that initialises the variables as you normally would do. In the initializer for the class that includes the module, invoke super() to invoke the initializer in the included module. This is simply following the method dispatch rules in Ruby.
On reflection, this will not work so well if the class including the module also has a superclass that needs to be initialised. The initializer in the module would need to accept a variable parameter list and pass this up to the superclass. It looks like a good avenue to explore though.
来源:https://stackoverflow.com/questions/12586051/initializing-instance-variables-in-mixins