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 :

One way to invoke a method dynamically in ruby is to send a message to the object :

A second way is instantiate a method object and then call it:

And the third way is to use the eval method:

Well, when to use what?

look at this script, it will be used to benchmark the 3 ways of calling :

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 :

24 Replies to “Ruby dynamic method calling”

  1. 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. Nice tutorial. Is there a one to one correspondence between a method name and the symbol identified by the method?

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

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

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

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

  7. Pingback: value method
  8. 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|"call") { n.times { m = test.method(:length); } }"send") { n.times { test.send(:length) } }"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)

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

  10. Really nice tutorial, really people should follow this blog one who willing to become as advanced ruby programmer. Please keep it post regularly.

  11. Really interesting, thanks. I ran the benchmarks from the article and the comments, and found that send is now quicker than :

    $ ruby -v
    # => ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-darwin10.8.0]
    $ irb
    require “benchmark”
    test = “hi man”
    m = test.method(:length)
    n = 100000

    Benchmark.bmbm {|x|“call”) { n.times { } }“send”) { n.times { test.send(:length) } }“eval”) { n.times { eval “test.length” } }
    Rehearsal —————————————-
    call 0.030000 0.000000 0.030000 ( 0.033575)
    send 0.020000 0.000000 0.020000 ( 0.024986)
    eval 1.620000 0.090000 1.710000 ( 1.733102)
    ——————————- total: 1.760000sec

    user system total real
    call 0.030000 0.000000 0.030000 ( 0.031896)
    send 0.020000 0.000000 0.020000 ( 0.024706)
    eval 1.610000 0.080000 1.690000 ( 1.718123)

    require “benchmark”
    test = “hi man”
    n = 100000

    Benchmark.bmbm {|x|“call”) { n.times { m = test.method(:length); } }“send”) { n.times { test.send(:length) } }“eval”) { n.times { eval “test.length” } }
    Rehearsal —————————————-
    call 0.190000 0.010000 0.200000 ( 0.201836)
    send 0.020000 0.000000 0.020000 ( 0.025355)
    eval 1.620000 0.090000 1.710000 ( 1.736082)
    ——————————- total: 1.930000sec

    user system total real
    call 0.150000 0.010000 0.160000 ( 0.163242)
    send 0.030000 0.000000 0.030000 ( 0.027984)
    eval 1.480000 0.110000 1.590000 ( 1.612706)

  12. Hi admin, i must say you have hi quality content here.

    Your website can go viral. You need initial traffic
    only. How to get it? Search for: Mertiso’s tips go viral

Leave a Reply

Your email address will not be published. Required fields are marked *