Reputation: 405
I'm trying to learn Ruby. I want to pass an arbitrary function and an arbitrary list of arguments and keyword arguments into another function.
for example, I have this arbitrary function below
def dummy_func(a, b)
return a+b
end
And I have this wrapper function
def wrapper(func, *args, **kwargs)
func(args, kwargs))
end
I want it so I can pass my arguments in any of the following ways and still return the correct answer
wrapper(dummy_func, a=1, b=2)
wrapper(dummy_func, 1, b=2)
wrapper(dummy_func, a=1, b=2)
wrapper(dummy_func, 1, 2)
Is this possible in Ruby? What would be an idiomatic way of approaching it?
Upvotes: 0
Views: 1131
Reputation: 164639
The idiomatic way is to instead yield to a block.
def dummy_func(a, b, key:)
return a+b+key
end
def wrapper
puts yield
end
a = 4
b = 5
c = 6
wrapper do
dummy_func(a ,b, key: c)
end
Since the block is closure it can see all the same variables that the call to wrapper
can. Now there's no need to pass wrapper's arguments through.
If you really want to make your wrapper, you can do some introspection to determine what arguments the wrapped function takes.
def dummy_func(a, b=23, key: 42)
return a+b+key
end
def no_keys(a, b=23)
return a+b
end
def wrapper(func, *array, **hash)
method = self.method(func)
takes_array = method.parameters.any? { |p| [:req, :opt, :rest].include?(p[0]) }
takes_hash = method.parameters.any? { |p| [:keyreq, :key, :keyrest].include?(p[0]) }
if takes_array && takes_hash
self.send(func, *array, **hash)
elsif takes_array
self.send(func, *array)
elsif takes_hash
self.send(func, **hash)
else
self.send(func)
end
end
a = 4
b = 5
c = 6
puts wrapper(:dummy_func, a, b, key:c)
puts wrapper(:no_keys, a, b)
But this is quite a bit more complex and less flexible than yielding to a block. This also limits you to "functions" which are really methods on the main object (there are no function references in Ruby). That's why they're called with self.send
. Blocks require no assumptions about what is being wrapped.
Upvotes: 2
Reputation: 6445
The closest you can get is keyword arguments:
https://www.justinweiss.com/articles/fun-with-keyword-arguments/
def hello_message(greeting, time_of_day, first_name:, last_name:)
"#{greeting} #{time_of_day}, #{first_name} #{last_name}!"
end
args = ["Morning"]
keyword_args = {last_name: "Weiss"}
hello_message("Good", *args, first_name: "Justin", **keyword_args)
=> "Good Morning, Justin Weiss!"
Upvotes: 0