実行時にメソッドが定義されている場所を見つける方法は?


328

最近、一連のコミットが発生した後、バックエンドプロセスが実行に失敗するという問題がありました。さて、私たちは良い男の子と女の子でありrake test、すべてのチェックイン後に実行しましたが、Railsのライブラリーのロードのいくつかの奇妙さのため、プロダクションモードでMongrelから直接実行したときにのみ発生しました。

私はバグを追跡しましたが、それは新しいRails gemがStringクラスのメソッドを上書きして、実行時のRailsコードでの1つの狭い用途を壊したためです。

とにかく、短い話ですが、実行時にRubyにメソッドが定義されている場所を尋ねる方法はありますか?そのような何かwhereami( :foo )が返ります/path/to/some/file.rb line #45か?この場合、それがStringクラスで定義されていることを私に言っても、ライブラリによってオーバーロードされているため、役に立ちません。

以下のためにgrepをするので、私は、私のプロジェクトのソース命を保証することはできません'def foo'必ずしも私が私が持っている場合はもちろんのこと、必要なものを私に与えることはありません多く def fooのを時々私は私が使用している可能性があり1実行時までわかりません。


1
Ruby 1.8.7では、この情報を見つけるために特別なメソッドが特別に追加されました(これは1.9.3にもまだあります)...以下の私の回答に詳細があります。
Alex D

回答:


419

これは本当に遅いですが、メソッドが定義されている場所を見つける方法は次のとおりです。

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>

Ruby 1.9以降を使用している場合は、 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]

これは、ネイティブコンパイル済みコードのように、すべてでは機能しないことに注意してください。メソッドのクラスは、同様に、あまりにも、いくつかのきちんとした機能を有する方法#所有者メソッドが定義されているファイルを返します。

編集:また、他の回答でREEの__file__and __line__とノートを参照してください。これらも便利です。-WG


1
source_locationは、activesupport-2.3.14を使用して1.8.7-p334で機能しているようです
Jeff Maass

メソッドを見つけowner
たら

1
二番は何2.method(:crime)ですか?
stack1 2015年

1
クラスのインスタンスFixnum
kitteehh

1
重要な注意:これは、動的に定義されたメソッドをからプルアップしませんmethod_missing。したがって、class_evalまたはのdefine_method内部にあるモジュールまたは祖先クラスがある場合method_missing、このメソッドは機能しません。
cdpalmer

83

上記の解決策より実際には少し先に進むことができます。Ruby 1.8 Enterprise Editionの場合、インスタンスには__file__および__line__メソッドがありMethodます。

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

Ruby 1.9以降には、source_location(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]

2
両方のために:私は「未定義のメソッドNoMethodError」を取得__file__し、__line__任意の上Methodの元、クラスインスタンス:method(:method).__file__
Vikrant Chaudhary、2011年

どのバージョンのルビーをお持ちですか?
ジェームスアダム

ruby 1.8.7(2010-06-23パッチレベル299)[x86_64-linux]
Vikrant Chaudhary

19
Ruby 1.9ではm.__file__m.__line__に置き換えられましたm.source_location
ジョナサントラン、

Ruby 2.1についてはどうですか?
ロケシュ2015

38

私はこのスレッドに遅れて来て、誰も言及しなかったことに驚いていMethod#ownerます。

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

2
Methodクラスを明示的に参照するのが最初であることに驚いています。1.9で導入されたもう1つのあまり知られていない宝物:Method#parameters
2012年

13

この問題に新しい情報を追加する新しい同様の質問から私の回答をコピーします。

Ruby 1.9にsource_locationというメソッドがあります

このメソッドを含むRubyソースのファイル名と行番号を返します。このメソッドがRuby(つまり、ネイティブ)で定義されていない場合はnilを返します。

これは、このgemによって1.8.7にバックポートされました。

したがって、メソッドをリクエストできます。

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

そして、source_locationそのメソッドについて尋ねます:

m.source_location

これは、ファイル名と行番号を含む配列を返します。たとえば、ActiveRecord::Base#validatesこれは次を返します:

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]

クラスとモジュールの場合、Rubyは組み込みサポートを提供していませんがsource_location、特定のメソッドのファイルまたはメソッドが指定されていない場合はクラスの最初のファイルを返すように構築された優れたGist があります。

実行中:

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]

TextMateがインストールされているMacでは、指定した場所にエディターがポップアップ表示されます。


7

これは役立つかもしれませんが、自分でコーディングする必要があります。ブログから貼り付けました:

Rubyには、クラス内でメソッドが追加または再定義されるたびに呼び出されるmethod_added()コールバックが用意されています。これはモジュールクラスの一部であり、すべてのクラスはモジュールです。method_removed()およびmethod_undefined()と呼ばれる2つの関連するコールバックもあります。

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


6

メソッドをクラッシュさせることができる場合は、それがどこにあるかを正確に伝えるバックトレースを取得します。

残念ながら、それをクラッシュさせることができない場合、それが定義されている場所を見つけることができません。メソッドを上書きまたはオーバーライドしてそのメソッドを使用してサルを試みた場合、クラッシュは上書きまたはオーバーライドされたメソッドから発生するため、使用できません。

メソッドをクラッシュさせる便利な方法:

  1. nil禁止されている場所を渡す-多くの場合、メソッドはnilクラスで、ArgumentErrorまたは常に存在するものを発生NoMethodErrorさせます。
  2. メソッドの内部知識があり、そのメソッドが他のメソッドを呼び出すことがわかっている場合は、他のメソッドを上書きして、その内部で発生させることができます。

あなたは、コードへのアクセス権を持っている場合は、同じように簡単に挿入することができますrequire 'ruby-debug'; debugger 。あなたがドロップしたいあなたのコードで
ブリキ男

「残念ながら、クラッシュできない場合は、定義されている場所を見つけることができません。」真実ではない。他の答えを見てください。
マーティンT.

6

たぶん、#source_locationメソッドがどこから来たのかを見つけるのを助けることができます。

例:

ModelName.method(:has_one).source_location

戻る

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

または

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

戻る

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

4

非常に遅い答え:)しかし、以前の答えは私を助けませんでした

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

なぜ合格nilですか?
Arup Rakshit、2014年

ドキュメンテーションの@ArupRakshit:«トレース用のハンドラーとしてprocを確立するか、パラメーターがの場合はトレースを無効にしますnil。»
2014年

3

あなたはこのようなことをすることができるかもしれません:

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

次に、foo_finderが最初に読み込まれるようにします。

ruby -r foo_finder.rb railsapp

(私はレールをいじっただけなので、正確にはわかりませんが、このように開始する方法はあると思います。)

これにより、String#fooのすべての再定義が表示されます。少しのメタプログラミングで、あなたが望むどんな機能にもそれを一般化することができます。ただし、実際に再定義を行うファイルの前にロードする必要があります。


3

を使用すると、いつでも現在地のバックトレースを取得できますcaller()


1
これはあなたを何と呼んだかを見つけるのに役立ちますが、何かが定義された場所を見つけようとしているときは良くありません。
ブリキの男
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.