345

最近、一連のコミットが発生した後、バックエンド プロセスの実行に失敗するという問題がありました。さて、私たちは良い子で、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ません。

4

10 に答える 10

441

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

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]

これは、ネイティブのコンパイル済みコードなど、すべてで機能するわけではないことに注意してください。Method クラスには、メソッドが定義されているファイルを返すMethod#ownerなどの便利な関数もあります。

編集:他の回答の REEの__file__andと メモも参照してください。それらも便利です。__line__-- wg

于 2009-03-18T21:12:44.903 に答える
85

実際には、上記のソリューションよりも少し先に進むことができます。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]
于 2010-08-25T09:52:04.090 に答える
39

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

class A; def hello; puts "hello"; end end
class B < A; end
b = B.new
b.method(:hello).owner
=> A
于 2012-02-20T04:07:21.567 に答える
15

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

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

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

これは、この宝石によって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]

source_locationクラスとモジュールの場合、Rubyは組み込みのサポートを提供していませんが、特定のメソッドのファイルを返すか、メソッドが指定されていない場合はクラスの最初のファイルを返す優れた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では、これにより、指定された場所にエディターもポップアップ表示されます。

于 2012-10-22T16:29:23.560 に答える
7

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

Ruby は、クラス内でメソッドが追加または再定義されるたびに呼び出される method_added() コールバックを提供します。これは Module クラスの一部であり、すべての Class が Module です。method_removed() と method_undefined() という 2 つの関連するコールバックもあります。

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

于 2008-10-06T20:01:29.790 に答える
6

メソッドをクラッシュさせることができれば、正確な場所を示すバックトレースが得られます。

残念ながら、クラッシュできない場合は、どこで定義されているかを見つけることができません。メソッドを上書きまたはオーバーライドすることでメソッドをモンキーしようとすると、上書きまたはオーバーライドされたメソッドからクラッシュが発生し、役に立たなくなります。

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

  1. nil禁止されている場所に渡す- 多くの場合、メソッドはnil クラスのArgumentErrorまたは ever-presentを発生させます。NoMethodError
  2. メソッドの内部知識があり、そのメソッドが他のメソッドを呼び出すことがわかっている場合は、他のメソッドを上書きして、その中でレイズできます。
于 2008-10-06T20:01:50.630 に答える
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
于 2010-05-14T18:36:18.477 に答える
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 のすべての再定義が表示されます。少しメタプログラミングを行うだけで、必要な機能に合わせて一般化できます。ただし、実際に再定義を行うファイルの前にロードする必要があります。

于 2008-10-06T20:16:47.620 に答える
3

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

于 2008-10-07T05:07:14.150 に答える