Question

Nous avons récemment rencontré un problème: après l'exécution d'une série de validations, l'exécution d'un processus back-end a échoué. Maintenant, nous étions de bons garçons et de petites filles et avons exécuté rake test après chaque enregistrement, mais, en raison des particularités du chargement de la bibliothèque de Rails, cela ne s'est produit que lorsque nous l'avons exécuté directement depuis Mongrel en mode de production. .

J'ai suivi le bogue et cela était dû à une nouvelle gemme Rails qui avait écrasé une méthode de la classe String de manière à rompre une utilisation limitée dans le code d'exécution Rails.

Quoi qu'il en soit, longue histoire courte, y at-il un moyen, à 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 ligne n ° 45 ? Dans ce cas, me dire qu'il était défini dans la classe String ne serait pas utile, car il était surchargé par une bibliothèque.

Je ne peux pas garantir que la source vit dans mon projet, donc Grepping pour 'def foo' ne me donnera pas nécessairement ce dont j'ai besoin, sans parler de mes nombreuses def foo , parfois, je ne sais pas avant l'exécution lequel je peux utiliser.

Était-ce utile?

La solution

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) # The "2" here is an instance of Fixnum.
#<Method: Fixnum(Perpetrator)#crime>

Si vous utilisez Ruby 1.9+, vous pouvez utiliser emplacement_source

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. La la classe de la méthode a également quelques fonctions intéressantes, comme Méthode # propriétaire qui renvoie le fichier où le la méthode est définie.

EDIT: Voir aussi le __ fichier __ et le __ ligne __ et les notes relatives à l'ERE dans l'autre réponse, elles sont également utiles. - wg

Autres conseils

Vous pouvez réellement aller un peu plus loin que la solution ci-dessus. Pour Ruby 1.8 Enterprise Edition, il existe les méthodes __ fichier __ et __ ligne __ sur les instances Méthode :

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 et les versions ultérieures, il existe emplacement_source (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]

J'arrive en retard sur ce fil de discussion et je suis surpris que personne n'ait mentionné la Méthode # propriétaire .

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

Copie de ma réponse à partir d’une nouvelle question similaire qui ajoute de nouvelles informations à ce problème.

Ruby 1.9 utilise une méthode appelée emplacement_source :

  

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

Cela a été rétroporté à 1.8.7 par ce bijou:

Vous pouvez donc demander la méthode:

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

Et demandez ensuite la localisation_source de cette méthode:

m.source_location

Ceci retournera un tableau avec le nom de fichier et le numéro de ligne. E.g pour ActiveRecord :: Base # valide et renvoie:

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 emplacement_source pour renvoyer le fichier d'une méthode donnée ou le premier fichier d'une classe si aucun la méthode 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 Mac sur lesquels TextMate est installé, cela permet également d'afficher l'éditeur à l'emplacement spécifié.

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

  

Ruby fournit un method_added ()   rappel qui est invoqué chaque fois qu'un   méthode est ajoutée ou redéfinie dans un   classe. Cela fait partie de la classe Module,   et chaque classe est un module. Il y a   aussi deux rappels liés appelés   method_removed () et   method_undefined ().

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

Si vous pouvez bloquer la méthode, vous obtiendrez une trace qui vous indiquera exactement où elle se trouve.

Malheureusement, si vous ne pouvez pas le bloquer, vous ne pouvez pas savoir où il a été défini. Si vous essayez de singer avec la méthode en la remplaçant ou en la redéfinissant, alors tout blocage proviendra de votre méthode remplacée ou surchargée et ne sera d'aucune utilisation.

Façons utiles d’imposer des méthodes:

  1. Passez nil là où cela est interdit - la méthode lève souvent un ArgumentError ou le toujours présent NoMethodError sur un classe nulle.
  2. Si vous avez une connaissance approfondie de la méthode et que vous savez que la méthode appelle à son tour une autre méthode, vous pouvez écraser cette méthode et la relancer à l'intérieur.

Peut-être que # emplacement_source peut aider à trouver d'où vient la méthode.

ex:

ModelName.method(:has_one).source_location

Retour

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

OU

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

Retour

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

Réponse très tardive :) Mais les réponses précédentes ne m'ont pas aidé

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

Vous pourriez peut-être faire quelque chose comme ceci:

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

Ensuite, assurez-vous que foo_finder est chargé en premier avec quelque chose comme

ruby -r foo_finder.rb railsapp

(Je me suis contenté de rails, alors je ne sais pas exactement, mais j'imagine qu'il y a un moyen de commencer un peu comme ça.)

Ceci vous montrera toutes les redéfinitions de String # foo. Avec un peu de méta-programmation, vous pouvez le généraliser pour la fonction de votre choix. Mais il doit être chargé AVANT le fichier qui fait la redéfinition.

Vous pouvez toujours obtenir une trace de votre position en utilisant caller () .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top