Sunday, March 27, 2011

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?

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

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

  • 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?

  • 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.

    Toby Hede : method.inspect will tell you if a method is attached to a Mixin
  • 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.

    dylanfm : Sweet, thats great
  • 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
    
    bjeanes : awesome find, this will definitely come in handy.
    Toby Hede : of course! that's very cool.

0 comments:

Post a Comment