web-dev-qa-db-fra.com

Obtenir l'emplacement de la classe à partir de l'objet de la classe

J'ai un tas de code à regarder, et maintenant c'est l'heure du débogage. Depuis que je n’ai jamais été fan du débogueur de Ruby, je cherche un moyen de lire le code et de le lire.

Ce que j'essaie de faire est d'obtenir l'emplacement du fichier où une classe chargée est définie:

Foo::Bar.create(:param) # how can I know file location in runtime?

Pour les projets plus petits et mieux organisés, je chercherais simplement class Bar, mais ce n’est pas possible dans la mesure où il existe de nombreuses classes nommées Bar et, pour aggraver les choses, certaines d’entre elles se trouvent sous le même espace de noms. Je sais, c'est la peine d'attendre pour arriver.

Note: J'utilise Ruby 1.8.7.

35
Haris Krajina

Pour Methods et Procs, 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'a pas été définie dans Ruby (c'est-à-dire natif).

Donc, vous pouvez demander la méthode:

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

Et demandez ensuite le source_location de cette méthode:

m.source_location

Cela retournera un tableau avec le nom de fichier et le 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]

Ruby n'offre pas de support intégré pour les classes et les modules, mais il existe un excellent Gist qui s'appuie sur source_location pour renvoyer 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:

EDIT: Pour Ruby 1.8.7, il existe une gemme qui rétablit les retours source_location:

29
Laas

Pour votre information, dans la console de Rails ou lors des sessions de débogage des applications Rails, vous pouvez connaître l'emplacement du disque où se trouve cette classe. comme

> show-source Job

cela vous donnera 

From: /home/john/projects/iisifix/app/models/job.rb @ line 13:
Class name: Job
Number of monkeypatches: 6. Use the `-a` option to display all available monkeypatches
Number of lines: 66

class Job < ApplicationRecord
  belongs_to :quote_request
  belongs_to :garage
13
illusionist

Voici un exemple simple montrant comment je dépiste des emplacements dans le code. Si j'ai besoin de connaître un emplacement dans un module:

class Foo
  attr_reader :initialize_loc
  def initialize
    @initialize_loc = [__FILE__, __LINE__]
    # do more stuff...
  end
end

Si j'ai besoin de savoir où quelque chose s'est passé:

require_relative 't1'

foo = Foo.new
# do lots of stuff until you want to know where something was initialized.
puts 'foo initialized at %s:%s' % foo.initialize_loc

Quand je lance le code, je reçois:

FooBar:Desktop foobar Ruby t2.rb 
foo initilized at /Users/foobar/Desktop/t1.rb:4

Si je ne veux pas jouer avec le code source du module et que le débogueur doit intervenir quand j'en ai besoin, je le laisserai faire:

require_relative 't1'
require 'Ruby-debug'

debugger
foo = Foo.new
# do lots of stuff until you want to know where something was initilized.
puts 'foo initilized at %s:%s' % foo.initialize_loc

L'exécution s'arrêtera et je passerai dans le débogueur à la ligne suivant immédiatement la variable debugger:

[0, 9] in t2.rb
  1  require_relative 't1'
  2  require 'Ruby-debug'
  3  
  4  debugger
=> 5  foo = Foo.new
  6  # do lots of stuff until you want to know where something was initilized.
  7  puts 'foo initilized at %s:%s' % foo.initialize_loc
  8  
t2.rb:5
foo = Foo.new
(rdb:1) 

Un simple s va "passer" avec moi dans la prochaine ligne de code, qui sera dans le bloc initialize pour Foo:

(rdb:1) s
[-1, 8] in /Users/foobar/Desktop/t1.rb
  1  class Foo
  2    attr_reader :initialize_loc
  3    def initialize
=> 4      @initialize_loc = [__FILE__, __LINE__]
  5      # do more stuff...
  6    end
  7  end
  8  
/Users/foobar/Desktop/t1.rb:4
@initialize_loc = [__FILE__, __LINE__]
(rdb:1) 

Au-delà, l'utilisation d'outils tels que grep -rn target_to_find path_to_search pour rechercher de manière récursive des répertoires et répertorier le nom du fichier et les numéros de ligne des lignes correspondant à la cible contribuera grandement à vous aider à trouver ce que vous recherchez.

Ou, en utilisant :vim /target_to_find/ path_to_search depuis Vim, vous obtiendrez les fichiers que vous recherchez.

3
the Tin Man

Mauvaises nouvelles! J'imagine qu'au moment de l'exécution, il est impossible de savoir quel fichier crée ou définit une classe dans Ruby 1.8.7.

Si le projet a une structure telle que Rails, vous pourrez le deviner.

Mais dans Ruby, plusieurs fichiers peuvent définir des méthodes pour la même classe La classe peut même être définie pendant l'exécution (métaprogrammation). 

Cela signifie qu'il peut y avoir plus d'un endroit où la classe est définie… .. Et ce que vous recherchez peut être réparti sur plusieurs fichiers.

Je suppose que vous devrez rechercher toutes les définitions de Bar et voir si elles sont dans le module Foo, ou commencer par rechercher toutes les définitions de Foo et vérifier ce qui est à l'intérieur . Si le code est en désordre, je ne vois pas manière simple, vous devrez suivre la forme de spaguetti en poi . Un bon éditeur et la recherche de plusieurs fichiers peuvent aider, mais vous devrez lire le code.

EDIT: Quelques bonnes nouvelles après tout. Dans Ruby 1.9, il y a source_location et on dirait qu'il existe un port arrière pour 1.8.7. Cependant, si la définition a été faite au moment de l'exécution par une évaluation, je ne sais pas si cela fonctionnera. Je pense que la solution la plus simple est un bon éditeur comme Rubymine qui peut généralement vous dire où le code a été défini.

0
Regedor
Klass.method(Klass.methods.first).source_location

En utilisant source_location pour une méthode, je peux rechercher la première méthode définie dans une classe. J'imagine que ce n'est pas infaillible à cause de la méta-programmation et d'autres hacks. 

0
shushugah

J'ai eu cette erreur en changeant une super-classe d'un objet et le correctif était d'arrêter et de démarrer le printemps.

0
bigtoe416

Franchement, étant donné l’organisation du code décrite, je pense que Ruby-debug est le moyen le plus simple de découvrir la destination de votre site d’appel: il suffit de définir un point d'arrêt et d'intervenir. Si vous êtes vraiment allergique au débogueur, vous pouvez instrumenter le site d'appel. avec Kernel#set_trace_func .

$max_trace = 10
set_trace_func proc { |event, file, line, id, binding, classname|
  printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
  $max_trace -= 1 
  set_trace_func(nil) unless $max_trace > 0
}
0
dbenhur