I’m pretty sure that you have heard lots about ruby, specially as being a dynamic language, you can create methods on the fly, add instance variables, define constants and invoke existing methods dynamically , and that’s what this post is all about :
As you know in ruby you can call a public instance method directly ,ex :
s= "hi man" p s.length #=> 6 p s.include? "hi" #=> true
One way to invoke a method dynamically in ruby is to send a message to the object :
p s.send(:length) #=> 6 p s.send(:include?,"hi") #=> true
A second way is instantiate a method object and then call it:
method_object = s.method(:length) p method_object.call #=> 6 method_object = s.method(:include?) p method_object.call('hi') #=> true
And the third way is to use the eval method:
eval "s.length" #=> 6 eval "s.include? 'hi'" #=>true
Well, when to use what?
look at this script, it will be used to benchmark the 3 ways of calling :
require "benchmark" test = "hi man" m = test.method(:length) n = 100000 Benchmark.bmbm {|x| x.report("call") { n.times { m.call } } x.report("send") { n.times { test.send(:length) } } x.report("eval") { n.times { eval "test.length" } } } ####################################### ##### The results ####################################### #Rehearsal ---------------------------------------- #call 0.050000 0.020000 0.070000 ( 0.077915) #send 0.080000 0.000000 0.080000 ( 0.086071) #eval 0.360000 0.040000 0.400000 ( 0.405647) #------------------------------- total: 0.550000sec # user system total real #call 0.050000 0.020000 0.070000 ( 0.072041) #send 0.070000 0.000000 0.070000 ( 0.077674) #eval 0.370000 0.020000 0.390000 ( 0.399442)
Well as you can see, instantiating a method object is the fastest dynamic way in calling a method, also notice how slow using eval is.
Also when sending a message to an object , or when instantiating a method object , u can call private methods of that object :
class Foo private def hi puts "hi man" end end # Normal method calling f = Foo.new #=> <Foo:0x10a0d51> f.hi #=>NoMethodError: private method `hi' called for #<Foo:0x10a0d51> # Sending a message f.send :hi # hi man # Instantiating a method object f.method(:hi).call # hi man # Using eval eval "f.hi" #=>NoMethodError: private method `hi' called for #<Foo:0x10a0d51> # Using instance_eval f.instance_eval {hi} # hi man


i was working on something similar today. i wanted to generate the method name on the fly , i was using eval, but turned out that passing a value to the auto-generated eval method is complicated.
when the user request /api/reset?method=video.list, i auto generate the api method name, for example API::Video::list(user) …. which was easy, but sending the object user was tough Until i found “send” … now all i do self.send(“API::Video::list”,user) and everything works like a charm.
great tutorial man, keep it up
Pingback: Ruby and Metaprogramming - Khaled alHabache’s official blog
Nice tutorial. Is there a one to one correspondence between a method name and the symbol identified by the method?
@kris, i didn’t get you mean really by “the symbol identified by the method”, can you explain more?
May be I should say “Method identified by Symbol”? When you write s.send(:length), how does the runtime know that what I intend to do is call a method called “length()” on the object s unless the method “length()” is internally stored with a symbol :length. Is my assumption right? What if I said s.send(:test) instead and there is a module method (not part of String class) called “test()”?
I am new to Ruby, hence my curiosity and the question.
@Kris, ruby interpreter has a Symbol table that stores class names, method names, class,instance and global variables.
The second part of your question is related to ruby’s method lookup mechanism, please have a look here on point 7.8, and if you couldn’t get it, tell me back so that i explain it in a separated blog post.
@Kris, if you want to explore more also on symbols you can do this: Symbol.all_symbols.size , and then try to define any new method, and execute that instruction again, you will notice that the number increased by one.
Thanks Khaled. I am going to try out what you described. Appreciate your patience and help. Will keep an eye on your blog for any future articles on ruby and “symbols” related articles. It is a bit difficult to wrap my head around symbols coming from C++/Java background but I am working towards it. I would like to see a blog post on how symbols enable programming productivity constructs that might otherwise not be possible without them.
just came across this website from google
this helps me a lot
many thanks
Very useful post.
Pingback: value method
Pingback: Tweets that mention Ruby dynamic method calling - Khaled alHabache’s official blog -- Topsy.com
Pingback: Using “Send” for Methods in Ruby :: dot unplanned
Thanks Khaled, I found this useful.
Nice article.
send is actually twice as fast as call if you (must) create the method object before each call, which is a more realistic scenario:
require "benchmark"
test = "hi man"
n = 100000
Benchmark.bmbm {|x|
x.report("call") { n.times { m = test.method(:length); m.call } }
x.report("send") { n.times { test.send(:length) } }
x.report("eval") { n.times { eval "test.length" } }
}
# results:
# user system total real
# call 0.110000 0.000000 0.110000 ( 0.115235)
# send 0.050000 0.000000 0.050000 ( 0.046434)
# eval 1.750000 0.020000 1.770000 ( 1.779832)
Well done – I love seeing benchmarks around different approaches in pure Ruby.
Beyond speed, though, it’s important to keep in mind that eval, when dealing with tainted strings, is also a potential risk for code injection attacks.
Nice.
Really nice tutorial, really people should follow this blog one who willing to become as advanced ruby programmer. Please keep it post regularly.