Eval? instance_eval? hmm


#1

Hey ruby wizards. I got a question for you. I want to call a method on
my object instance, with the method name specified by a string. I want
to pass in a hash to my method.

I tried:

myMethodName = “doSomeStuff”
myObject.instance_eval “#{myMethodName}(#{myHash})”

But it complained that it didn’t have a method named #{myHash.to_s}.

So I tried myObject.instance_eval { myMethodName(myHash) }

But it compalined that it didn’t have a method named “myMethodName”
(which is obvious – the method I wanted to call is doSomeStuff).

I’m new to metaprogramming in ruby. What am I missing here?

Last thought: I guess I could assign a transient local var
@input_hash”, then call the method which would use @input_hash instead
of requiring an input. That just seems a little ugly though. Any
prettier solution?

-Steve


#2

2009/6/1 Steve H. removed_email_address@domain.invalid

So I tried myObject.instance_eval { myMethodName(myHash) }

But it compalined that it didn’t have a method named “myMethodName”
(which is obvious – the method I wanted to call is doSomeStuff).

I’m new to metaprogramming in ruby. What am I missing here?

myObject.send(myMethodName, myHash) is a more appropriate idiom for
what
you seem to be doing.


#3

Steve H. wrote:

Hey ruby wizards. I got a question for you. I want to call a method on
my object instance, with the method name specified by a string. I want
to pass in a hash to my method.

I tried:

myMethodName = “doSomeStuff”
myObject.instance_eval “#{myMethodName}(#{myHash})”

But it complained that it didn’t have a method named #{myHash.to_s}.

You can easily see what you’ve done wrong if you just use ‘puts’ instead
of ‘myObject.instance_eval’

Here’s it’s because you turned the hash into a string, and then inserted
the string into your code, and then tried to run it.

irb(main):001:0> myHash={“one”=>1}
=> {“one”=>1}
irb(main):002:0> def showit(x); puts “got #{x.inspect}”; end
=> nil
irb(main):003:0> myMethodName = “showit”
=> “showit”
irb(main):004:0> puts “#{myMethodName}(#{myHash})”
showit(one1)
=> nil

Imagine what happens if you type “showit(one1)” at the command line.

What you needed was just to pass literally ‘myHash’ as the argument,
whilst still interpolating the method name, i.e.

irb(main):005:0> instance_eval “#{myMethodName}(myHash)”
got {“one”=>1}
=> nil

However there are better ways to achieve this, without using the string
version of eval. Probably all you want is this:

myObject.send(myMethodName, myHash)

I’m new to metaprogramming in ruby. What am I missing here?

This isn’t metaprogramming (metaprogramming = programs which write
programs). This is just dynamic method dispatch.

Regards,

Brian.


#4

James – perfect. Thanks!

Brian C. wrote:

You can easily see what you’ve done wrong if you just use ‘puts’ instead
of ‘myObject.instance_eval’

Here’s it’s because you turned the hash into a string, and then inserted
the string into your code, and then tried to run it.

Believe it or not, I did realize that. I just couldn’t find any docs
showing me the proper usage of the string form of “instance_eval” (ie,
passing an argument).

irb(main):005:0> instance_eval “#{myMethodName}(myHash)”
got {“one”=>1}
=> nil

See I thought about trying this, but didn’t even bother, as I expected
it to fail. Anyway I’m glad to see it does indeed work like this.

However there are better ways to achieve this, without using the string
version of eval. Probably all you want is this:

myObject.send(myMethodName, myHash)

Yup. This works perfectly, thanks!!

This isn’t metaprogramming (metaprogramming = programs which write
programs). This is just dynamic method dispatch.

Hah yeah I realize that now… cut me some slack, it was ~1:30am my
time and the wife was harassing me to come to bed! :wink:
Anyway, as you may have guessed, I’m new to Ruby in general. Only been
working with the language for about 3 months. So far I’m loooooving it.
:slight_smile:

Thanks for the helpful replies! I got it working exactly how I wanted.

-Steve


#5

Steve H. wrote:

Here’s it’s because you turned the hash into a string, and then inserted
the string into your code, and then tried to run it.

Believe it or not, I did realize that. I just couldn’t find any docs
showing me the proper usage of the string form of “instance_eval” (ie,
passing an argument).

I wouldn’t expect there to be any. eval or instance_eval with a string
just parses and runs Ruby code, so the proper usage is “just put some
valid Ruby code in there” :slight_smile:

That’s why it’s useful to use puts for debugging - you can see at a
glance whether that code would be valid or not, if you had just inserted
it into your program at that point.

Thanks for the helpful replies! I got it working exactly how I wanted.

No problem, glad it’s all making sense.

Cheers,

Brian.