Nous avons récemment eu un problème où, après une série de commits, un processus back-end a échoué. Maintenant, nous étions de bons petits garçons et filles et nous exécutions rake test
Après chaque enregistrement, mais, en raison de certaines bizarreries dans le chargement de la bibliothèque de Rails, cela ne s'est produit que lorsque nous l'avons exécuté directement de Mongrel en mode production.
J'ai suivi le bogue et cela était dû à un nouveau Rails gem écrasant une méthode de la classe String de manière à rompre une utilisation étroite à l'exécution Rails = code.
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 retourne /path/to/some/file.rb line #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 la source des données dans mon projet, donc grepping pour 'def foo'
Ne me donnera pas nécessairement ce dont j'ai besoin, sans mentionner si j'ai plusieursdef foo
' s, parfois je ne sais pas jusqu'au moment de l'exécution de celle que je pourrais utiliser.
C'est vraiment tard, mais voici comment vous pouvez trouver où une méthode est définie:
# 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 êtes sur 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 natif compilé. Le Method class a aussi quelques fonctions intéressantes, comme Method # owner qui renvoie le fichier où la méthode est définie.
EDIT: Voir aussi le __file__
et __line__
et notes pour REE dans l’autre réponse, ils sont également pratiques. - wg
Vous pouvez réellement aller un peu plus loin que la solution ci-dessus. Pour Ruby 1.8 Enterprise Edition, il y a le __file__
et __line__
méthodes sur les instances 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
Pour Ruby 1.9 et au-delà, 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]
J'arrive en retard sur ce fil, et je suis surpris que personne ne mentionne Method#owner
.
class A; def hello; puts "hello"; end end
class B < A; end
b = B.new
b.method(:hello).owner
=> A
Copier ma réponse d’un plus récent question similaire qui ajoute de nouvelles informations à ce problème.
Ruby 1.9 a une méthode appelée emplacement_source :
Renvoie le 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 natif)
Cela a été rétroporté à 1.8.7 par cette gemme:
Vous pouvez donc demander la méthode:
m = Foo::Bar.method(:create)
Et ensuite demander le source_location
de cette méthode:
m.source_location
Cela retournera un tableau avec nom de fichier et numéro de ligne. E.g pour ActiveRecord::Base#validates
ceci retourne:
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 source_location
pour retourner le fichier d’une méthode donnée ou le premier fichier d’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 Mac sur lesquels TextMate est installé, l’éditeur s’affiche également à l’emplacement spécifié.
Cela peut aider, mais vous devrez le coder vous-même. Collé du blog:
Ruby fournit un rappel method_added () qui est appelé chaque fois qu'une méthode est ajoutée ou redéfinie dans une classe. Il fait partie de la classe Module et chaque classe est un module. Il existe également deux rappels associé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 écrasant ou en la surchargeant, alors tout blocage proviendra de votre méthode écrasée ou surchargée et ne sera d'aucune utilisation.
Moyens utiles de planter des méthodes:
nil
là où elle l’interdit - la plupart du temps, la méthode lève un ArgumentError
ou le NoMethodError
toujours présent sur une classe nil.Peut-être le #source_location
peut aider à trouver d'où vient la méthode.
ex:
ModelName.method(:has_one).source_location
Revenir
[project_path/vendor/Ruby/version_number/gems/activerecord-number/lib/active_record/associations.rb", line_number_of_where_method_is]
OR
ModelName.new.method(:valid?).source_location
Revenir
[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 pouvez toujours obtenir une trace de votre position en utilisant caller()
.
Vous pourriez peut-être faire quelque chose comme ça:
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 seulement contenté de Rails, donc je ne sais pas exactement, mais j'imagine qu'il y a un moyen de commencer comme ça.)
Cela 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.