eric2323223
eric2323223

Reputation: 3628

How to determine the class from which a specified method originated?

I got this question from this discussion. A method call like object.m does not always mean the class of "object" has a "m" method, just like the find method to a Array object is not directly originated from Array object, but from the mixed-in Enumerable module. My question is, given a method, how can we determine the class from which the method originated?

Upvotes: 7

Views: 380

Answers (5)

ben_h
ben_h

Reputation: 4704

tobyhede's answer is awesome, but I just did a bit of digging in irb and there's no need to slice up the output of #inspect. The Method class

>> Object.new.method(:inspect)
=> #<Method: Object(Kernel)#inspect>

has some useful methods of its own:

>> Object.new.method(:inspect).methods - Object.methods
=> ["owner", "call", "to_proc", "unbind", "arity", "receiver", "[]"]

In particular the #owner method, which returns the owner as a proper object:

>> [].method(:count).owner
=> Array
>> [].method(:detect).owner
=> Enumerable

Upvotes: 7

Toby Hede
Toby Hede

Reputation: 37143

Any class/object method is an object in Ruby, and has some methods of it's own.

So you can do this:

[].method(:count).inspect
=> "#<Method: Array#count>"

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

Quick bit of RegEx and you're done.

Upvotes: 10

Keltia
Keltia

Reputation: 14743

I'm not sure we can precisely find where a method come from, when you include a mixin, all the methods become part of your class as if you did put them there. See answer from dylanfm for an approx.

Upvotes: 0

dylanfm
dylanfm

Reputation: 6345

I'm thinking something like this could work

def print_ancestor_definitions(cl,method)
  ancestors = cl.ancestors
  p ancestors.join(' < ') #Print heirarchy
  p "Searching..."
  ancestors.each do |c|
    if c.instance_methods.include? method
      p "#{c} defines #{method} as an instance method!"
    elsif c.singleton_methods.include? method
      p "#{c} defines #{method} as a singleton method"
    else
      p "#{c} doesn't define #{method}"
    end
  end
end

print_ancestor_definitions(Array,'find')
# >> "Array < Enumerable < Object < Kernel"
# >> "Searching..."
# >> "Array defines find as an instance method!"
# >> "Enumerable defines find as an instance method!"
# >> "Object doesn't define find"
# >> "Kernel doesn't define find"

I suppose the last one to have the method is the one who defines it?

Upvotes: 1

Greg Fairbrother
Greg Fairbrother

Reputation: 1041

maybe you use caller() to give you the backtrace see:

http://www.ruby-doc.org/core/classes/Kernel.html#M005955

Upvotes: 1

Related Questions