When passing a block to instance_eval, it is meant to be executed within the context of that instance. self , when referenced either explicitly or implicitly within that block, should refer to the instance that instance_eval has been called on. This seems to work fine in all cases, except when passing a method object that has been converted to a proc. In this case, self refers to the instance that the method is defined on, rather than the instance where the block is evaluated. Here's a code example to demonstrate what I mean:
class A
def test(&b)
instance_eval(&b)
end
end
class B
def test_a(a)
a.test { puts self }
end
def test_b_helper(*args)
puts self
end
def test_b(a)
m = method(:test_b_helper).to_proc
a.test(&m)
end
end
a = A.new
b = B.new
b.test_a(a) #<A:0x007ff66b086c68>
b.test_b(a) #<B:0x007fa3e1886bc0>
The expected behaviour is for both tests to return the same output. In this case, self should refer to an instance of A, not B.
I have looked in the docs and done some searches, but I have not been able to find information on this peculiarity. I am hoping that there are some experienced Rubyists who can help clear up this difference in behaviour.
Just to clarify, I am using Ruby 1.9.2.
The difference is, that Blocks and Procs are closures , Method objects are not.
Excerpt from D. Flanagan, Y. Matsumoto. The Ruby Programming Language , O'Reilly 2008, p. 204:
One important difference between
Method
objects andProc
objects is that Method objects are not closures. Ruby's methods are intended to be completely self-contained, and they never have access to local variables outside of their own scope. The only binding retained by aMethod
object, therefore, is the value ofself
— the object on which the method is to be invoked.
When you now cast the Method object to_proc
, you bind the value of self
to the calling instance of B, hence you get the result you described above. Actually, self
is already fixed when you create the Method
object.
This gets particularly clear when you consider the following code:
class A
def foo
puts 'bar'
end
end
class B; end
class C < A; end
foo = A.instance_method(:foo)
# => #<UnboundMethod: A#foo>
a = A.new
foo.bind(a).call
# bar
b = B.new
foo.bind(b).call
# TypeError: bind argument must be an instance of A
c = C.new
foo.bind(c).call
# bar
Simply put: self
is always fixed on Method
and UnboundMethod
objects.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.