344 votes

Comment trouver l'endroit où une méthode est définie au moment de l'exécution ?

Nous avons récemment eu un problème où, après une série de commits, un processus backend ne s'est pas exécuté. Maintenant, nous avons été de bons petits garçons et filles et avons exécuté rake test après chaque check-in mais, en raison de certaines bizarreries dans le chargement des bibliothèques de Rails, cela ne se produisait que lorsque nous l'exécutions directement depuis Mongrel en mode production.

J'ai trouvé le bogue et il était dû à une nouvelle gemme Rails qui écrasait une méthode dans la classe String d'une manière qui cassait une utilisation étroite dans le code Rails d'exécution.

Bref, pour faire court, existe-t-il un moyen, au moment de l'exécution, de demander à Ruby où une méthode a été définie ? Quelque chose comme whereami( :foo ) qui renvoie /path/to/some/file.rb line #45 ? Dans ce cas, me dire qu'il a été défini dans la classe String ne serait pas utile, car il a été surchargé par une bibliothèque.

Je ne peux pas garantir que la source se trouve dans mon projet, donc rechercher 'def foo' ne me donnera pas forcément ce dont j'ai besoin, sans compter que si j'ai beaucoup de def foo Parfois, je ne sais pas jusqu'au moment de l'exécution quelle est celle que j'utilise.

441voto

wesgarrison Points 3675

C'est vraiment tard, mais voici comment vous pouvez trouver où une méthode est définie :

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)
#<Method: Fixnum(Perpetrator)#crime>

Si vous êtes sous Ruby 1.9+, vous pouvez utiliser 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]

Notez que cela ne fonctionnera pas sur tout, comme le code compilé natif. Le site Classe de méthode a aussi quelques fonctions intéressantes, comme Méthode#propriétaire qui renvoie le fichier où la méthode est définie.

EDIT : Voir aussi le __file__ y __line__ et les notes pour REE dans l'autre réponse, elles sont aussi très pratiques. -- wg

84voto

James Adam Points 799

Vous pouvez en fait aller un peu plus loin que la solution ci-dessus. Pour Ruby 1.8 Enterprise Edition, il existe la fonction __file__ y __line__ méthodes sur Method instances :

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

Pour Ruby 1.9, il y a source_location (merci 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]

39voto

Alex D Points 14591

J'arrive tardivement sur ce fil de discussion et je suis surpris que personne n'ait mentionné Method#owner .

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

15voto

Laas Points 2463

Je copie ma réponse d'une réponse plus récente question similaire qui ajoute de nouvelles informations à ce problème.

Ruby 1.9 a une méthode appelée source_location :

Renvoie le nom du fichier source Ruby et le numéro de ligne contenant cette méthode ou nil si cette méthode n'a pas été définie dans Ruby (c'est-à-dire en mode natif).

Ceci a été reporté à l'arrière dans 1.8.7 par ce bijou :

Vous pouvez donc demander la méthode :

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

Et puis, demandez le source_location de cette méthode :

m.source_location

Ceci retournera un tableau avec le nom du fichier et le numéro de ligne. Par exemple, pour ActiveRecord::Base#validates ce retour :

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]

Pour les classes et les modules, Ruby n'offre pas de support intégré, mais il existe un excellent Gist qui s'appuie sur les éléments suivants source_location pour retourner le fichier pour une méthode donnée ou le premier fichier pour une classe si aucune méthode n'a été spécifiée :

En action :

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]

Sur les Macs où TextMate est installé, cela fait également apparaître l'éditeur à l'emplacement spécifié.

7voto

Ken Points 1693

Cela peut vous aider mais vous devrez le coder vous-même. Collé du blog :

Ruby fournit une méthode_added() qui est invoquée à chaque fois qu'une méthode méthode est ajoutée ou redéfinie dans une classe. Elle fait partie de la classe Module, et chaque classe est un module. Il existe également deux callbacks associés appelés method_removed() et method_undefined().

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X