Dynamic method calling in Ruby Dynamic method calling in Ruby ruby ruby

Dynamic method calling in Ruby


is there any reason to ever use send?

call needs a method object, send doesn't:

class Foo  def method_missing(name)    "#{name} called"  endendFoo.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; endendBenchmark.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        realsend   0.210000   0.000000   0.210000 (  0.215181)call   0.740000   0.000000   0.740000 (  0.739262)


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.


Here is all possible method calls:

require 'benchmark/ips'class FooBar  def name; endendel = FooBar.newBenchmark.ips do |x|  x.report('plain') { el.name }  x.report('eval') { eval('el.name') }  x.report('method call') { el.method(:name).call }  x.report('send sym') { el.send(:name) }  x.report('send str') { el.send('name') }  x.compare!end

And results are:

Warming up --------------------------------------               plain   236.448k i/100ms                eval    20.743k i/100ms         method call   131.408k i/100ms            send sym   205.491k i/100ms            send str   168.137k i/100msCalculating -------------------------------------               plain      9.150M (± 6.5%) i/s -     45.634M in   5.009566s                eval    232.303k (± 5.4%) i/s -      1.162M in   5.015430s         method call      2.602M (± 4.5%) i/s -     13.009M in   5.010535s            send sym      6.729M (± 8.6%) i/s -     33.495M in   5.016481s            send str      4.027M (± 5.7%) i/s -     20.176M in   5.027409sComparison:               plain:  9149514.0 i/s            send sym:  6729490.1 i/s - 1.36x  slower            send str:  4026672.4 i/s - 2.27x  slower         method call:  2601777.5 i/s - 3.52x  slower                eval:   232302.6 i/s - 39.39x  slower

It's expected that plain call is the fastest, no any additional allocations, symbol lookups, just lookup and evaluation of method.

As for send via symbol, it is faster than via string as its much more easer to allocate memory for symbol. Once it's defined it's stored for long term in memory and there no reallocations.

The same reason can be said about method(:name) (1) it's requires to allocate memory for Proc object (2) we are calling the method in class which leads for additional method lookup which takes time too.

eval is runs interpreter so it's the heaviest.