Getting a Reference to a Method
Problem
You want to the name of a method into a reference to the method itself.
Solution
Use the eponymous Object#method method:
s = 'A string' length_method = s.method(:length) # => # length_method.arity # => 0 length_method.call # => 8
Discussion
The Object#methods introspection method returns an array of strings, each containing the name of one of the methods available to that object. You can pass any of these names into an object's method method and get a Method object corresponding to that method of that object.
A Method object is bound to the particular object whose method method you called. Invoke the method's Method#call method, and it's just like calling the object's method directly:
1.succ # => 2 1.method(:succ).call # => 2
The Method#arity method indicates how many arguments the method takes. Arguments, including block arguments, are passed to call just as they would be to the original method:
5.method('+').call(10) # => 15 [1,2,3].method(:each).call { |x| puts x } # 1 # 2 # 3
A Method object can be stored in a variable and passed as an argument to other methods. This is useful for passing preexisting methods into callbacks and listeners:
class EventSpawner def initialize @listeners = [] @state = 0 end def subscribe(&listener) @listeners << listener end def change_state(new_state) @listeners.each { |l| l.call(@state, new_state) } @state = new_state end end class EventListener def hear(old_state, new_state) puts "Method triggered: state changed from #{old_state} " + "to #{new_state}." end end spawner = EventSpawner.new spawner.subscribe do |old_state, new_state| puts "Block triggered: state changed from #{old_state} to #{new_state}." end spawner.subscribe &EventListener.new.method(:hear) spawner.change_state(4) # Block triggered: state changed from 0 to 4. # Method triggered: state changed from 0 to 4.
A Method can also be used as a block:
s = "sample string" replacements = { "a" => "i", "tring" => "ubstitution" } replacements.collect(&s.method(:gsub)) # => ["simple string", "sample substitution"]
You can't obtain a reference to a method that's not bound to a specific object, because the behavior of call would be undefined. You can get a reference to a class method by calling method on the class. When you do this, the bound object is the class itself: an instance of the Class class. Here's an example showing how to obtain references to an instance and a class method of the same class:
class Welcomer def Welcomer.a_class_method return "Greetings from the Welcomer class." end def an_instance_ method return "Salutations from a Welcomer object." end end Welcomer.method("an_instance_method") # NameError: undefined method 'an_instance_method' for class 'Class' Welcomer.new.method("an_instance_method").call # => "Salutations from a Welcomer object." Welcomer.method("a_class_method").call # => "Greetings from the Welcomer class."
See Also
- Recipe 7.11, "Coupling Systems Loosely with Callbacks," contains a more complex listener example