357 Stimmen

Wie finde ich zur Laufzeit heraus, wo eine Methode definiert ist?

Kürzlich hatten wir ein Problem, bei dem nach einer Reihe von Commits ein Backend-Prozess nicht lief. Wir waren gute Jungs und Mädchen und führten nach jedem Check-In rake test aus, aber aufgrund einiger Eigenheiten beim Laden von Rails-Bibliotheken trat der Fehler nur auf, wenn wir es direkt von Mongrel im Produktionsmodus ausführten.

Ich habe den Bug verfolgt und es lag daran, dass ein neues Rails-Gem eine Methode in der String-Klasse überschrieben hat, was eine schmale Verwendung im Laufzeit-Rails-Code gebrochen hat.

Auf jeden Fall, lange Rede, kurzer Sinn, gibt es eine Möglichkeit, Ruby zur Laufzeit zu fragen, wo eine Methode definiert wurde? Etwas wie whereami(:foo), das /pfad/zu/einer/datei.rb Zeile #45 zurückgibt? In diesem Fall wäre es nicht hilfreich, mir zu sagen, dass es in der Klasse String definiert wurde, weil es von einer Bibliothek überladen wurde.

Ich kann nicht garantieren, dass die Quelle in meinem Projekt existiert, also wird das Suchen nach 'def foo' nicht unbedingt das liefern, was ich brauche, ganz zu schweigen davon, wenn ich viele def foos habe, manchmal weiß ich erst zur Laufzeit, welche ich benutze.

1 Stimmen

In Ruby 1.8.7 wurde eine spezielle Methode hinzugefügt, um diese Informationen speziell zu finden (und sie ist immer noch in 1.9.3 vorhanden) ... Details in meiner Antwort unten.

0 Stimmen

Vor langer Zeit habe ich einen Artikel geschrieben, der diese Fragen beantwortet blog.widefix.com/find-method-source-location

7voto

Ken Punkte 2064

Dies könnte helfen, aber Sie müssten es selbst codieren. Eingefügt aus dem Blog:

Ruby bietet einen method_added() Callback, der jedes Mal aufgerufen wird, wenn eine Methode innerhalb einer Klasse hinzugefügt oder neu definiert wird. Es gehört zur Modulklasse, und jede Klasse ist ein Modul. Es gibt auch zwei verwandte Callbacks namens method_removed() und method_undefined().

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

7voto

Orion Edwards Punkte 117361

Wenn Sie die Methode zum Absturz bringen können, erhalten Sie einen Backtrace, der Ihnen genau sagt, wo sie sich befindet.

Leider, wenn Sie es nicht zum Absturz bringen können, können Sie nicht herausfinden, wo es definiert wurde. Wenn Sie versuchen, mit der Methode zu experimentieren, indem Sie sie überschreiben oder außer Kraft setzen, wird jeder Absturz von Ihrer überschriebenen oder außer Kraft gesetzten Methode stammen, und es wird nicht hilfreich sein.

Nützliche Möglichkeiten, Methoden zum Absturz zu bringen:

  1. Geben Sie nil an, wo es verboten ist - oft löst die Methode eine ArgumentError oder den allgegenwärtigen NoMethodError in einer nil-Klasse aus.
  2. Wenn Sie Insiderwissen über die Methode haben und wissen, dass die Methode wiederum eine andere Methode aufruft, können Sie die andere Methode überschreiben und innerhalb dieser Methode einen Fehler auslösen.

0 Stimmen

Wenn Sie Zugriff auf den Code haben, können Sie genauso einfach require 'ruby-debug'; debugger in Ihrem Code einfügen, wo Sie möchten.

0 Stimmen

"Leider stimmt es nicht, dass man herausfinden kann, wo es definiert wurde, wenn man es nicht zum Absturz bringen kann." Siehe andere Antworten.

4voto

tig Punkte 23120

Sehr späte Antwort :) Aber frühere Antworten haben mir nicht geholfen

set_trace_func proc{ |event, file, line, id, binding, classname|
  printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
}
# rufen Sie Ihre Methode auf
set_trace_func nil

0 Stimmen

Warum übergibst du nil?

0 Stimmen

@ArupRakshit von der Dokumentation: "Etabliert proc als den Handler für das Tracing oder deaktiviert das Tracing, wenn der Parameter nil ist."

3voto

AShelly Punkte 33678

Vielleicht können Sie etwas Ähnliches wie folgt tun:

foo_finder.rb:

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

Dann stellen Sie sicher, dass foo_finder zuerst geladen wird, z.B. mit

ruby -r foo_finder.rb railsapp

(Ich habe nur mit Rails herumgespielt, also weiß ich es nicht genau, aber ich stelle mir vor, dass es eine Möglichkeit gibt, es so ähnlich zu starten.)

Dies zeigt Ihnen alle Neudefinitionen von String#foo. Mit etwas Metaprogrammierung könnten Sie es für jede gewünschte Funktion verallgemeinern. Es muss jedoch VOR der Datei geladen werden, die tatsächlich die Neudefinition durchführt.

3voto

Sie können jederzeit einen Backtrace Ihres Standorts erhalten, indem Sie caller() verwenden.

1 Stimmen

Dies ist nützlich, um herauszufinden, was dich aufgerufen hat, aber nicht gut, wenn du versuchst herauszufinden, wo etwas definiert wurde.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X