如何在运行时找到方法的定义?

咱们最近遇到了一个问题,即在发生一系列提交后,后端进程没法运行。 如今,咱们是优秀的小男孩和女孩,并在每次办理登机手续后都进行了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()始终回溯您所在的位置。

相关文章
相关标签/搜索