How to find where a method is defined at runtime?
我们最近遇到了一个问题,在发生一系列提交之后,后端进程无法运行。现在,我们都是好孩子,每次登记入住后都会运行
我跟踪了这个bug,这是由于一个新的rails gem在string类中重写了一个方法,从而打破了运行时rails代码中的一个狭隘用法。
总之,长话短说,有没有一种方法,在运行时,问ruby在哪里定义了一个方法?像
我不能保证源代码在我的项目中存在,所以对
这确实很晚了,但您可以通过以下方法找到定义方法的位置:
http://gist.github.com/76951
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # 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> |
如果您使用的是Ruby1.9+,则可以使用
1 2 3 4 5 6 7 | 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] |
注意,这并不能解决所有问题,比如本机编译代码。方法类也有一些整洁的函数,比如方法所有者,它返回定义方法的文件。
编辑:另一个答案也可以看到
实际上,您可以比上面的解决方案更进一步。对于Ruby1.8企业版,在
1 2 3 4 5 6 7 8 9 10 | 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 |
对于Ruby1.9及更高版本,有
1 2 3 4 5 6 | 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] |
我来晚了,很惊讶没人提到
1 2 3 4 5 | class A; def hello; puts"hello"; end end class B < A; end b = B.new b.method(:hello).owner => A |
从一个新的类似问题中复制我的答案,该问题将添加新信息。
Ruby1.9有一个名为source_location的方法:
Returns the Ruby source filename and line number containing this method or nil if this method was not defined in Ruby (i.e. native)
此GEM已将其恢复为1.8.7:
- 18卢布来源地
因此,您可以请求使用以下方法:
1 | m = Foo::Bar.method(:create) |
然后要求使用该方法的
1 | m.source_location |
这将返回一个带有文件名和行号的数组。例如,对于
1 2 | 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不提供内置支持,但有一个很好的要点,它基于
- ruby哪里是模块
行动中:
1 2 3 | 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的macs上,这也会在指定位置弹出编辑器。
如果你能使这个方法崩溃,你会得到一个回溯,它会准确地告诉你它在哪里。
不幸的是,如果您不能崩溃它,那么您就无法找到它的定义位置。如果您试图通过重写或重写方法来蒙骗该方法,那么任何崩溃都将来自于被重写或重写的方法,并且不会有任何用处。
碰撞方法的有用方法:
这可能会有所帮助,但您必须自己编写代码。从日志中粘贴:
Ruby provides a method_added()
callback that is invoked every time a
method is added or redefined within a
class. It’s part of the Module class,
and every Class is a Module. There are
also two related callbacks called
method_removed() and
method_undefined().
http://scie.nti.st/2008/9/17/making-methods-immutable-in-ruby
也许
前任:
1 | ModelName.method(:has_one).source_location |
返回
1 | [project_path/vendor/ruby/version_number/gems/activerecord-number/lib/active_record/associations.rb", line_number_of_where_method_is] |
或
1 | ModelName.new.method(:valid?).source_location |
返回
1 | [project_path/vendor/ruby/version_number/gems/activerecord-number/lib/active_record/validations.rb", line_number_of_where_method_is] |
很晚的回答:)但是更早的回答对我没有帮助。
1 2 3 4 5 6 | set_trace_func proc{ |event, file, line, id, binding, classname| printf"%8s %s:%-2d %10s %8s ", event, file, line, id, classname } # call your method set_trace_func nil |
通过使用
您可以这样做:
FuffyField.Rb:
1 2 3 4 5 6 7 8 9 10 | class String def String.method_added(name) if (name==:foo) puts"defining #{name} in: \t" puts caller.join(" \t") end end end |
然后确保foo-finder首先加载了
1 | ruby -r foo_finder.rb railsapp |
(我只弄乱了铁轨,所以我不太清楚,但我想有一种方法可以像这样启动它。)
这将显示字符串foo的所有重新定义。通过一点元编程,您可以将它概括为您想要的任何函数。但它确实需要在执行重新定义的文件之前加载。