tags:

views:

252

answers:

3

I want to store several different methods in an array in Ruby. Suppose I want to store the type method twice:

[type, type]

doesn't store two entries of type in an array; it executes type twice, and stores the results in the array. how do I refer explicitly to the method object itself?

(this is just a simplified version of what I really want.)

EDIT: on second thoughts, it bothers me that the solution proposed below avoids the problem by passing the name of the method. how do you pass the method object itself? for example, what if you pass [:type, :type] to a method that has an alternative resolution for type? how can you pass the type method object itself?

+1  A: 
[:type, :type].collect { |method_name| self.send(method_name) }

Alternatively, if the method is part of an object:

method = obj.method(:type)
values = [method.call, method.call]
Josh Matthews
this doesn't seem to work for me - it just results in `[Object, Object]`.
Peter
The first part of the answer was basically copy-pasted from irb, so I assume that the simplification of your situation is screwing it up.
Josh Matthews
Looks right to me. The type of Object is Object... You probably want `self.send(method_name)` so it applies the method to your object rather than the global Object object.
Dave Ray
ah, I see - I just misunderstood how it works. thanks folks.
Peter
Looks like self.send will work in the global case as well, so I'll amend my answer.
Josh Matthews
+8  A: 

If you want to store a method rather than the result of calling a method or just the message you'd send to invoke it, you need to use the method method on the owning object. So for example

"hello".method(:+)

will return the + method of the object "hello", so that if you call it with the argument " world", you'll get "hello world".

helloplus = "hello".method(:+)
helloplus.call " world" # => "hello world"
Chuck
[method(:type), method(:type)]
jrhicks
@jrhicks: Only if the methods belong to self.
Chuck
First SO Ruby badge - congrats!
Mike Woodhouse
@Mike: Wow, I had no idea I was the first one. Thanks!
Chuck
Whoever downvoted, could you please leave a comment explaining why? As far as I know, this answer is completely accurate. If you know it's wrong somehow, you'd be doing everyone a favor if you set us straight.
Chuck
There's a slight problem with this. `method` returns a copy of the method, in a proc, not the pointer to the method. Re-defining a method you have created a copy of won't alter the copy, for example. See my answer ;)
August Lilleaas
@August: That's because you don't change the method, you replace the method. Obviously a reference to the old method will still refer to the old method just a like a reference to an old instance variable value will still refer to the old one even if you give the instance variable a new value. If you want dynamic dispatch rather than storing a particular method, you want to send a message instead.
Chuck
+4  A: 

If you're thinking about doing method references in Ruby, you're doing it wrong.

There is a built-in method in Ruby called method. It will return a proc version of the method. It is not a reference to the original method, though; every call to method will create a new proc. Changing the method won't affect the proc, for example.

def my_method
  puts "foo"
end

copy = method(:my_method)

# Redefining
def my_method
  puts "bar"
end

copy.call
# => foo

When you want to store pieces of code, and don't want to use regular methods, use procs.

stack = [proc { do_this }, proc { do_that }]
stack.each {|s| s.call }
August Lilleaas
As I said in the comments to my answer, that's exactly how it should work. You don't "change the method," you replace the method. Obviously a reference to the old method will still refer to the old method just a like a reference to an old instance variable value will still refer to the old one even if you give the instance variable a new value. If you want dynamic dispatch rather than storing a particular method, you want to send a message instead.
Chuck