abdul ahmad
abdul ahmad

Reputation: 476

Pass method name as parameter to another method

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

Answers (3)

thesecretmaster
thesecretmaster

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

philomory
philomory

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

John Naegle
John Naegle

Reputation: 8257

You want to use send:

def two(param, callback)
  send(callback, param)
end

Upvotes: 2

Related Questions