How to do instance_eval with arguments


#1

I’m prototyping a DSL and came across a situtation where I have a lambda
that I would like to give to instance_eval, but the lambda takes
arguments. Instance_eval will not supply any arguments when evaluating
the lambda and I don’t see a straightforward way around this.

Any suggestions?

Here’s a test case …

class Dummy
def f
:dummy_value
end
end

def instance_eval_with_args(obj, *args, &block)

Magic goes here to evaluate +block+ in the scope of

+obj+, yet pass a list of argument values to the block.

end

class TestInstanceEvalWithArgs < Test::Unit::TestCase
def test_instance_eval_with_args
# Create a block that returns the value of an argument and a value
# of a method call to +self+. This is the basic functionality I
need.
block = lambda { |a| [a, f] }

assert_equal [:arg_value, :dummy_value],
  instance_eval_with_args(Dummy.new, :arg_value, &block)

end
end


– Jim W.


#2

On Thu, Feb 09, 2006 at 07:33:53AM +0900, Jim W. wrote:

I’m prototyping a DSL and came across a situtation where I have a lambda
that I would like to give to instance_eval, but the lambda takes
arguments. Instance_eval will not supply any arguments when evaluating
the lambda and I don’t see a straightforward way around this.

Any suggestions?

This is what 1.9’s Object#instance_exec does:
http://eigenclass.org/hiki.rb?Changes+in+Ruby+1.9#l10

One way to implement it in 1.8:

The following script was annotated with my xmp filter

http://eigenclass.org/hiki.rb?Enhanced+xmp+code+evaluation+and+annotation

class Object
def instance_exec(*args, &block)
mname = “_instance_exec#{Thread.current.object_id.abs}”
class << self; self end.class_eval{ define_method(mname, &block) }
begin
ret = send(mname, *args)
ensure
class << self; self end.class_eval{ undef_method(mname) } rescue
nil
end
ret
end
end

your test case

class Dummy
def f
:dummy_value
end
end

def instance_eval_with_args(obj, *args, &block)

Magic goes here to evaluate +block+ in the scope of

+obj+, yet pass a list of argument values to the block.

obj.instance_exec(*args, &block)
end

require ‘test/unit’
class TestInstanceEvalWithArgs < Test::Unit::TestCase
def test_instance_eval_with_args
# Create a block that returns the value of an argument and a value
# of a method call to +self+. This is the basic functionality I
need.
block = lambda { |a| [a, f] }

assert_equal [:arg_value, :dummy_value],
  instance_eval_with_args(Dummy.new, :arg_value, &block)

end
end
END

>> Loaded suite -

>> Started

>> .

>> Finished in 0.000561 seconds.

>>

>> 1 tests, 1 assertions, 0 failures, 0 errors


#3

Hi,

In message “Re: how to do instance_eval with arguments”
on Thu, 9 Feb 2006 07:33:53 +0900, Jim W.
removed_email_address@domain.invalid writes:
|
|I’m prototyping a DSL and came across a situtation where I have a lambda
|that I would like to give to instance_eval, but the lambda takes
|arguments. Instance_eval will not supply any arguments when evaluating
|the lambda and I don’t see a straightforward way around this.
|
|Any suggestions?

CVS HEAD (1.9) has instance_exec method that works exactly what you
want.

						matz.

#4

Mauricio F. wrote:

On Thu, Feb 09, 2006 at 07:33:53AM +0900, Jim W. wrote:

Any suggestions?
[…]

One way to implement it in 1.8:

[… solution elided …]

Thanks! That should be good enough for my prototype … at least until
1.9 features become standard.


– Jim W.


#5

Yukihiro M. wrote:

removed_email_address@domain.invalid writes:
|
|I’m prototyping a DSL and came across a situtation where I have a lambda
|that I would like to give to instance_eval, but the lambda takes
|arguments. Instance_eval will not supply any arguments when evaluating
|the lambda and I don’t see a straightforward way around this.
|
|Any suggestions?

CVS HEAD (1.9) has instance_exec method that works exactly what you
want.

Excellent! I think Matz’s time machine is every bit as good as
Guido’s[1].


– Jim W.

[1] http://www.catb.org/jargon/html/G/Guido.html