我们最近遇到了一个问题,在发生一系列提交后,后端进程无法运行。现在,我们都是好孩子,每次签入后都会运行 rake test
,但由于 Rails 的库加载存在一些奇怪的问题,只有当我们在生产模式下直接从 Mongrel 运行它时才会发生这种情况。
我跟踪了这个错误,这是由于一个新的 Rails gem 覆盖了 String 类中的一个方法,这种方式打破了运行时 Rails 代码中的一个狭窄用途。
无论如何,长话短说,有没有办法在运行时询问 Ruby 在哪里定义了一个方法?类似 whereami( :foo )
的东西会返回 /path/to/some/file.rb line #45
?在这种情况下,告诉我它是在 String 类中定义的将是没有帮助的,因为它被某个库重载了。
我不能保证源代码存在于我的项目中,因此对 'def foo'
的 grepping 不一定能满足我的需求,更不用说我是否有 许多 def foo
,有时我没有直到运行时才知道我可能使用的是哪一个。
这真的很晚了,但是您可以通过以下方式找到定义方法的位置:
# 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
您实际上可以比上面的解决方案走得更远。对于 Ruby 1.8 企业版,在 Method
实例上有 __file__
和 __line__
方法:
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]
Method
类实例(例如:method(:method).__file__
)上的 __file__
和 __line__
都得到“NoMethodError:未定义的方法”。
m.__file__
和 m.__line__
已替换为 m.source_location
。
source_location
适用于 Ruby 1.9 及更高版本,包括 2.1
我来晚了这个帖子,很惊讶没有人提到Method#owner
。
class A; def hello; puts "hello"; end end
class B < A; end
b = B.new
b.method(:hello).owner
=> A
Method#parameters
。
从较新的 similar question 复制我的答案,为这个问题添加了新信息。
Ruby 1.9 具有名为 source_location 的方法:
返回包含此方法的 Ruby 源文件名和行号,如果此方法未在 Ruby(即本机)中定义,则返回 nil
此 gem 已将其向后移植到 1.8.7:
ruby18_source_location
因此,您可以请求该方法:
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 模块
在行动:
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 上,这也会在指定位置弹出编辑器。
也许 #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]
或者
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]
这可能会有所帮助,但您必须自己编写代码。从博客粘贴:
Ruby 提供了一个 method_added() 回调,每次在类中添加或重新定义方法时都会调用该回调。它是模块类的一部分,每个类都是一个模块。还有两个相关的回调,称为 method_removed() 和 method_undefined()。
http://scie.nti.st/2008/9/17/making-methods-immutable-in-ruby
如果你可以让这个方法崩溃,你会得到一个回溯,它会告诉你它在哪里。
不幸的是,如果你不能让它崩溃,那么你就无法找到它的定义位置。如果您试图通过覆盖或覆盖它来使用该方法,那么任何崩溃都将来自您覆盖或覆盖的方法,并且它没有任何用处。
崩溃方法的有用方法:
在禁止它的地方传递 nil - 很多时候该方法会在 nil 类上引发 ArgumentError 或永远存在的 NoMethodError。如果您了解该方法的内部知识,并且知道该方法又会调用其他方法,那么您可以覆盖其他方法,并在其中引发。
require 'ruby-debug'; debugger
。
很晚的答案:)但早期的答案对我没有帮助
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
nil
?
nil
,则禁用跟踪。»
您可能可以执行以下操作:
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 的所有重新定义。通过一点元编程,您可以将其概括为您想要的任何功能。但它确实需要在实际执行重新定义的文件之前加载。
您始终可以使用 caller()
追溯您所在的位置。
owner
方法2.method(:crime)
中的第二个是什么?Fixnum
类的一个实例method_missing
中提取任何动态定义的方法。因此,如果您的模块或祖先类在method_missing
中包含class_eval
或define_method
,则此方法将不起作用。