Reputation: 83680
How can I access instance variable from singleton method?
class Test
def initialize(a)
@a = a
end
def item
item = "hola"
def item.singl
[self, @a].join(" + ")
end
item
end
end
test = Test.new("cao")
item = test.item
item.singl
#=> ... @a is nil
Upvotes: 3
Views: 1587
Reputation: 15954
You are not trying to access an instance variable of item
. item
is a String
object whereas @a
is an instance variable of the Test
object test
.
Both are independent. The only way to access that @a
from item
is to have a reference to test
(or @a
) in item
, e.g.
class Test
attr_reader :a
def initialize(a)
@a = a
end
def item
item = "hola"
def item.singl
[self, @parent.a].join(" + ")
end
item.instance_variable_set(:@parent, self)
item
end
end
test = Test.new("cao")
item = test.item
item.singl
Upvotes: 1
Reputation: 4115
Try using define_method. Def puts you inside a new scope.
class Test
def initialize(a)
@a = a
end
def item
item = "hola"
item.singleton_class.send(:define_method, :singl) do
[self, @a].join(" + ")
end
item
end
end
test = Test.new("cao")
item = test.item
item.singl #=> "hola + "
In your example though, you still have a problem, inside the singleton class of a string @a hasn't been defined. This is primarily because self in this context is the string instance, not a test instance where @a
exists. To fix this you can rebind the instance variable to something else, but this might not be the behavior you're looking for. You can also, set the instance variable in your new singleton class.
For example,
class Test
def initialize(a)
@a = a
end
def item
item = "hola"
new_self = self
item.singleton_class.send(:define_method, :singl) do
[self, new_self.instance_variable_get(:@a)].join(" + ")
end
item
end
end
test = Test.new("cao")
item = test.item
item.singl
class Test
def initialize(a)
@a = a
end
def item
item = "hola"
item.singleton_class.send(:define_method, :singl) do
[self, @a].join(" + ")
end
item.singleton_class.send(:define_method, :set) do
@a = "cao"
end
item
end
end
test = Test.new("cao")
item = test.item
item.set
item.singl
Its important to note the differences between the two methods. In the first method, we retain a reference to the original instance variable, via the original object. In the second method, we make a new instance variable, bound under the new singleton class, containing a copy of the original test.@a.
If you are using a non-native object you may be able to get away with a mixture of both methods. Referencing the old instance variable's object with the singelton classes new instance variable via a pointer, but this won't work for int, string, float, etc...
EDIT: As Benoit pointed out, in the second method the "set" method should just be an attr_accessor. In fact, you can set the instance variable without defining a new method at all. Via item.instance_variable_set(:@, "cao")
Upvotes: 6
Reputation: 13675
You're trying to set your instance variable on the Test
class and retrieve it in the string instance, these are not the same objects and do not share instance variables. You could do the following to pass it between the two instances:
class Test
def initialize(a)
@a = a
end
def item
item = "hola"
item.singleton_class.send :attr_accessor, :a
# set the item's @a with the Test instance one
item.a = @a
def item.singl
[self, @a].join(" + ")
end
item
end
end
test = Test.new("cao")
item = test.item
puts item.singl
Upvotes: 1