如何从动态包含的模块中访问实例变量?

时间:2022-10-30 09:04:58

I am dynamically including a module into the Baz class in the foobarbaz method.

我在foobarbaz方法中动态地将一个模块包含到Baz类中。

However, when I execute this in ruby, I get a nil puts. Doesn't the module have access to Foo's instance variables?

但是,当我在ruby中执行此操作时,我会得到一个零值。模块是否可以访问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

NOTE, for this, I was using Ruby 2.0.0. The following also does not puts desired result in Ruby 2.1.2.

注意,为此,我使用的是Ruby 2.0.0。以下内容也没有将所需的结果放在Ruby 2.1.2中。

3 个解决方案

#1


2  

Here is a meta programming for you :

这是一个元编程:

#!/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

module_eval says

module_eval说

Evaluates the string or block in the context of mod, except that when a block is given, constant/class variable lookup is not affected....

在mod的上下文中计算字符串或块,除了在给出块时,常量/类变量查找不受影响....

Thus inside Bar.module_eval { @current_session = session }, @current_session is the instance variable of Bar only and I am setting the value of it to the instance variable value of the class Foo, which is @current_session.

因此在Bar.module_eval {@current_session = session}中,@ current_session只是Bar的实例变量,我将它的值设置为类Foo的实例变量值,即@current_session。

Baz.send(:include, Bar) is helpfull, which returns class/module itself, which is including the other module. include(module, ...) → self.

Baz.send(:include,Bar)是有用的,它返回类/模块本身,包括另一个模块。包括(模块,...)→自我。

class Baz
end

Read this post to understand the below stuff.

阅读这篇文章,了解以下内容。

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

Update

更新

As @Christian Fazzin gave a good suggestion :-

正如@Christian Fazzin给出了一个很好的建议: -

If you want Bar module to have write method also, then you have to do 2 changes -

如果你想要Bar模块也有写方法,那么你必须进行2次更改 -

  • Bar should contain then attr_accesor :current_session, instead of what it has now.
  • Bar应该包含attr_accesor:current_session,而不是它现在拥有的。
  • You don't need to use the power of module_eval there, rather use syntactic sugraness of write methods, like put Bar.current_session = current_session inside the method foobarbaz . Remove the lines session = current_session and Bar.module_eval { @current_session = session }.
  • 你不需要在那里使用module_eval的强大功能,而是使用写方法的语法sugraness,比如在方法foobarbaz中放置Bar.current_session = current_session。删除行session = current_session和Bar.module_eval {@current_session = session}。

#2


1  

After creating an instance of Foo (foo, say) and in doing so initializing foo's instance variable @current_session to 'current session', it appears to me that you want foo.foobarbaz to do the following:

创建一个Foo实例(foo,比如说)并在这样做时将foo的实例变量@current_session初始化为'当前会话',在我看来你想让foo.foobarbaz执行以下操作:

  • cause Baz to include the module Bar
  • 导致Baz包含模块Bar
  • create an instance of Baz (baz, say)
  • 创建一个Baz实例(baz,比方说)
  • create an instance variable named @current_session for baz and assign it the value of foo's instance variable of the same name
  • 为baz创建一个名为@current_session的实例变量,并为其指定foo的同名实例变量的值
  • invoke baz.foobar to return the value of baz's instance variable @current_session.
  • 调用baz.foobar返回baz的实例变量@current_session的值。

If my understanding is correct, we can perform these four steps with four lines in Foo#foobarbaz:

如果我的理解是正确的,我们可以在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"

I've slightly modified what foobarbaz returns to show where it is coming from.

我稍微修改了foobarbaz返回的内容,以显示它的来源。

Note that the third line of foobarbaz could be changed to either of the following

请注意,foobarbaz的第三行可以更改为以下任一项

baz.instance_variable_set(:@current_session, @current_session)
baz.instance_variable_set(:@current_session,
      instance_variable_get(:@current_session))

If the latter of these were used, @current_session's accessor would not be needed.

如果使用后者,则不需要@ current_session的访问者。

#3


0  

You'd just need to set instance variable (not class instance variable!) @current_session of class Baz.

你只需要设置实例变量(不是类实例变量!)@current_session类Baz。

With slightest modification of your code without need of additional class/module methods the most straightforward way is to define initialization method that sets the required variable:

只需对代码进行最轻微的修改而无需额外的类/模块方法,最直接的方法是定义设置所需变量的初始化方法:

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

#1


2  

Here is a meta programming for you :

这是一个元编程:

#!/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

module_eval says

module_eval说

Evaluates the string or block in the context of mod, except that when a block is given, constant/class variable lookup is not affected....

在mod的上下文中计算字符串或块,除了在给出块时,常量/类变量查找不受影响....

Thus inside Bar.module_eval { @current_session = session }, @current_session is the instance variable of Bar only and I am setting the value of it to the instance variable value of the class Foo, which is @current_session.

因此在Bar.module_eval {@current_session = session}中,@ current_session只是Bar的实例变量,我将它的值设置为类Foo的实例变量值,即@current_session。

Baz.send(:include, Bar) is helpfull, which returns class/module itself, which is including the other module. include(module, ...) → self.

Baz.send(:include,Bar)是有用的,它返回类/模块本身,包括另一个模块。包括(模块,...)→自我。

class Baz
end

Read this post to understand the below stuff.

阅读这篇文章,了解以下内容。

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

Update

更新

As @Christian Fazzin gave a good suggestion :-

正如@Christian Fazzin给出了一个很好的建议: -

If you want Bar module to have write method also, then you have to do 2 changes -

如果你想要Bar模块也有写方法,那么你必须进行2次更改 -

  • Bar should contain then attr_accesor :current_session, instead of what it has now.
  • Bar应该包含attr_accesor:current_session,而不是它现在拥有的。
  • You don't need to use the power of module_eval there, rather use syntactic sugraness of write methods, like put Bar.current_session = current_session inside the method foobarbaz . Remove the lines session = current_session and Bar.module_eval { @current_session = session }.
  • 你不需要在那里使用module_eval的强大功能,而是使用写方法的语法sugraness,比如在方法foobarbaz中放置Bar.current_session = current_session。删除行session = current_session和Bar.module_eval {@current_session = session}。

#2


1  

After creating an instance of Foo (foo, say) and in doing so initializing foo's instance variable @current_session to 'current session', it appears to me that you want foo.foobarbaz to do the following:

创建一个Foo实例(foo,比如说)并在这样做时将foo的实例变量@current_session初始化为'当前会话',在我看来你想让foo.foobarbaz执行以下操作:

  • cause Baz to include the module Bar
  • 导致Baz包含模块Bar
  • create an instance of Baz (baz, say)
  • 创建一个Baz实例(baz,比方说)
  • create an instance variable named @current_session for baz and assign it the value of foo's instance variable of the same name
  • 为baz创建一个名为@current_session的实例变量,并为其指定foo的同名实例变量的值
  • invoke baz.foobar to return the value of baz's instance variable @current_session.
  • 调用baz.foobar返回baz的实例变量@current_session的值。

If my understanding is correct, we can perform these four steps with four lines in Foo#foobarbaz:

如果我的理解是正确的,我们可以在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"

I've slightly modified what foobarbaz returns to show where it is coming from.

我稍微修改了foobarbaz返回的内容,以显示它的来源。

Note that the third line of foobarbaz could be changed to either of the following

请注意,foobarbaz的第三行可以更改为以下任一项

baz.instance_variable_set(:@current_session, @current_session)
baz.instance_variable_set(:@current_session,
      instance_variable_get(:@current_session))

If the latter of these were used, @current_session's accessor would not be needed.

如果使用后者,则不需要@ current_session的访问者。

#3


0  

You'd just need to set instance variable (not class instance variable!) @current_session of class Baz.

你只需要设置实例变量(不是类实例变量!)@current_session类Baz。

With slightest modification of your code without need of additional class/module methods the most straightforward way is to define initialization method that sets the required variable:

只需对代码进行最轻微的修改而无需额外的类/模块方法,最直接的方法是定义设置所需变量的初始化方法:

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