Reputation: 476
I want to pass a method name, not a block, as a parameter to another method to use as a callback:
def one(param)
puts param
end
def two(param, &callback)
callback.call(param)
end
two('hi', :one)
Is this possible? I tried it and got an error saying I passed two parameters but only one was expected.
Upvotes: 5
Views: 6502
Reputation: 1984
While @JohnNaegle's answer is correct, I find this a slightly more elegant way to do this is using #method
, which returns a Method
object. The method object has a .call(*args)
method, though which you can pass arguments. So, with your example it would look like:
def one(param)
puts param
end
def two(param, callback)
callback.call(param)
end
two('hi', method(:one))
The change is that you are passing a reference to an instance of Method
by calling Object.method
(same thing as method
) rather than a string.
If you want to pass a symbol instead of a Method
you could use the following version of #two
:
def two_2(param, callback)
method(callback.to_sym).call(param)
end
two_2('hi', :one)
# Or you could also pass a string or anything that #responds_to?(:to_sym)
two_2('hi', 'one')
A further improvement I would recommend, so that two
can accept more than one parameter to pass to the callback would be listing the callback first then using *params
to receive as many parameters as necessary. Here is an example (using definition of #one
from above):
def one_with_many_params(*params)
puts params.join
end
# Improved version of two
def two_3(callback, *params)
callback.call(params)
end
# Improved version of two_2
def two_4(callback, *params)
method(callback.to_sym).call(params)
end
two_3(method(:one), 'hi') #=> "hi"
two_3(method(:one_with_many_params), "Hi", " my ", "name", " is ", "Foo!")
#=> "Hi my name is Foo!"
two_4('one', 'Hello!') #=> "Hello!"
two_4(:one_with_many_params, "Hi", " my ", "name", " is ", "Bar!")
#=> "Hi my name is Bar!"
Upvotes: 4
Reputation: 1767
If you want to send the name of a method, then, yes, a symbol is the correct form for that, as John's answer points out (and you can then use send to call that method). But, if you want, you can also send the method itself:
def one(param)
puts param
end
def two(param, &callback)
callback.call(param)
end
one_method = method(:one)
two('hi', &one_method)
The advantage of that way of doing things is that you can do it when a method is expecting a block, without (as two
does) without having to change the method definition. You also can pass in methods from different objects, whereas with send if you want to call the method on a different object you'd also have to pass in who you want to call it on (or figure that out in some other way):
class Foo
def one(param)
puts param
end
end
class Bar
def two(param, &callback)
callback.call(param)
end
end
f = Foo.new
b = Bar.new
b.two('hi') {|param| puts param } # with a block
b.two('hi', &f.method(:one)) # with a Method object
Upvotes: 4
Reputation: 8257
You want to use send
:
def two(param, callback)
send(callback, param)
end
Upvotes: 2