¿Cómo encontrar dónde se define un método en tiempo de ejecución?
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 test
despué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.
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
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 Method
las 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]
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
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_location
de 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#validates
esto 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_location
devolver 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.