Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby programmatically calling method, with variable number of args

I am trying to do something similar to this:

def foo(mode= :serial)

  if (mode == :serial) then
    self.send(:bar, "one_type")
  else
    self.send(:bar,"second_type",:T5)
  end
end

I can obviously type this out like this.

But I've recently tried expanding it to include a second function like this:

def foo(mode= :serial)

  if (mode == :serial) then
    self.send(:bar, "one_type")
    self.send(:foobar, "one_type",20)
  else
    self.send(:bar,"second_type",:T5)
    self.send(:foobar, "one_type",20,:T5)
  end
end

I can still continue as it is, but I thought to myself, there's a pattern here, I should abstract the arguments away into another layer and make this simpler.

So what I wanted to do was something like this:

arg_arr   = [:bar, "one_type"]
arg_arr_2 = [:foobar, "one_type", 20]
if (mode == :serial) then
  self.send(arg_arr)
  self.send(arg_arr_2)
else
  arg_arr << :T5
  arg_arr2 << :T5
  self.send(arg_arr)
  self.send(arg_arr2 )
end

I tried some other ideas involving .each, .inspect, but nothing that would work (the usual error was can't convert array into string, which I'm guessing refers to the fact that it treats the array as the entire function name). I can do it if I explicitly say "use array elements[0] , [1] etc, but that just seems wasteful.

Is there a way to achieve this without writing code that is hardcoded to the number of arguments?

like image 961
John Nikolaou Avatar asked Jan 11 '17 08:01

John Nikolaou


People also ask

How do you accept number of arguments in Ruby?

Ruby has support for methods that accept any number of arguments, either positional or keyword. def some_method(*args) can be called with zero or more parameters. The args variable within the method will be an array of all values passed in when the method is called.

What does * args mean in Ruby?

In the code you posted, *args simply indicates that the method accepts a variable number of arguments in an array called args . It could have been called anything you want (following the Ruby naming rules, of course).

How do you pass an argument in Ruby?

On the command-line, any text following the name of the script is considered a command-line argument. Separated by spaces, each word or string will be passed as a separate argument to the Ruby program. The following example shows the proper syntax to use to launch the test.

What are parameters in Ruby?

What is a parameter? Parameters in ruby are variables that are defined in method definition and which represent the ability of a method to accept arguments. So, if we will not have the appropriate parameters, then we will not be able to pass arguments to a method that will contain the data we need.


2 Answers

Try this

def foo(a, b)
  puts a
  puts b
end

array = ['bar', 'qux']
send(:foo, *array) # using send
foo(*array) # using the method name

Both print

bar
qux

The splat operator * packs or unpacks an array.

like image 135
akuhn Avatar answered Oct 01 '22 20:10

akuhn


Some years ago I did what you are trying now. With an asterisk in front of a method parameter you can receive as many parameters as you want in a function. So You don't need to know the number of the given parameters. It's called a splat.

Send your values as an array with an asterisk in front too and it will work.

I tested the folling with an irb console:

def test(*args)
  puts args.inspect
end

my_args = [1, 2, 3]
self.send(:test, *my_args)
# [1, 2, 3]
# => nil

Or send as many single parameters as you want:

self.send(:test, 'a', 'b', 'c', 'd')
# ["a", "b", "c", "d"]
# => nil

If you have a fixed number of parameters this will work:

def test(arg1, arg2, arg3)
  puts arg1.inspect
  puts arg2.inspect
  puts arg3.inspect
end

my_args = [1, 2, 3]
self.send(:test, *my_args)
# 1
# 2
# 3
# => nil
like image 41
guitarman Avatar answered Oct 01 '22 20:10

guitarman