런타임에 메소드가 정의 된 위치를 찾는 방법은 무엇입니까?


328

최근에 일련의 커밋이 발생한 후 백엔드 프로세스가 실행되지 않는 문제가있었습니다. 이제 우리는 좋은 작은 소년과 소녀 였고 rake test체크인 할 때마다 달렸 지만 Rails의 라이브러리 로딩에 약간의 이상이 있었기 때문에 프로덕션 모드에서 Mongrel에서 직접 실행할 때만 발생했습니다.

버그를 추적했으며 런타임 Rails 코드에서 좁은 사용을 중단하는 방식으로 String 클래스의 메소드를 덮어 쓰는 새로운 Rails gem으로 인한 것입니다.

어쨌든, 간단히 말해서, 런타임에 메소드가 정의 된 위치를 Ruby에 물어볼 수있는 방법이 있습니까? 그런 식으로 뭔가 whereami( :foo )를 반환 /path/to/some/file.rb line #45합니까? 이 경우 String 클래스에 정의되었다고 말하면 일부 라이브러리에 의해 오버로드 되었기 때문에 도움이되지 않습니다.

나는 프로젝트에서 소스의 수명을 보장 할 수 없으므로 grepping을 위해 'def foo'필자가 필요한 것을 줄 필요는 없으며, 많은 것을 가지고 있는지 언급 def foo하지 않고 때로는 런타임 중 어느 것을 사용하고 있는지 알 수 없습니다.


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]

네이티브 컴파일 코드와 같은 모든 기능에는 적용되지 않습니다. 방법 클래스 처럼, 너무, 몇 가지 참신한 기능이 방법 # 소유자 메소드가 정의 된 파일을 반환합니다.

편집 : 또한 볼 __file____line__그들도 편리있어, 다른 대답 REE에 대한 메모를. -wg


1
source_location은 activesupport-2.3.14를 사용하여 1.8.7-p334에서 작동하는 것으로 보입니다
Jeff Maass

방법을 찾은 후 방법의 owner방법을 시도하십시오
Juguang

1
에서 두 번째는 2.method(:crime)무엇입니까?
stack1

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_locationJonathan 이 감사합니다.

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

어떤 루비 버전이 있습니까?
제임스 아담

루비 1.8.7 (2010-06-23 패치 레벨 299) x86_64에 리눅스]
Vikrant Chaudhary

19
루비 1.9에서 m.__file__m.__line__로 대체되었습니다 m.source_location.
Jonathan Tran

루비 2.1은 어떻습니까?
Lokesh

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에서 소개 된 또 다른 덜 알려진 보물 : Method#parameters.
fny

13

이 문제에 새로운 정보를 추가 하는 새로운 유사한 질문 에서 내 대답을 복사 합니다.

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메소드가 지정되지 않은 경우 주어진 메소드에 대한 파일 또는 클래스에 대한 첫 번째 파일을 리턴 하도록 빌드하는 훌륭한 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

도움이 될 수 있지만 직접 코딩해야합니다. 블로그에서 붙여 넣기 :

루비는 메소드가 클래스 내에서 추가되거나 재정의 될 때마다 호출되는 method_added () 콜백을 제공합니다. 모듈 클래스의 일부이며 모든 클래스는 모듈입니다. method_removed () 및 method_undefined ()라는 두 개의 관련 콜백도 있습니다.

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


6

메소드를 중단시킬 수 있다면 정확한 위치를 알려주는 역 추적을 얻게됩니다.

불행히도 충돌이 없으면 정의 된 위치를 찾을 수 없습니다. 메서드를 덮어 쓰거나 재정 의하여 메서드를 사용하여 원숭이를 시도하면 덮어 쓰거나 재정의 된 메서드에서 충돌이 발생하여 아무 소용이 없습니다.

유용한 충돌 방법 :

  1. nil그것이 금지 된 곳을 통과 하십시오-메소드가 nil 클래스에서 ArgumentError또는 항상 존재 하는 시간을 많이 가질 것 NoMethodError입니다.
  2. 메소드에 대한 내부 지식이 있고 메소드가 다른 메소드를 호출한다는 것을 알고 있다면 다른 메소드를 겹쳐 써서 그 내부에서 발생할 수 있습니다.

코드에 액세스 할 수 require 'ruby-debug'; debugger 있으면 코드에 삽입 하려는 위치에 쉽게 삽입 할 수 있습니다 .
Tin Man

"안타깝게도 충돌이 없으면 정의 된 위치를 찾을 수 없습니다." 사실이 아닙니다. 다른 답변도 있습니다.
Martin 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

문서에서 @ArupRakshit :«추적을위한 처리기로 proc를 설정하거나 매개 변수가이면 추적을 비활성화합니다 nil
tig

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
이 기능은 전화를 걸 때 유용하지만 정의 된 위치를 찾으려고 할 때는 좋지 않습니다.
Tin Man
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.