5 Dec, 2008 in Ruby, metaprogramming by khelll

Ruby dynamic method calling

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

10 Responses so far | Have Your Say!

  1. moski doski - Gravatar

    moski doski  |  December 31st, 2008 at 6:39 am #

    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 :)

  2. Ruby and Metaprogramming – Khaled alHabache’s official blog - Gravatar

    Ruby and Metaprogramming - Khaled alHabache’s official blog  |  December 31st, 2008 at 7:25 am #

    [...] Ruby dynamic method calling [...]

  3. Kris - Gravatar

    Kris  |  January 2nd, 2009 at 8:55 pm #

    Nice tutorial. Is there a one to one correspondence between a method name and the symbol identified by the method?

  4. khelll - Gravatar

    khelll  |  January 2nd, 2009 at 11:55 pm #

    @kris, i didn’t get you mean really by “the symbol identified by the method”, can you explain more?

  5. Kris - Gravatar

    Kris  |  January 3rd, 2009 at 2:20 pm #

    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.

  6. khelll - Gravatar

    khelll  |  January 3rd, 2009 at 11:57 pm #

    @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.

  7. khelll - Gravatar

    khelll  |  January 4th, 2009 at 9:45 am #

    @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.

  8. Kris - Gravatar

    Kris  |  January 4th, 2009 at 10:29 am #

    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.

  9. vin - Gravatar

    vin  |  April 23rd, 2009 at 2:29 am #

    just came across this website from google

    this helps me a lot

    many thanks :D

  10. Rafa1970 - Gravatar

    Rafa1970  |  February 18th, 2010 at 1:10 am #

    Very useful post.

Leave a Feedback

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">