我们最近遇到了一个问题,即在发生一系列提交后,后端进程无法运行。现在,我们都是优秀的小男孩和女孩,并在每次办理登机手续后都跑rake test
,但是由于Rails库加载中的一些奇怪现象,它只发生在我们直接从Mongrel以生产模式运行时。
我跟踪了这个错误,这是因为一个新的Rails gem以一种破坏运行时Rails代码中一个狭隘用法的方式覆盖了String类中的一个方法。
无论如何,长话短说,有没有办法在运行时向Ruby询问定义方法的位置?像whereami( :foo )
这样返回/path/to/some/file.rb line #45
的东西?在这种情况下,告诉我它是在类String中定义的将是无益的,因为它被某些库重载。
我不能保证源代码存在于我的项目中,因此对'def foo'
的重点不一定会给我我需要的东西,更不用说我是否很多 def foo
是的,有时我直到运行时才知道我可能正在使用哪一个。
答案 0 :(得分:408)
这已经很晚了,但是你可以在这里找到定义方法的地方:
# 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]
请注意,这不适用于所有内容,例如本机编译代码。 Method class也有一些简洁的函数,比如Method#owner,它返回定义方法的文件。
编辑:另请参阅__file__
和__line__
以及其他答案中的REE注释,它们也很方便。 - wg
答案 1 :(得分:81)
实际上你可以比上面的解决方案更进一步。对于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_location
(感谢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 :(得分:37)
我迟到了这个帖子,很惊讶没有人提到Method#owner
。
class A; def hello; puts "hello"; end end
class B < A; end
b = B.new
b.method(:hello).owner
=> A
答案 3 :(得分:12)
从较新的similar question复制我的答案,为此问题添加新信息。
Ruby 1.9 有一个名为source_location的方法:
返回包含此方法的Ruby源文件名和行号,如果未在Ruby中定义此方法(即本机),则返回nil
这个gem已被反向移植到 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上,这也会弹出指定位置的编辑器。
答案 4 :(得分:6)
这可能会有所帮助,但您必须自己编写代码。粘贴在博客上:
Ruby提供了method_added() 每次调用时都会调用的回调 方法在a中添加或重新定义 类。它是Module类的一部分, 每个班级都是一个模块。有 还有两个相关的回调叫做 method_removed()和 method_undefined()。
http://scie.nti.st/2008/9/17/making-methods-immutable-in-ruby
答案 5 :(得分:6)
如果你可以崩溃这个方法,你会得到一个回溯,它会告诉你确切的位置。
不幸的是,如果您无法崩溃,那么您无法找到它的定义位置。如果您尝试通过覆盖或覆盖它来尝试使用该方法,那么任何崩溃都将来自您的覆盖或覆盖方法,并且它将没有任何用处。
崩溃方法的有用方法:
nil
禁止它的地方 - 很多时候该方法会在nil类上引发ArgumentError
或永远存在的NoMethodError
。答案 6 :(得分:5)
也许#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]
OR
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]
答案 7 :(得分:3)
很晚才回答:)但早先的答案并没有帮助我
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
答案 8 :(得分:2)
你可能会做这样的事情:
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_finderruby -r foo_finder.rb railsapp
(我只是搞砸了轨道,所以我不确切知道,但我想有一种方法可以像这样开始。)
这将显示String#foo的所有重新定义。通过一些元编程,您可以将其概括为您想要的任何功能。但它确实需要在实际进行重新定义的文件之前加载。
答案 9 :(得分:2)
您始终可以使用caller()
来回溯您所在的位置。