i just came across a bug in it (it's not re-entrant) - here's the fix:
[#instance_exec implementation]
The example you gave does work with my implementation too:
RUBY_VERSION # => "1.8.5"
RUBY_RELEASE_DATE # => "2006-06-24"
class Object
module InstanceExecHelper; end
include InstanceExecHelper
def instance_exec(*args, &block)
mname = "__instance_exec_#{Thread.current.object_id.abs}_#{object_id.abs}"
InstanceExecHelper.module_eval{ define_method(mname, &block) }
begin
ret = send(mname, *args)
ensure
InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
# ==========
# thx to this
end
ret
end
end
a = []
a.instance_exec 42 do |x|
instance_exec x do |y|
push y
p size
end
end
a # => [42]
a.size # => 1
# >> 1
I had actually a test for that (see test_instance_exec_nested below). Do you
have any additional assertions handy?
(So far test_instance_exec_with_frozen_obj fails in all the other
implementations I've seen.)
require 'test/unit'
class TestInstanceEvalWithArgs < Test::Unit::TestCase
def test_instance_exec
# Create a block that returns the value of an argument and a value
# of a method call to +self+.
block = lambda { |a| [a, f] }
assert_equal [:arg_value, :dummy_value],
Dummy.new.instance_exec
arg_value, &block)
end
def test_instance_exec_with_frozen_obj
block = lambda { |a| [a, f] }
obj = Dummy.new
obj.freeze
assert_equal [:arg_value, :dummy_value],
obj.instance_exec
arg_value, &block)
end
def test_instance_exec_nested
i = 0
obj = Dummy.new
block = lambda do |arg|
[arg] + instance_exec(1){|a| [f, a] }
end
# the following assertion expanded by the xmp filter automagically from:
# obj.instance_exec
arg_value, &block) #=>
assert_equal([:arg_value, :dummy_value, 1], obj.instance_exec
arg_value, &block))
end
end