Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic method calling in Ruby

As far as I am aware there are three ways to dynamically call a method in Ruby:

Method 1:

s = SomeObject.new method = s.method(:dynamic_method) method.call 

Method 2:

s = SomeObject.new s.send(:dynamic_method) 

Method 3:

s = SomeObject.new eval "s.dynamic_method" 

By benchmarking them I have established that Method 1 is by far the fastest, Method 2 is slower, and Method 3 is by far the slowest.

I have also found that .call and .send both allow calling private methods, while eval does not.

So my question is: is there any reason to ever use .send or eval? Why would you not always just use the fastest method? What other differences do these methods of calling dynamic methods have?

like image 977
Abraham P Avatar asked Jul 03 '13 17:07

Abraham P


People also ask

What is metaprogramming in Ruby?

Metaprogramming is a technique by which you can write code that writes code by itself dynamically at runtime. This means you can define methods and classes during runtime.


2 Answers

is there any reason to ever use send?

call needs a method object, send doesn't:

class Foo   def method_missing(name)     "#{name} called"   end end  Foo.new.send(:bar)         #=> "bar called" Foo.new.method(:bar).call  #=> undefined method `bar' for class `Foo' (NameError) 

is there any reason to ever use eval?

eval evaluates arbitrary expressions, it's not just for calling a method.


Regarding benchmarks, send seems to be faster than method + call:

require 'benchmark'  class Foo   def bar; end end  Benchmark.bm(4) do |b|   b.report("send") { 1_000_000.times { Foo.new.send(:bar) } }   b.report("call") { 1_000_000.times { Foo.new.method(:bar).call } } end 

Result:

           user     system      total        real send   0.210000   0.000000   0.210000 (  0.215181) call   0.740000   0.000000   0.740000 (  0.739262) 
like image 107
Stefan Avatar answered Oct 05 '22 13:10

Stefan


Think of it this way:

Method 1 (method.call): Single run-time

If you run Ruby once on your program straight through, you control the entire system and you can hold onto a "pointer to your method" via the "method.call" approach. All you are doing is holding on to a handle to "live code" that you can run whenever you want. This is basically as fast as calling the method directly from within the object (but it is not as fast as using object.send - see benchmarks in other answers).

Method 2 (object.send): Persist name of method to database

But what if you want to store the name of the method you want to call in a database and in a future application you want to call that method name by looking it up in the database? Then you would use the second approach, which causes ruby to call an arbitrary method name using your second "s.send(:dynamic_method)" approach.

Method 3 (eval): Self-modifying method code

What if you want to write/modify/persist code to a database in a way that will run the method as brand new code? You might periodically modify the code written to the database and want it to run as new code each time. In this (very unusual case) you would want to use your third approach, which allows you to write your method code out as a string, load it back in at some later date, and run it in its entirety.

For what it's worth, generally it is regarded in the Ruby world as bad form to use Eval (method 3) except in very, very esoteric and rare cases. So you should really stick with methods 1 and 2 for almost all problems you encounter.

like image 35
Steve Midgley Avatar answered Oct 05 '22 14:10

Steve Midgley