Ruby – ¿Determinar los orígenes del método?

Cuando se envía un mensaje, un objeto Ruby busca si tiene un método con ese nombre para responder. Su búsqueda de método busca en el siguiente orden y usa el primer método que encuentra.

  1. Métodos singleton definidos en sí mismo (métodos aka en su “eigenclass”)
  2. Métodos definidos en su clase.
  3. Cualquier módulo mezclado en su clase, en orden inverso de inclusión (solo la primera inclusión de un módulo dado tiene algún efecto: si la superclase incluye el módulo A y la subclase lo incluye de nuevo, se ignora en la subclase; si la subclase incluye A luego B, luego A, se ignora la segunda A) ( actualización : tenga en cuenta que esto se escribió antes de que existiera Module.prepend )
  4. Su clase padre
  5. Cualquier método mezclado en la clase padre, el padre padre, etc.

O, para decirlo de manera más simple, se ve a sí mismo, luego todo en self.class.ancestors en el orden en que self.class.ancestors en la lista.

Esta ruta de búsqueda se sigue en el momento en que se llama al método; Si crea una instancia de una clase, luego vuelva a abrir la clase y agregue un método o mezcle uno a través de un módulo, la instancia existente obtendrá acceso a ese método.

Si todo esto falla, busca si tiene un método method_missing , o si su clase sí, su clase principal, etc.

Mi pregunta es la siguiente: además de examinar el código a mano, o usar métodos de ejemplo como puts "I'm on module A!" , ¿puedes decir de dónde viene un método dado? Por ejemplo, ¿puede enumerar los métodos de un objeto y ver “este está en la clase principal, este está en el módulo A, este está en la clase y reemplaza a la clase principal”, etc.?

Object#method devuelve un objeto Method que proporciona metadatos sobre un método determinado. Por ejemplo:

 > [].method(:length).inspect => "#" > [].method(:max).inspect => "#" 

En Ruby 1.8.7 y versiones posteriores, puede usar el Method#owner para determinar la clase o el módulo que definió el método.

Para obtener una lista de todos los métodos con el nombre de la clase o módulo donde están definidos, puede hacer algo como lo siguiente:

 obj.methods.collect {|m| "#{m} defined by #{obj.method(m).owner}"} 

Obtenga el objeto Método (o UnboundMethod) apropiado y pregunte por su owner . Así que podrías hacer el method(:puts).owner y obtener Kernel .

Para encontrar qué métodos de instancia están definidos en A (pero no en superclases):

 A.methods(false) 

Para encontrar qué métodos de instancia están definidos en A y sus superclases:

 A.methods 

Para encontrar en qué clase (o módulo) se define un método dado en:

 method(:my_method).owner 

Para encontrar qué objeto es el receptor para un método dado:

 method(:my_method).receiver 

Puedes usar Object#method . Por ejemplo,

 [1, 2, 3].method(:each_cons) # => # 

dice que el método each_cons de un Array proviene del módulo Enumerable.