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

460voto

wesgarrison Punkte 6865

Dies ist wirklich spät, aber so können Sie herausfinden, wo eine Methode definiert ist:

http://gist.github.com/76951

# So finden Sie heraus, woher eine Methode stammt.
# Habe dies von Dave Thomas gelernt, während er den Advanced Ruby Studio unterrichtete
# Befürwortet die Trennung von Methodendefinitionen in
# Module, insbesondere bei der Erweiterung von Built-in-Klassen.
module Täter
  def verbrechen
  end
end

class Fixnum
  include Täter
end

p 2.method(:verbrechen) # Das "2" hier ist eine Instanz von Fixnum.
#

Wenn Sie Ruby 1.9+ verwenden, können Sie source_location verwenden

require 'csv'

p CSV.new('string').method(:flock)
# => #

CSV.new('string').method(:flock).source_location
# => ["/Pfad/zum/ruby/1.9.2-p290/lib/ruby/1.9.1/forwardable.rb", 180]

Beachten Sie, dass dies nicht bei allem funktioniert, z. B. bei nativ kompiliertem Code. Die Method-Klasse hat auch einige nützliche Funktionen, wie z. B. Method#owner, die die Datei zurückgibt, in der die Methode definiert ist.

EDIT: Siehe auch die __file__ und __line__ und Hinweise für REE in der anderen Antwort, sie sind auch nützlich. -- wg

1 Stimmen

Source_location scheint für 1.8.7-p334 unter Verwendung von activesupport-2.3.14 zu funktionieren

0 Stimmen

Nachdem Sie die Methode gefunden haben, versuchen Sie die Methode owner der Methode.

1 Stimmen

Was ist die Nummer zwei in 2.method(:crime)?

87voto

James Adam Punkte 1039

Sie können tatsächlich etwas weiter gehen als die oben genannte Lösung. Für Ruby 1.8 Enterprise Edition gibt es die Methoden __file__ und __line__ auf Method-Instanzen:

require 'rubygems'
require 'activesupport'

m = 2.days.method(: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

Für Ruby 1.9 und darüber hinaus gibt es source_location (danke Jonathan!):

require 'active_support/all'
m = 2.days.method(:ago)
# => #    # stammt aus dem Numeric-Modul

m.source_location   # Datei und Zeile anzeigen
# => ["/var/lib/gems/1.9.1/gems/activesupport-3.0.6/.../numeric/time.rb", 63]

2 Stimmen

Ich bekomme "NoMethodError: undefined method" für sowohl __file__ als auch __line__ auf einer beliebigen Method-Klasseninstanz, z.B. method(:method).__file__.

0 Stimmen

Welche Version von Ruby hast du?

0 Stimmen

Ruby 1.8.7 (2010-06-23 Patchlevel 299) [x86_64-Linux]

39voto

Alex D Punkte 28883

Ich komme spät in diesen Thread und bin überrascht, dass niemand Method#owner erwähnt hat.

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

2 Stimmen

Ich bin überrascht, dass du der Erste bist, der die Methodenklasse explizit erwähnt. Ein weiterer weniger bekannter Schatz, der in 1.9 eingeführt wurde: Method#parameters.

15voto

Laas Punkte 5840

Kopiere meine Antwort von einer neueren ähnlichen Frage, die neue Informationen zu diesem Problem hinzufügt.

Ruby 1.9 verfügt über eine Methode namens source_location:

Gibt den Dateinamen und die Zeilennummer des Ruby-Quellcodes zurück, die diese Methode enthalten, oder nil, wenn diese Methode nicht in Ruby definiert wurde (d.h. nativ)

Dies wurde in 1.8.7 durch dieses Gem zurückportiert:

Daher können Sie die Methode anfordern:

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

Und dann nach dem source_location dieser Methode fragen:

m.source_location

Dies gibt ein Array mit Dateiname und Zeilennummer zurück. Zum Beispiel für ActiveRecord::Base#validates gibt dies zurück:

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]

Für Klassen und Module bietet Ruby keine integrierte Unterstützung, aber es gibt einen ausgezeichneten Gist, der auf source_location aufbaut, um die Datei für eine bestimmte Methode zurückzugeben oder die erste Datei für eine Klasse zurückzugeben, wenn keine Methode angegeben wurde:

In Aktion:

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]

Auf Macs mit installiertem TextMate wird an dieser Stelle auch der Editor an der angegebenen Stelle geöffnet.

9voto

Samda Punkte 1269

Vielleicht kann #source_location helfen, um herauszufinden, woher die Methode stammt.

zum Beispiel:

ModelName.method(:has_one).source_location

Rückgabe

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

ODER

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

Rückgabe

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

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