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

北城余情 提交于 2020-03-12 19:14:04

我们最近遇到了一个问题,即在发生一系列提交后,后端进程无法运行。 现在,我们是优秀的小男孩和女孩,并在每次办理登机手续后都进行了rake test ,但是由于Rails库加载中的一些奇怪现象,它只发生在我们直接从Mongrel生产模式中运行时。

我追踪了这个错误,这是因为一个新的Rails gem以一种破坏运行时Rails代码中一个狭隘用法的方式覆盖了String类中的一个方法。

无论如何,长话短说,有没有办法在运行时询问Ruby在哪里定义了一个方法? 像whereami( :foo )那样返回/path/to/some/file.rb line #45 ? 在这种情况下,告诉我它是在类String中定义的将是无益的,因为它被某些库重载。

我不能保证源代码存在于我的项目中,所以对'def foo'追求并不一定会给我我需要的东西,更不用说我有多少 def foo ,有时候直到运行时我都不知道哪一个我可能正在使用。


#1楼

从一个更新的类似问题中复制我的答案,该问题为此问题添加了新信息。

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]

对于类和模块,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()始终回溯您所在的位置。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!