I'm trying to find a way to evaluate an expression that consists of a function and argument. The problem is, I don't want the argument converted into a string.
Specifically, I have the function x="display_datetime" of type String, which takes an object y of type Time. The function name is created dynamically, so I can't just type, display_datetime(y).
What I've tried:
eval("#{x}(#{y})") - This is how I'd normally do it, but this converts the Time to a string. The whole purpose of "display_datetime" is to do a custom Time to string conversion, so this would not be useful
x.send(y) - This does not compile, because send is meant to send classes functions, not functions arguments.
x.constantize... - also doesn't compile, even if x includes the classname.
- y.send(:to_s(:datetime)) This approach is less than ideal, but I still can't get it to work. display_datetime just calls y.to_s(:datetime), so I tried setting the dynamically generated function name x to simply be .to_s(:datetime), then invoking x on y with the send command. This would work, except for the fact that to_s takes an argument. I don't know how to call send (or try) when the argument being sent has to take an argument
- Another idea I've had is to override the Time class to add a conversion function to it like to_s, but that doesn't take any argument.
Those last two approaches are less than ideal. I feel like there has to be some better way of doing this along the lines of the first 3 approaches.
Any feedback is appreciated.
Thanks
2 Answers 2
There is no problem with send
. Do this:
send(x, y)
-
This works. You just need to call send on the object. So I'm accepting this answer. I didn't know send could take a function name and an argument list. I verified that if y is of type fixnum, the type is still fix num inside the function. So there's no string conversion taking place. This makes this the best approach for my needs.jsarma– jsarma2014年02月10日 19:52:45 +00:00Commented Feb 10, 2014 at 19:52
Given
s = "display_datetime" # function name, dynamic
y = Time.now # or some other Time object
And some object o
that responds to the display_datetime()
method, you can call o.display_datetime
dynamically in several ways, at least:
o.send(s.intern, y) # => o.send(:display_datetime, y)
Or,
eval("o.#{s}(y)") # => eval("o.display_datetime(y)")
Alternatively,
o.instance_eval("#{s}(y)") # => o.instance_eval("display_datetime(y)")
Or if you want evalception, if the variable n
contains the name of the variable o
:
n = 'o'
eval %Q(#{n}.send(:instance_eval, "\#{s}(y)"))
-
Thanks for your answer. Your first solution works well for me. The last 3 don't work, because y gets stringified in all those. I gave the answer to the other guy though, because his came in earlier.jsarma– jsarma2014年02月10日 19:57:21 +00:00Commented Feb 10, 2014 at 19:57
Explore related questions
See similar questions with these tags.
display_datetime