Photo by Jeffrey Dungen on Unsplash

The Arguments Forwarding shorthand in Ruby 2.7

How to 3-dots operator can be used to automatically forward arguments to another method call

Tech - RubyCademy
Published in
2 min readOct 27, 2020

--

In Ruby, it occurs that a method directly forwards arguments to another one

Here, forwarding *args, **kwargs, &blk is redundant and it impacts the readability of the code. To overcome this problem, Ruby 2.7 introduced the arguments forwarding shorthand

In the above example, everything passed to Exporter.run is forwarded to new. As new calls initialize then Exporter#initialize receives all the arguments passed to the call of Exporter.run.

Neat!

Now, what happens if Exporter.run needs to manipulate arguments to instantiate the right exporter, for example?

produces

syntax error, unexpected def self.run(type, ...)
^^^

Indeed, the arguments forwarding shorthand can’t be mixed with other arguments and it also can’t be destructured.

The above feature enhancement is in discussion since December 2019. So what if you still want to manipulate arguments before forwarding them to another method call?

Feel free to have a look at the module_function vs extend self article if you’re not familiar with the module_function method.

In Exporter.run method, we declare a proc that takes 2 arguments:

  • *_ : regular arguments (unused in the block)
  • **kwarg : keywords arguments

In this block, we check if type passed as a keyword argument is whitelisted by Exporter. If so, we instantiate the right exporter by forwarding all the arguments using the Argument Forwarding shorthand—we instantiate Exporter::CSVExporter in this case.

Finally, we call the generate_export method on our freshly instantiated exporter.

The block passed to our proc can manipulate the arguments because the Argument Forwarding shorthand is passed to call. So the arguments passed to call are directly passed as arguments of the block.

UPDATE: Ruby 3

Arguments forwarding now supports leading arguments:

def method_missing(meth, ...)
send(:"do_#{ meth }", ...)
end

RubyCademy’s Newsletter

In the RubyCademy newsletter, I share what I’m learning about Ruby & Rails, including helpful tips, code insights, and exclusive Ruby Cards with detailed explanations.

If it sounds helpful, feel free to subscribe for free:

Thank you for taking the time to read this message!

--

--