tags:

views:

68

answers:

4

When sent a message, a Ruby object searches to see whether it has a method by that name to respond with. Its method lookup searches in the following order, and uses the first method it finds.

  1. Singleton methods defined on itself
  2. Methods defined in its class
  3. Any modules mixed into its class, in reverse order of inclusion (only the earliest inclusion of a given module has any effect - if the superclass includes module A, and the subclass includes it again, it’s ignored in the subclass; if the subclass includes A then B then A, the second A is ignored)
  4. Its superclass
  5. Any methods mixed into the superclass, and so on up the line

This lookup path is followed at the moment the method is called; if you make an instance of a class, then reopen the class and add a method or mix one in via a module, the existing instance will gain access to that method.

If all of this fails, it looks to see if it has a method_missing method, or if its class does, etc.

My question is this: aside from examining the code by hand, or using example methods like puts "I'm on module A!", can you tell where a given method came from? Can you, for example, list an object's methods and see "this one is on the superclass, this one is on module A, this one is on the class and overrides the superclass," etc?

+3  A: 

Get the appropriate Method (or UnboundMethod) object and ask for its owner. So you could do method(:puts).owner and get Kernel.

Chuck
+5  A: 

Object#method returns a Method object giving meta-data about a given method. For example:

> [].method(:length).inspect
=> "#<Method: Array#length>"
> [].method(:max).inspect
=> "#<Method: Array(Enumerable)#max>"

In Ruby 1.8.7 and later, you can use Method#owner to determine the class or module that defined the method.

To get a list of all the methods with the name of the class or module where they are defined you could do something like the following:

obj.methods.collect {|m| "#{m} defined by #{obj.method(m).owner}"}
Phil Ross
you can get the #owner method in 1.8.6 by using the backports gem.
rogerdpack
+1  A: 

You can use Object#method. For example,

[1, 2, 3].method(:each_cons) # => #<Method: Array(Enumerable)#each_cons>

tells that the each_cons method of an Array comes from the Enumerable module.

sluukkonen
+1  A: 

To find which instance methods are defined on A (but not on superclasses):

A.methods(false)

To find which instance methods are defined on A AND its superclasses:

A.methods

To find which class (or module) a given method is defined on:

method(:my_method).owner

To find which object is the receiver for a given method:

method(:my_method).receiver
banister
To get specific types of methods: someobject.private_methods, someobject.public_methods, someobject.protected_methods, someobject.singleton_methods, SomeClass.instance_methods, SomeClass.private_instance_methods, SomeClass.protected_instance_methods, SomeClass.public_instance_methods (which is the same as just .instance_methods).
Nathan Long