如何在運行時找到方法的定義?

咱們最近遇到了一個問題,即在發生一系列提交後,後端進程沒法運行。 如今,咱們是優秀的小男孩和女孩,並在每次辦理登機手續後都進行了rake test ,可是因爲Rails庫加載中的一些奇怪現象,它只發生在咱們直接從Mongrel生產模式中運行時。 git

我追蹤了這個錯誤,這是由於一個新的Rails gem以一種破壞運行時Rails代碼中一個狹隘用法的方式覆蓋了String類中的一個方法。 github

不管如何,長話短說,有沒有辦法在運行時詢問Ruby在哪裏定義了一個方法? 像whereami( :foo )那樣返回/path/to/some/file.rb line #45 ? 在這種狀況下,告訴我它是在類String中定義的將是無益的,由於它被某些庫重載。 編程

我不能保證源代碼存在於個人項目中,因此對'def foo'追求並不必定會給我我須要的東西,更不用說我有多少 def foo ,有時候直到運行時我都不知道哪個我可能正在使用。 後端


#1樓

從一個更新的相似問題中複製個人答案,該問題爲此問題添加了新信息。 數組

Ruby 1.9有一個名爲source_location的方法: ruby

返回包含此方法的Ruby源文件名和行號,若是未在Ruby中定義此方法,則返回nil(即本機) app

這個寶石被反向移植到1.8.7編輯器

因此你能夠請求方法: spa

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

而後詢問該方法的source_locationcode

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不提供內置支持,可是若是沒有指定方法,那麼有一個優秀的Gist構建在source_location以返回給定方法的文件或類的第一個文件:

在行動:

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上,這也會彈出指定位置的編輯器。


#2樓

這可能會有所幫助,但您必須本身編寫代碼。 粘貼在博客上:

Ruby提供了一個method_added()回調,每次在類中添加或從新定義方法時都會調用該回調。 它是Module類的一部分,每一個Class都是一個Module。 還有兩個相關的回調叫作method_removed()和method_undefined()。

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


#3樓

若是你能夠崩潰這個方法,你會獲得一個回溯,它會告訴你確切的位置。

不幸的是,若是你不能崩潰它,那麼你沒法找到它的定義。 若是您嘗試經過覆蓋或覆蓋它來使用該方法,那麼任何崩潰都未來自您的覆蓋或重寫方法,而且它將沒有任何用處。

崩潰方法的有用方法:

  1. 傳遞nil禁止它 - 不少時候該方法會在nil類上引起ArgumentError或者永遠存在的NoMethodError
  2. 若是你對方法有內在的瞭解,而且你知道該方法又調用了其餘方法,那麼你能夠覆蓋另外一個方法,而後在其中加註。

#4樓

你能夠作這樣的事情:

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的全部從新定義。 經過一些元編程,您能夠將其歸納爲您想要的任何功能。 但它確實須要在實際進行從新定義的文件以前加載。


#5樓

您能夠使用caller()始終回溯您所在的位置。

相關文章
相關標籤/搜索