2008-10-06 6 views
290

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 Jungen und Mädchen und liefen rake test nach jedem Check-in, aber aufgrund einiger Seltsamkeiten in Rails 'Bibliotheksladen, es kam nur vor, wenn wir es direkt von Mongrel im Produktionsmodus ausgeführt.Wie finde ich heraus, wo eine Methode zur Laufzeit definiert ist?

Ich verfolgt den Fehler nach unten und es war aufgrund eines neuen Rails-Juwel, eine Methode in der String-Klasse in einer Weise zu überschreiben, die eine enge Verwendung im Laufzeit-Rails-Code brach.

Wie auch immer, kurz gesagt, gibt es einen Weg, Ruby zu fragen, 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 zu sagen, dass es in der Klasse String definiert wurde, weil es von einer Bibliothek überladen wurde.

ich die Quelle Leben in meinem Projekt nicht garantieren kann, wird so greppen für 'def foo' nicht unbedingt mir geben, was ich brauche, nicht zu erwähnen, wenn ich vieledef foo ‚s, manchmal erst zur Laufzeit Ich weiß nicht, was eine, die ich vielleicht benutze.

+1

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

Antwort

373

Das ist wirklich zu spät, aber hier ist, wie Sie feststellen können, wo ein Verfahren definiert:

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) 
#<Method: Fixnum(Perpetrator)#crime> 

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

Beachten Sie, dass dies nicht für alles funktioniert, wie nativen kompilierten Code. Die Method class hat auch einige nette Funktionen wie Method#owner, die die Datei zurückgibt, in der die Methode definiert ist.

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

+1

source_location scheint für 1.8.7-p334 zu funktionieren mit activesupport-2.3.14 – MaasSql

+0

Nachdem Sie die Methode gefunden haben, versuchen Sie die 'Eigentümer'-Methode von Method – Juguang

+0

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

6

Dies kann helfen, aber Sie müssten es selbst codieren. Geklebte aus dem Blog:

Ruby provides a method_added() callback that is invoked every time a method is added or redefined within a class. It’s part of the Module class, and every Class is a Module. There are also two related callbacks called method_removed() and method_undefined().

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

4

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 abstürzen können, können Sie nicht herausfinden, wo es definiert wurde. Wenn Sie versuchen, die Methode zu umgehen, indem Sie sie überschreiben oder überschreiben, kommt jeder Absturz von Ihrer überschriebenen oder überschriebenen Methode, und sie wird nicht verwendet.

Praktische Wege der Absturz Methoden:

  1. Pass nil wo sie es verbietet - ein großer Teil der Zeit die Methode eine ArgumentError oder die allgegenwärtige NoMethodError auf einer Null-Klasse erhöhen.
  2. Wenn Sie Insiderwissen über die Methode haben, und Sie wissen, dass die Methode wiederum eine andere Methode aufruft, können Sie die andere Methode überschreiben und innerhalb dieser erhöhen.
+0

Wenn Sie Zugriff auf den Code haben, können Sie einfach 'require 'ruby-debug' einfügen; Debugger "in Ihrem Code, in dem Sie ablegen möchten. –

2

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 foo_finder sicherzustellen, wird zunächst mit so etwas wie

ruby -r foo_finder.rb railsapp 

(I‘belastet Ich habe nur mit Schienen verstimmt, also weiß ich es nicht genau, aber ich kann mir vorstellen, dass es einen Weg gibt, es so zu starten.

Dies zeigt Ihnen alle Re-Definitionen von String # foo. Mit ein wenig Meta-Programmierung könnten Sie es für jede gewünschte Funktion verallgemeinern. Aber es muss vor der Datei geladen werden, die die Neudefinition tatsächlich durchführt.

2

Mit caller() können Sie immer ein Backtrace von wo Sie sind.

+0

Dies ist nützlich, um zu finden, was Sie anrief, aber nicht gut, wenn Sie versuchen, zu finden, wo etwas definiert wurde. –

3

Sehr spät Antwort :) Aber früher Antworten half mir nicht

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 
+0

Warum passierst du 'nil'? –

+0

@ArupRakshit aus der Dokumentation: "Erstellt Proc als Handler für die Ablaufverfolgung, oder deaktiviert die Ablaufverfolgung, wenn der Parameter" Null "ist.» – tig

78

Sie tatsächlich ein wenig weiter als die Lösung oben gehen kann. 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] 
+0

Ich mag diesen. – mikezter

+1

Ich bekomme "NoMethodError: undefined Methode" für beide "__file__" und "__line__" für jede 'Methode' Klasseninstanz, zB:' method (: method) .__ file__'. –

+0

Welche Version von Ruby hast du? –

34

ich komme spät zu diesem Thema, und bin überrascht, dass niemand erwähnt Method#owner.

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

Ich bin überrascht, dass Sie der Erste sind, der die [Methodenklasse] referenziert (http://www.ruby-doc.org/core-1.9.3/ Method.html) explizit. Ein weiterer weniger bekannter Schatz, der in 1.9 eingeführt wurde: 'Method # parameter'. – fny

10

Kopieren meiner Antwort von einem neueren similar question der zu diesem Problem neue Informationen hinzufügt.

Rubin 1,9 hat Methode source_location genannt:

Returns the Ruby source filename and line number containing this method or nil if this method was not defined in Ruby (i.e. native)

Diese zu zurückportiert wurde 1.8.7 dieses Juwel:

So können Sie Anfrage für die Methode:

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. E.g für ActiveRecord::Base#validates dieser zurück:

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, 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 erste Datei für eine Rückkehr 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 auch der Editor am angegebenen Speicherort angezeigt.

3

Vielleicht kann die #source_location helfen zu finden, wo die Methode herkommt.

ex:

ModelName.method(:has_one).source_location 

Return

[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 

Return

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