Wie zu finden, wo ein Verfahren zur Laufzeit festgelegt ist?
-
05-07-2019 - |
Frage
Wir hatten vor kurzem ein Problem, wo, nach einer Reihe von Commits stattgefunden hatte, ein Back-End-Prozess ausgeführt fehlgeschlagen. Nun, wir waren gute kleine Jungen und Mädchen und lief rake test
nach jedem Check-in, aber aufgrund einiger Merkwürdigkeiten in Rails' Bibliothek Laden, es trat nur, wenn wir es lief direkt von Mongrel im Produktionsmodus.
raupen ich den Fehler und es war aufgrund einer neuen Rails gem eine Methode in der String-Klasse in einer Art und Weise zu überschreiben, die eine schmale Verwendung in der Runtime-Rails-Code brach.
Wie auch immer, lange Geschichte kurz, gibt es eine Möglichkeit, zur Laufzeit, zu fragen, Rubin, wo eine Methode definiert wurde? So etwas wie whereami( :foo )
die /path/to/some/file.rb line #45
zurückkehrt? In diesem Fall sagt mir, dass es in der Klasse String definiert wurde wäre nicht hilfreich, weil es durch eine Bibliothek überlastet war.
Ich kann nicht garantieren, dass die Quelle lebt in meinem Projekt wird so greppen für 'def foo'
nicht unbedingt mir geben, was ich brauche, nicht zu erwähnen, wenn ich viele def foo
ist, manchmal weiß ich nicht, bis Laufzeit, welche ich kann mit Hilfe sein.
Lösung
Das ist wirklich zu spät, aber hier ist, wie Sie feststellen können, wo eine Methode definiert ist:
# 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+ sind, können Sie a href verwenden <= "http://www.ruby-doc.org/core-1.9.3/Method.html#method-i-source_location" rel = "noreferrer"> 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 alles funktioniert, wie native kompilierten Code. Die Methode Klasse hat einige nette Funktionen, auch, wie Methode # Besitzer , die die Datei zurückgibt, wo die Methode definiert ist.
EDIT: Auch die __file__
und __line__
und Hinweise für REE in der anderen Antwort sehen, sie sind auch nützlich. - wg
Andere Tipps
Sie können tatsächlich ein wenig weiter als die Lösung oben gehen. Für Ruby 1.8 Enterprise Edition, gibt es die __file__
und __line__
Methoden auf 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 darüber hinaus gibt es source_location
(dank 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]
Ich komme spät auf diesen Thread und bin überrascht, dass niemand Method#owner
erwähnt.
class A; def hello; puts "hello"; end end
class B < A; end
b = B.new
b.method(:hello).owner
=> A
Kopieren meiner Antwort von einer neueren ähnlicher Frage , die neuen Informationen ergänzt dieses Problem.
Rubin 1.9 hat Methode namens Quellpfad :
Gibt die Ruby-Quelldateinamen und die Zeilennummer diese Methode oder gleich Null enthalten, wenn diese Methode nicht in Ruby (das heißt nativ) definiert wurde
Dies wurde zurück portiert auf 1.8.7 dieses Juwel:
Sie können also für das Verfahren anfordern:
m = Foo::Bar.method(:create)
Und dann fragen Sie nach dem source_location
dieser Methode:
m.source_location
Dies wird ein Array mit Dateinamen und die Zeilennummer zurück.
Z. B für ActiveRecord::Base#validates
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 die Klassen und Module, ist Rubin nicht eingebaute Unterstützung bieten, aber es ist ein ausgezeichnetes Gist aus, dass es auf source_location
baut Datei für eine bestimmte Methode oder die ersten Datei für eine Klasse zurück, 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 Textmate installiert, das auch den Editor an der angegebenen Stelle erscheint.
Dies kann helfen, aber sie würde es selbst codieren. Geklebte aus dem Blog:
Rubin bietet eine method_added () Rückruf, der jedes Mal, wenn ein aufgerufen wird Verfahren hinzugefügt wird oder in einem neu definiert Klasse. Es ist ein Teil der Modulklasse, und jede Klasse ist ein Modul. Es gibt auch zwei verwandte Rückrufe genannt method_removed () und (Method_undefined).
http://scie.nti.st / 2008/9/17 / Making-Methoden-unveränderlich-in-ruby
Wenn Sie die Methode zum Absturz bringen können, erhalten Sie eine Rückverfolgung erhalten, die Ihnen genau sagen, wo es ist.
Leider, wenn Sie es nicht zum Absturz bringen können, dann können Sie nicht herausfinden, wo es definiert wurde. Wenn Sie mit dem Verfahren zum Affen versuchen, indem sie sie überschreiben oder überschreiben sie, dann wird jeder Absturz kommt aus Ihrer überschrieben oder überschriebene Methode, und es wird keine Verwendung sein.
Praktische Wege, Methoden Absturz:
- Pass
nil
wo sie es verbietet, -. Eine Menge Zeit die Methode eineArgumentError
erhöhen wird oder die allgegenwärtigeNoMethodError
auf einer Null-Klasse - Wenn Sie in Kenntnis des Verfahrens haben, und Sie wissen, dass das Verfahren wiederum eine andere Methode aufruft, dann können Sie die andere Methode overrwrite, und im Inneren, dass erhöhen.
Vielleicht kann der #source_location
zu finden, in denen das Verfahren kommen.
ex:
ModelName.method(:has_one).source_location
Zurück
[project_path/vendor/ruby/version_number/gems/activerecord-number/lib/active_record/associations.rb", line_number_of_where_method_is]
oder
ModelName.new.method(:valid?).source_location
Zurück
[project_path/vendor/ruby/version_number/gems/activerecord-number/lib/active_record/validations.rb", line_number_of_where_method_is]
Sehr spät Antwort :) Aber früher Antworten haben mir nicht helfen
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
Sie könnten in der Lage sein, so etwas zu 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
Dann gewährleisten foo_finder zuerst geladen wird mit so etwas wie
ruby -r foo_finder.rb railsapp
(Ich habe nur mit Schienen durcheinander, so dass ich weiß nicht genau, aber ich glaube, es gibt einen Weg, es zu starten Art wie diese.)
Dies zeigt Ihnen alle Re-Definitionen von String # foo. Mit einer wenig Meta-Programmierung, können Sie es für verallgemeinern, was auch immer gewünschte Funktion. Aber es braucht, bevor die Datei geladen werden, die tatsächlich funktioniert die Neudefinition.
Sie können immer einen Backtrace von wo Sie sind von caller()
verwendet wird.