Reputation: 19713
I am dynamically including a module into the Baz
class in the foobarbaz
method.
However, when I execute this in ruby, I get a nil puts
. Doesn't the module have access to Foo
's instance variables?
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.
Upvotes: 0
Views: 547
Reputation: 110665
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:
Baz
to include
the module Bar
Baz
(baz
, say)@current_session
for baz
and assign it the value of foo
's instance variable of the same namebaz.foobar
to return the value of baz
's instance variable @current_session
. If my understanding is correct, we can perform these four steps with four lines in 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.
Note that the third line of foobarbaz
could be changed to either of the following
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.
Upvotes: 1
Reputation: 118261
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
Evaluates the string or block in the context of mod, except that when a block is given, constant/class variable lookup is not affected....
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
.
Baz.send(:include, Bar)
is helpfull, which returns class/module itself, which is including the other module. include(module, ...) → self
.
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 :-
If you want Bar
module to have write method also, then you have to do 2 changes -
Bar
should contain then attr_accesor :current_session
, instead of what it has now.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 }
.Upvotes: 2
Reputation: 7719
You'd just need to set instance variable (not class instance variable!) @current_session
of class 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
Upvotes: 0