tutorial software rails last ejemplos descargar curso ruby-on-rails ruby runtime methods definition

ruby on rails - software - ¿Cómo encontrar dónde se define un método en tiempo de ejecución?



ruby on rails tutorial (10)

Recientemente tuvimos un problema donde, después de una serie de confirmaciones, no se pudo ejecutar un proceso de back-end. Ahora, éramos buenos niños y niñas y rake test después de cada registro 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 de Rails sobrescribía un método en la clase String de una manera que rompió un uso limitado en el código de Rails en tiempo de ejecución.

De todos modos, cuento, ¿hay alguna manera, en tiempo de ejecución, de preguntar a Ruby dónde se ha definido un método? Algo como whereami( :foo ) que devuelve /path/to/some/file.rb line #45 ? En este caso, decirme que se definió en la clase Cadena no sería útil, ya que estaba sobrecargado por alguna biblioteca.

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


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 de archivo de origen de Ruby y el número de línea que contiene este método o nil si este método no se definió en Ruby (es decir, nativo)

Esto ha sido backported a 1.8.7 por esta gema:

Así que puedes solicitar el método:

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

Y luego pregunte por la source_location de origen de ese método:

m.source_location

Esto devolverá una matriz con nombre de archivo y número de línea. Por ejemplo, para ActiveRecord::Base#validates esto devuelve:

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 las clases y los módulos, Ruby no ofrece soporte integrado, pero hay un excelente Gist por ahí que se basa en source_location para devolver el archivo para un método dado o el primer archivo para una clase si no se especificó ningún método:

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 abre el editor en la ubicación especificada.


En realidad, puede ir un poco más lejos que la solución anterior. Para Ruby 1.8 Enterprise Edition, existen los métodos __file__ y __line__ en las instancias de Method :

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 más allá, hay 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]


Es posible que puedas hacer algo como esto:

foo_finder.rb:

class String def String.method_added(name) if (name==:foo) puts "defining #{name} in:/n/t" puts caller.join("/n/t") end end end

Luego asegúrate de que foo_finder se carga primero con algo como

ruby -r foo_finder.rb railsapp

(Solo me he metido con los rieles, así que no sé exactamente, pero me imagino que hay una manera de comenzar de esta manera).

Esto te mostrará todas las re-definiciones de String # foo. Con un poco de meta-programación, podría generalizarlo para cualquier función que desee. Pero debe cargarse ANTES del archivo que realmente hace la redefinición.


Esto es realmente tarde, pero aquí es cómo puede encontrar dónde se define 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 usar source_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 claras, como Method#owner que devuelve el archivo donde se define el método.

EDITAR: También vea el __file__ y __line__ y las notas para REE en la otra respuesta, también son útiles. - wg


Esto puede ayudar, pero tendría que codificarlo usted mismo. Pegado desde el blog:

Ruby proporciona una devolución de llamada method_added () que se invoca cada vez que se agrega o se redefine un método dentro de una clase. Es parte de la clase Módulo, y cada Clase es un Módulo. También hay dos devoluciones de llamada relacionadas denominadas method_removed () y method_undefined ().

http://scie.nti.st/2008/9/17/making-methods-immutable-in-ruby


Llego tarde a este hilo y me sorprende que nadie haya mencionado al Method#owner .

class A; def hello; puts "hello"; end end class B < A; end b = B.new b.method(:hello).owner => A


Respuesta muy tardía :) Pero las respuestas anteriores no me ayudaron

set_trace_func proc{ |event, file, line, id, binding, classname| printf "%8s %s:%-2d %10s %8s/n", event, file, line, id, classname } # call your method set_trace_func nil


Si puedes bloquear el método, obtendrás un seguimiento que te dirá exactamente dónde está.

Desafortunadamente, si no puede bloquearlo, entonces no puede descubrir dónde se ha definido. Si intenta simularse con el método sobrescribiéndolo o anulándolo, entonces cualquier falla provendrá de su método sobrescrito o anulado, y no será de ninguna utilidad.

Formas útiles de chocar métodos:

  1. Pase nil donde lo prohíba: la NoMethodError de las veces, el método generará un ArgumentError o el siempre presente NoMethodError en una clase nula.
  2. Si tiene conocimiento interno del método, y sabe que el método a su vez llama a otro método, entonces puede sobrescribir el otro método y subir dentro de él.

Siempre puede obtener un seguimiento de dónde se encuentra utilizando caller() .


Tal vez la #source_location pueda ayudar a encontrar de dónde proviene el método.

ex:

ModelName.method(:has_one).source_location

Regreso

[project_path/vendor/ruby/version_number/gems/activerecord-number/lib/active_record/associations.rb", line_number_of_where_method_is]

O

ModelName.new.method(:valid?).source_location

Regreso

[project_path/vendor/ruby/version_number/gems/activerecord-number/lib/active_record/validations.rb", line_number_of_where_method_is]