Bir yöntem çalışma zamanında nerede tanımlanır?


328

Kısa bir süre önce, bir dizi işlem gerçekleştirildikten sonra bir arka uç işleminin başarısız olduğu bir sorun yaşadık. Şimdi, iyi küçük kız ve erkeklerdik ve rake testher check-in sonrasında koştuk , ancak Rails'in kütüphane yüklemesindeki bazı tuhaflıklar nedeniyle, sadece üretim modunda doğrudan Mongrel'den çalıştırdığımızda gerçekleşti.

Ben hata izledi ve çalışma zamanı Rails kodunda dar bir kullanım kırdı bir şekilde String sınıfında bir yöntem üzerine yeni bir Rails gem nedeniyle oldu.

Her neyse, uzun lafın kısası, çalışma zamanında Ruby'ye bir yöntemin nerede tanımlandığını sormanın bir yolu var mı? Böyle bir whereami( :foo )şey geri /path/to/some/file.rb line #45mi geliyor? Bu durumda, bana String sınıfında tanımlandığını söylemek yararsız olurdu, çünkü bazı kütüphane tarafından aşırı yüklenmişti.

Projemdeki kaynak hayatlarını garanti edemiyorum, bu yüzden greppelenmek 'def foo'bana ihtiyacım olanı vermeyecek, çok fazla varsa bahsetmiyorum def foo, bazen çalışma zamanına kadar hangisini kullanacağımı bilmiyorum.


1
Ruby 1.8.7'de, bu bilgiyi bulmak için özel bir yöntem eklendi (ve hala 1.9.3'te var) ... aşağıdaki cevabımdaki detaylar.
Alex D

Yanıtlar:


419

Bu gerçekten geç, ancak bir yöntemin nerede tanımlandığını şu şekilde bulabilirsiniz:

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+ kullanıyorsanız, şunları kullanabilirsiniz: 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]

Bunun yerel derlenmiş kod gibi her şeyde çalışmadığını unutmayın. Yöntem sınıfı gibi, çok, Güzel işlevlere sahiptir Yöntem # sahibine yöntemi tanımlanır dosyayı döndürür.

DÜZENLEME: Ayrıca bakınız __file__ve __line__onlar da kullanışlı konum, diğer yanıtında REE için ve notlar. - wg


1
source_location, activesupport-2.3.14
Jeff Maass

yöntemi bulduktan sonra Method'un deneyin owneryöntemi
Juguang

1
Twoki numara ne 2.method(:crime)?
stack1.02.2015

1
sınıfın bir örneğiFixnum
kitteehh

1
Önemli not: Bu işlem, dinamik olarak tanımlanmış herhangi bir yöntemi kullanmayacaktır method_missing. Dolayısıyla , içinde class_evalveya define_methodiçinde bir modül veya ata sınıfınız varsa method_missing, bu yöntem çalışmaz.
cdpalmer

83

Aslında yukarıdaki çözümden biraz daha ileri gidebilirsiniz. Ruby 1.8 Enterprise Edition için, örneklerde __file__ve __line__yöntemleri vardır 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 ve sonrası için, var source_location(teşekkürler 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
Her ikisi için: Ben "tanımsız yöntemi NoMethodError" olsun __file__ve __line__herhangi Methodeski, sınıf örneği: method(:method).__file__.
Vikrant Chaudhary

Hangi yakut sürümüne sahipsiniz?
James Adam

ruby 1.8.7 (2010-06-23 patchlevel 299) [x86_64-linux]
Vikrant Chaudhary

19
On Ruby 1.9, m.__file__ve m.__line__ile değiştirildi m.source_location.
Jonathan Tran

Ruby 2.1 ne olacak?
Lokesh

38

Bu konuya geç geliyorum ve kimsenin bahsetmediğine şaşırdım Method#owner.

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

2
Method sınıfına açıkça ilk başvuruda bulunduğunuza şaşırdım . Bir başka daha az bilinen hazine 1.9 tanıtıldı: Method#parameters.
fny

13

Yanıtımı bu soruna yeni bilgiler ekleyen daha yeni bir benzer sorudan kopyalayarak .

Ruby 1.9 , source_location adlı bir yönteme sahiptir :

Bu yöntemi içeren Ruby kaynak dosya adını ve satır numarasını veya bu yöntem Ruby'de tanımlanmadıysa (yani yerel) döndürür

Bu kadar backported edilmiştir 1.8.7 bu taş tarafından:

Böylece yöntem için talepte bulunabilirsiniz:

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

Ve sonra source_locationbu yöntemin ne olduğunu sorun :

m.source_location

Bu, dosya adı ve satır numarası içeren bir dizi döndürür. Örneğin ActiveRecord::Base#validatesbu iadeler için:

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]

Sınıflar ve modüller için Ruby yerleşik destek sunmaz, ancak source_locationbelirli bir yöntem için dosyayı veya herhangi bir yöntem belirtilmemişse bir sınıf için ilk dosyayı döndürmek için inşa edilmiş mükemmel bir Gist vardır :

Eylemde:

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'in yüklü olduğu Mac'lerde bu, düzenleyiciyi belirtilen konumda açar.


7

Bu yardımcı olabilir, ancak kendiniz kodlamanız gerekir. Blogdan yapıştırıldı:

Ruby, sınıfta her yöntem eklendiğinde veya yeniden tanımlandığında çağrılan bir method_added () geri çağırma sağlar. Bu, Module sınıfının bir parçasıdır ve her Sınıf bir Modüldür. Ayrıca method_removed () ve method_undefined () adında ilgili iki geri arama vardır.

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


6

Yöntemi çökürebilirseniz, size tam olarak nerede olduğunu söyleyecek bir geri izleme alırsınız.

Ne yazık ki, onu çökertemezseniz, nerede tanımlandığını bulamazsınız. Üzerine yazarak veya geçersiz kılarak yöntemle maymun oluşturmaya çalışırsanız, üzerine yazılan veya geçersiz kılınan yönteminizden herhangi bir kilitlenme olur ve herhangi bir kullanım olmaz.

Kilitleme yöntemlerinin yararlı yolları:

  1. Yasakladığı nilyerden geçin - çoğu zaman yöntem nil sınıfında bir ArgumentErrorveya şimdiye kadar var olanı yükseltir NoMethodError.
  2. Eğer yöntem hakkında içeriden bilginiz varsa ve yöntemin başka bir yöntemi çağırdığını biliyorsanız, diğer yöntemi geçersiz kılabilir ve bunun içinde yükseltebilirsiniz.

Koda erişiminiz varsa, kodunuzu require 'ruby-debug'; debugger bırakmak istediğiniz yere kolayca ekleyebilirsiniz .
Tin Man

"Maalesef, kilitlenemezseniz, nerede tanımlandığını bulamazsınız." Doğru değil. Diğer cevapları görün.
Martin T.

6

Belki de #source_locationyöntemin nereden geldiğini bulmaya yardımcı olabilir.

örn:

ModelName.method(:has_one).source_location

Dönüş

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

VEYA

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

Dönüş

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

4

Çok geç cevap :) Ama önceki cevaplar bana yardımcı olmadı

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

Neden geçiyorsun nil?
Arup Rakshit

@ArupRakshit belgelerden: «İzleme için işleyici olarak proc kurar veya parametre ise izlemeyi devre dışı bırakır nil
tig

3

Bunun gibi bir şey yapabilirsiniz:

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

Sonra foo_finder gibi bir şey yüklendiğinden emin olun

ruby -r foo_finder.rb railsapp

(Sadece raylarla uğraştım, bu yüzden tam olarak bilmiyorum, ama böyle bir şekilde başlamanın bir yolu olduğunu hayal ediyorum.)

Bu size String # foo'nun tüm yeniden tanımlarını gösterecektir. Biraz meta programlama ile, istediğiniz herhangi bir işlev için genelleştirebilirsiniz. Ancak, yeniden tanımlamayı gerçekte yapan dosyadan ÖNCE yüklenmesi gerekir.


3

Nerede olursanız olun, bulunduğunuz yerin geri izini alabilirsiniz caller().


1
Bu, sizi neyin aradığını bulmak için yararlıdır, ancak bir şeyin nerede tanımlandığını bulmaya çalışırken iyi değildir.
Tin Man
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.