¿Cómo encontrar dónde se define un método en tiempo de ejecución?

Resuelto Matt Rogish asked hace 16 años • 11 respuestas

Recientemente tuvimos un problema en el que, después de que se produjeron una serie de confirmaciones, no se pudo ejecutar un proceso de backend. Ahora, éramos buenos niños y niñas y corríamos rake testdespués de cada check-in pero, debido a algunas rarezas en la carga de la biblioteca de Rails, solo ocurrió cuando lo ejecutamos directamente desde Mongrel en modo de producción.

Rastreé el error y se debió a que una nueva gema Rails sobrescribía un método en la clase String de una manera que rompía un uso limitado en el código Rails en tiempo de ejecución.

De todos modos, para resumir, ¿hay alguna manera, en tiempo de ejecución, de preguntarle a Ruby dónde se ha definido un método? ¿ Algo whereami( :foo )así vuelve /path/to/some/file.rb line #45? En este caso, decirme que fue definido en la clase String sería inútil, porque estaba sobrecargado por alguna biblioteca.

No puedo garantizar que la fuente esté en mi proyecto, por lo que buscar 'def foo'no necesariamente me dará lo que necesito, sin mencionar si tengo muchas def foo , a veces no sé hasta el tiempo de ejecución cuál puedo estar usando.

Matt Rogish avatar Oct 07 '08 01:10 Matt Rogish
Aceptado

Esto es muy tarde, pero así es como puedes encontrar dónde está definido un método:

http://gist.github.com/76951

# How to find out where a method comes from.
# Learned this from Dave Thomas while teaching Advanced Ruby Studio
# Makes the case for separating method definitions into
# modules, especially when enhancing built-in classes.
module Perpetrator
  def crime
  end
end

class Fixnum
  include Perpetrator
end

p 2.method(:crime) # The "2" here is an instance of Fixnum.
#<Method: Fixnum(Perpetrator)#crime>

Si estás en Ruby 1.9+, puedes usarsource_location

require 'csv'

p CSV.new('string').method(:flock)
# => #<Method: CSV#flock>

CSV.new('string').method(:flock).source_location
# => ["/path/to/ruby/1.9.2-p290/lib/ruby/1.9.1/forwardable.rb", 180]

Tenga en cuenta que esto no funcionará en todo, como el código compilado nativo. La clase Método también tiene algunas funciones interesantes, como Método#propietario que devuelve el archivo donde se define el método.

EDITAR: consulte también las notas __file__y __line__para REE en la otra respuesta, también son útiles. -- wg

wesgarrison avatar Mar 18 '2009 21:03 wesgarrison

De hecho, puedes ir un poco más allá de la solución anterior. Para Ruby 1.8 Enterprise Edition, existen los métodos __file__y __line__en Methodlas instancias:

require 'rubygems'
require 'activesupport'

m = 2.days.method(:ago)
# => #<Method: Fixnum(ActiveSupport::CoreExtensions::Numeric::Time)#ago>

m.__file__
# => "/Users/james/.rvm/gems/ree-1.8.7-2010.01/gems/activesupport-2.3.8/lib/active_support/core_ext/numeric/time.rb"
m.__line__
# => 64

Para Ruby 1.9 y posteriores, existe source_location(¡gracias Jonathan!):

require 'active_support/all'
m = 2.days.method(:ago)
# => #<Method: Fixnum(Numeric)#ago>    # comes from the Numeric module

m.source_location   # show file and line
# => ["/var/lib/gems/1.9.1/gems/activesupport-3.0.6/.../numeric/time.rb", 63]
James Adam avatar Aug 25 '2010 09:08 James Adam

Llegué tarde a este hilo y me sorprende que nadie lo haya mencionado Method#owner.

class A; def hello; puts "hello"; end end
class B < A; end
b = B.new
b.method(:hello).owner
=> A
Alex D avatar Feb 20 '2012 04:02 Alex D

Copiando mi respuesta de una pregunta similar más reciente que agrega nueva información a este problema.

Ruby 1.9 tiene un método llamado source_location :

Devuelve el nombre del archivo fuente de Ruby y el número de línea que contiene este método o nulo si este método no se definió en Ruby (es decir, nativo)

Esta gema ha respaldado esto a 1.8.7 :

  • ubicación_fuente_ruby18

Entonces puedes solicitar el método:

m = Foo::Bar.method(:create)

Y luego pregunte por el source_locationde ese método:

m.source_location

Esto devolverá una matriz con el nombre del archivo y el número de línea. Por ejemplo, para ActiveRecord::Base#validatesesto regresa:

ActiveRecord::Base.method(:validates).source_location
# => ["/Users/laas/.rvm/gems/ruby-1.9.2-p0@arveaurik/gems/activemodel-3.2.2/lib/active_model/validations/validates.rb", 81]

Para clases y módulos, Ruby no ofrece soporte integrado, pero existe un Gist excelente que se basa en source_locationdevolver el archivo para un método determinado o el primer archivo para una clase si no se especificó ningún método:

  • módulo rubí donde_está

En acción:

where_is(ActiveRecord::Base, :validates)

# => ["/Users/laas/.rvm/gems/ruby-1.9.2-p0@arveaurik/gems/activemodel-3.2.2/lib/active_model/validations/validates.rb", 81]

En Mac con TextMate instalado, esto también muestra el editor en la ubicación especificada.

Laas avatar Oct 22 '2012 16:10 Laas