我动态地将一个模块包含在Baz
方法的foobarbaz
类中。
然而,当我在ruby中执行此操作时,我得到一个nil puts
。模块是否可以访问Foo
的实例变量?
class Foo
attr_accessor :current_session
def initialize(current_session)
@current_session = current_session
end
def foobarbaz
Baz.send(:include, Bar) # For Ruby < 2.1
# Baz.include(Bar) # For Ruby > 2.1
end
end
class Baz
end
module Bar
def foobar
@current_session
# 'foobar'
end
end
puts Foo.new('current_session').foobarbaz.new.foobar # nil
注意,为此,我使用的是Ruby 2.0.0。以下内容在Ruby 2.1.2中也没有puts
期望的结果。
答案 0 :(得分:2)
以下是元编程:
#!/usr/bin/env ruby
class Foo
attr_accessor :current_session
def initialize(current_session)
@current_session = current_session
end
def foobarbaz
session = current_session
Bar.module_eval { @current_session = session }
Baz.send(:include, Bar)
end
end
在mod的上下文中计算字符串或块,除了在给出块时,常量/类变量查找不受影响....
因此在Bar.module_eval { @current_session = session }
内,@current_session
只是Bar
的实例变量,我将它的值设置为类Foo
的实例变量值,是@current_session
。
Baz.send(:include, Bar)
是有帮助的,它返回类/模块本身,包括另一个模块。 include(module, ...) → self
class Baz
end
阅读此post以了解以下内容。
module Bar
class << self
attr_reader :current_session
end
def foobar
Bar.current_session
end
end
puts Foo.new('current_session').foobarbaz.new.foobar
# >> current_session
<强>更新强>
由于@Christian Fazzin给出了一个很好的建议: -
如果您希望Bar
模块也有写入方法,那么您必须进行2次更改 -
Bar
应该包含attr_accesor :current_session
,而不是它现在拥有的内容。module_eval
的强大功能,而是使用写法的语法sugraness ,比如把Bar.current_session = current_session
放在方法{{1}中}。删除行foobarbaz
和session = current_session
。答案 1 :(得分:1)
创建Foo
(foo
之后的实例)之后,将foo
的实例变量@current_session
初始化为'current session'
,它会显示对我来说,你希望foo.foobarbaz
做以下事情:
Baz
到include
模块Bar
Baz
(baz
,比如说)@current_session
创建一个名为baz
的实例变量,并为其指定foo
同名实例变量的值baz.foobar
以返回baz
的实例变量@current_session
的值。 如果我的理解是正确的,我们可以在Foo#foobarbaz
中使用四行执行这四个步骤:
class Baz
end
module Bar
def foobar
@current_session + ' in Baz'
end
end
class Foo
attr_accessor :current_session
def initialize(current_session)
@current_session = current_session
end
def foobarbaz
Baz.include(Bar)
baz = Baz.new
baz.instance_variable_set(:@current_session, self.current_session)
baz.foobar
end
end
foo = Foo.new('current session')
foo.foobarbaz
#=> "current session in Baz"
我稍微修改了foobarbaz
返回的内容以显示其来源。
请注意,foobarbaz
的第三行可以更改为以下任一项
baz.instance_variable_set(:@current_session, @current_session)
baz.instance_variable_set(:@current_session,
instance_variable_get(:@current_session))
如果使用后者,则不需要@current_session
的访问者。
答案 2 :(得分:0)
您只需要设置类@current_session
的实例变量(不是类实例变量!)Baz
。
对代码进行最轻微的修改而不需要额外的类/模块方法,最直接的方法是定义设置所需变量的初始化方法:
class Foo
attr_accessor :current_session
def initialize(current_session)
@current_session = current_session
end
def foobarbaz
# define Baz#initialize on-the-fly, alternatively with define_method
Baz.class_eval "def initialize; @current_session = '#{@current_session}';end"
Baz.send(:include, Bar) # For Ruby < 2.1
end
end
class Baz
end
module Bar
def foobar
@current_session
# 'foobar'
end
end
puts Foo.new('current_session').foobarbaz.new.foobar
# current_session