metaprogramming - Dynamic method calling in Ruby - Stack Overflow

Ask questions Research chat →

https://stackoverflow.com/questions/17454992/dynamic-method-calling-in-ruby · scraped

ruby

Attachments

Scraped Content

— 853 words · 2026-02-14 17:41:52 UTC ·

Excerpt

![](https://prod-files-secure.s3.us-west-2.amazonaws.com/871f1661-80b8-4d0c-ac3b-2adfc6ff4c66/7a945b9d-3ff3-43ff-a929-ce7bf87688c1/apple-touch-icon2.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466V4RKZLI6%2F20260214%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20260214T174151Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEEEaCXVzLXdlc3QtMiJHMEUCIFlKeCai1YmyJQmMSTAsHX%2Bp6xTyKxIjj%2B6qf3dtXnUIAiEApsuy4yOql33P%2BNm2cGgr3w0rqPdnMdoaMUAi8DN0bdoq%2FwMIChAAGgw2Mzc0MjMxODM4MDUiDBRYj4nwneCSZ4123yrcA1B9i%2FXt7KFGFDRko7r3S1a0%2BiTamFJ2efe3HJTeVY556Ls%2FNzizJUIQLXXWh84c8yTgfkD7cbCFJYs5tZ0m3cJabprtXUzB8qTZxb%2Ffntftje8iI9hBQyXM7Aj5D9B%2Bh%2Bo5bPRM9Wm7xoLk76MMhkty35ZXWVOTLEQ3synCp%2BnI%2FGD3HKdWlUdL%2FaEtcSYSgiqVwYGroiccodGsdWwMFnOgH35itUMyQlVdZfdX%2FXF28LJ%2B8clqcly3VcbnWcp14wMRyEuceLNRez4WeT3qG%2BPlZPrcLJ0axcYhHF%2FZNJRrSfsYUsxXp5d%2FairxZcSgEDw0BTeBT813J%2FgpzNKlexIiXLT44RRH1qhC1cfsv%2FwWJpvfRcaJ8K%2B4hAuAaf%2FxOMnbSDXDEvwGCHJZkmE
![](https://prod-files-secure.s3.us-west-2.amazonaws.com/871f1661-80b8-4d0c-ac3b-2adfc6ff4c66/7a945b9d-3ff3-43ff-a929-ce7bf87688c1/apple-touch-icon2.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466V4RKZLI6%2F20260214%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20260214T174151Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEEEaCXVzLXdlc3QtMiJHMEUCIFlKeCai1YmyJQmMSTAsHX%2Bp6xTyKxIjj%2B6qf3dtXnUIAiEApsuy4yOql33P%2BNm2cGgr3w0rqPdnMdoaMUAi8DN0bdoq%2FwMIChAAGgw2Mzc0MjMxODM4MDUiDBRYj4nwneCSZ4123yrcA1B9i%2FXt7KFGFDRko7r3S1a0%2BiTamFJ2efe3HJTeVY556Ls%2FNzizJUIQLXXWh84c8yTgfkD7cbCFJYs5tZ0m3cJabprtXUzB8qTZxb%2Ffntftje8iI9hBQyXM7Aj5D9B%2Bh%2Bo5bPRM9Wm7xoLk76MMhkty35ZXWVOTLEQ3synCp%2BnI%2FGD3HKdWlUdL%2FaEtcSYSgiqVwYGroiccodGsdWwMFnOgH35itUMyQlVdZfdX%2FXF28LJ%2B8clqcly3VcbnWcp14wMRyEuceLNRez4WeT3qG%2BPlZPrcLJ0axcYhHF%2FZNJRrSfsYUsxXp5d%2FairxZcSgEDw0BTeBT813J%2FgpzNKlexIiXLT44RRH1qhC1cfsv%2FwWJpvfRcaJ8K%2B4hAuAaf%2FxOMnbSDXDEvwGCHJZkmE0E2Rp09hVloZyAuwt5wRO8Lg1P6%2B0w3tx0uYTXmsBkkX9CES%2BrUui6j0XBsEYLqZ6YwGrbrWfrIc8KfSFwAg8TNlwWQ%2BN2R6rtMRDErnp92aD%2BcfOj1wCU%2F0ra9wqN2%2B7JNtDztgiFrViErRuAZur8Wz%2Bo4j1UtfeArvnG4ko3V2%2B0nJbkJWqk6%2FDYRunaBNYto1TZsdaGiHAKy%2Fgl7XJrONbPdxEj3yvIcI%2BlZTfMMjRwswGOqUB3VHvszvqf3V9RMkQu7j4KuYVwDh6sViYFMH1h5wiQtRGlbRSSXiTn3jSj%2FQHH8EkgEXPQk6JLu2Pcn%2F1ifqlnRmULjUwj%2FBI%2BHkuicNTyn7qDtBEeyiHKiAf0n%2F2W%2FvkTS04aRtC2nQI9MXoyTPNJSAi36rrlhYl9hEeTcaxdwTKtfovsqQw%2BomU9yBtEwsa%2F%2BFLykXvsj4JrlXFlE%2F6G%2FNagoEJ&X-Amz-Signature=ceff60150b24d9fc2910fc8f87d88e546166a14601f100b4fcad4a2860393c67&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject) is there any reason to ever use send? call needs a method object, send doesn't: ```plain text 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: ```plain text 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: ```plain text user system total real send 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: ```plain text require 'benchmark/ips' class FooBar def name; end end el = FooBar.new Benchmark.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: ```plain text 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/100ms Calculating ------------------------------------- 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.027409s Comparison: 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. I updated the benchmark from @Stefan to check if there are some speed improvements when saving reference to method. But again – send is much faster than call ```plain text require 'benchmark' class Foo def bar; end end foo = Foo.new foo_bar = foo.method(:bar) Benchmark.bm(4) do |b| b.report("send") { 1_000_000.times { foo.send(:bar) } } b.report("call") { 1_000_000.times { foo_bar.call } } end ``` These are the results: ```plain text user system total real send 0.080000 0.000000 0.080000 ( 0.088685) call 0.110000 0.000000 0.110000 ( 0.108249) ``` So send seems to be the one to take. The whole point of send and eval is that you can change the command dynamically. If the method you want to execute is fixed, then you can hard-wire that method without using send or eval. ```plain text receiver.fixed_method(argument) ``` But when you want to invoke a method that varies or you do not know in advance, then you cannot write that directly. Hence the use of send or eval. ```plain text receiver.send(method_that_changes_dynamically, argument) eval "#{code_to_evaluate_that_changes_more_dramatically}" ``` Additional use of send is that, as you noticed, you can call a method with explicit receiver using send.

Visibility

Visible to everyone

Reading Status

Related Bookmarks

My Note


Saved!

Annotations

Export as Markdown
+ Annotate selection

Add Annotation