Object#instance_eval is often of use when creating a DSL with Ruby, but
it's not as powerful as the Object#instance_exec method introduced in
the 1.9 branch, which can be used as in
i.e. it allows you to pass arguments to the block which is to be
evaluated with a new self*1.
This is one of the 1.9 features I wouldn't mind having in 1.8.
My first implementation, posted to ruby-talk, was
class Objectdef instance_exec(*args,&block)mname="__instance_exec_#{Thread.current.object_id.abs}"class <<self;selfend.class_eval{define_method(mname,&block)}beginret=send(mname,*args)ensureclass <<self;selfend.class_eval{undef_method(mname)}rescuenilendretendend
It operates by defining a temporary method in the singleton class of the
object to be used as the new self inside the block, easily passing
the basic test provided by Jim Weirich in ruby-talk:179038:
class Dummydef f:dummy_valueendendrequire'test/unit'class TestInstanceEvalWithArgs<Test::Unit::TestCasedef 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)endend#>> Loaded suite -#>> Started#>> .#>> Finished in 0.000566 seconds.#>> #>> 1 tests, 1 assertions, 0 failures, 0 errors
That instance_exec implementation is thread-safe thanks to the
Thread.current.object_id trick, but it doesn't work with immediate
values (Fixnums and friends), and most importantly it bombs when given a
frozen object:
At that point I remembered there had been some discussion on ruby-core regarding
instance_exec. A quick google search showed that there's a number of implementations floating around.
Funnily enough, my implementation was very similar to
Ara Howard's, posted in
the original
ruby core thread*2.
I also discovered that Rails ships with its own instance_exec in active_support:
class Procdef bind(object)block,time=self,Time.now(class <<object;selfend).class_evaldomethod_name="__bind_#{time.to_i}_#{time.usec}"define_method(method_name,&block)method=instance_method(method_name)remove_method(method_name)methodend.bind(object)endendclass Objectdef instance_exec(*arguments,&block)block.bind(self)[*arguments]endend
That definition is essentially equivalent to the other ones, but
not strictly thread-safe, though (it will fail if your box or your Ruby
interpreter are really fast ;), and when you decide to mess up with
the system clock).
I also found Facets mentioned in that
context, but was too lazy to find the instance_exec equivalent amongst
its 400+ methods(!) (or maybe it was never added actually?).