it-swarm.com.de

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

Wir hatten kürzlich ein Problem, bei dem nach einer Reihe von Commits ein Backend-Prozess nicht ausgeführt werden konnte. Nun, wir waren gute kleine Jungs und Mädchen und liefen nach jedem Einchecken rake test, Aber aufgrund einiger Kuriositäten beim Laden der Rails-Bibliothek nur, wenn wir es direkt von Mongrel im Produktionsmodus aus liefen.

Ich habe den Fehler aufgespürt und es lag an einem neuen Rails gem, das eine Methode in der String-Klasse auf eine Weise überschrieb, die eine enge Verwendung in der Laufzeit unterbrach Rails = Code.

Wie auch immer, um es kurz zu machen, gibt es eine Möglichkeit, zur Laufzeit zu fragen, Ruby wo eine Methode definiert wurde? Etwas wie whereami( :foo ), das /path/to/some/file.rb line #45 Zurückgibt. In diesem Fall wäre es nicht hilfreich, mir mitzuteilen, dass es in der Klasse String definiert wurde, da es von einer Bibliothek überladen wurde.

Ich kann nicht garantieren, dass die Quelle in meinem Projekt lebt. Wenn ich nach 'def foo' Greife, bekomme ich nicht unbedingt das, was ich brauche, ganz zu schweigen von vielendef foo ' s, manchmal weiß ich erst zur Laufzeit, welche ich verwenden kann.

324
Matt Rogish

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

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>

Wenn Sie Ruby 1.9+ verwenden, können Sie 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]

Beachten Sie, dass dies nicht bei allen Anwendungen funktioniert, z. B. bei systemeigenem kompiliertem Code. Die Methodenklasse hat auch einige nette Funktionen, wie Methodeneigentümer , die die Datei zurückliefern, in der die Methode definiert ist.

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

410
wesgarrison

Sie können tatsächlich ein bisschen weiter gehen als die Lösung oben. Für Ruby 1.8 Enterprise Edition gibt es das __file__ und __line__ Methoden für Method Instanzen:

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

Für Ruby 1.9 und höher gibt es source_location (danke 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]
81
James Adam

Ich komme zu spät zu diesem Thread und bin überrascht, dass niemand Method#owner Erwähnt hat.

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

Durch Kopieren meiner Antwort von einer neueren ähnliche Frage werden diesem Problem neue Informationen hinzugefügt.

Ruby 1.9 hat eine Methode namens source_location :

Gibt den Ruby Quelldateinamen und die Zeilennummer zurück, die diese Methode enthalten, oder nil, wenn diese Methode nicht in Ruby (d. H. Native) definiert wurde.)

Dies wurde von diesem Juwel auf 1.8.7 zurückportiert:

So können Sie nach der Methode fragen:

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

Und dann fragen Sie nach dem source_location Dieser Methode:

m.source_location

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

ActiveRecord::Base.method(:validates).source_location
# => ["/Users/laas/.rvm/gems/[email protected]/gems/activemodel-3.2.2/lib/active_model/validations/validates.rb", 81]

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

In Aktion:

where_is(ActiveRecord::Base, :validates)

# => ["/Users/laas/.rvm/gems/[email protected]/gems/activemodel-3.2.2/lib/active_model/validations/validates.rb", 81]

Auf Macs mit installiertem TextMate wird hierdurch auch der Editor am angegebenen Speicherort geöffnet.

12
Laas

Dies kann helfen, aber Sie müssten es selbst codieren. Aus dem Blog eingefügt:

Ruby bietet einen method_added () - Rückruf, der jedes Mal aufgerufen wird, wenn eine Methode innerhalb einer Klasse hinzugefügt oder neu definiert wird. Es ist Teil der Modulklasse und jede Klasse ist ein Modul. Es gibt auch zwei verwandte Rückrufe mit den Namen method_removed () und method_undefined ().

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

6
Ken

Wenn Sie die Methode zum Absturz bringen können, erhalten Sie eine Rückverfolgung, die Ihnen genau sagt, wo sie sich befindet.

Wenn Sie es nicht zum Absturz bringen können, können Sie leider nicht herausfinden, wo es definiert wurde. Wenn Sie versuchen, mit der Methode einen Affen zu erzeugen, indem Sie sie überschreiben oder überschreiben, wird ein Absturz durch Ihre überschriebene oder überschriebene Methode verursacht und kann nicht verwendet werden.

Nützliche Methoden zum Abstürzen:

  1. Übergeben Sie nil, wo es das verbietet - in den meisten Fällen wird durch die Methode ArgumentError oder das allgegenwärtige NoMethodError für eine Nullklasse ausgelöst.
  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 erhöhen.
5
Orion Edwards

Vielleicht die #source_location kann helfen, herauszufinden, woher die Methode kommt.

ex:

ModelName.method(:has_one).source_location

Rückkehr

[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

Rückkehr

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

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
}
# call your method
set_trace_func nil
3
tig

Mit caller() können Sie immer zurückverfolgen, wo Sie sich gerade befinden.

2
Garry

Möglicherweise können Sie Folgendes tun:

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

Stellen Sie dann sicher, dass foo_Finder zuerst mit so etwas wie geladen wird

Ruby -r foo_Finder.rb railsapp

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

Dies zeigt Ihnen alle Neudefinitionen von String # foo. Mit ein wenig Meta-Programmierung können Sie es für jede gewünschte Funktion verallgemeinern. Es muss jedoch VOR der Datei geladen werden, die tatsächlich die Neudefinition vornimmt.

2
AShelly