Ruby-on-rails 如何找到在运行时定义方法的位置?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/175655/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 20:43:11  来源:igfitidea点击:

How to find where a method is defined at runtime?

ruby-on-railsrubyruntimemethodsdefinition

提问by Matt Rogish

We recently had a problem where, after a series of commits had occurred, a backend process failed to run. Now, we were good little boys and girls and ran rake testafter every check-in but, due to some oddities in Rails' library loading, it only occurred when we ran it directly from Mongrel in production mode.

我们最近遇到了一个问题,在发生一系列提交后,后端进程无法运行。现在,我们是好小男孩和女孩,rake test每次签入后都会跑,但是由于 Rails 库加载中的一些奇怪之处,只有在我们直接从 Mongrel 以生产模式运行时才会发生这种情况。

I tracked the bug down and it was due to a new Rails gem overwriting a method in the String class in a way that broke one narrow use in the runtime Rails code.

我跟踪了这​​个错误,这是由于一个新的 Rails gem 覆盖了 String 类中的一个方法,这种方式打破了运行时 Rails 代码中的一个狭窄用途。

Anyway, long story short, is there a way, at runtime, to ask Ruby where a method has been defined? Something like whereami( :foo )that returns /path/to/some/file.rb line #45? In this case, telling me that it was defined in class String would be unhelpful, because it was overloaded by some library.

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

I cannot guarantee the source lives in my project, so grepping for 'def foo'won't necessarily give me what I need, not to mention if I have manydef foo's, sometimes I don't know until runtime which one I may be using.

我不能保证源代码存在于我的项目中,因此 grepping for'def foo'不一定会提供我需要的东西,更不用说我是否有很多def foo's,有时直到运行时我才知道我可能正在使用哪个。

回答by wesgarrison

This is really late, but here's how you can find where a method is defined:

这真的很晚了,但您可以通过以下方法找到定义方法的位置:

http://gist.github.com/76951

http://gist.github.com/76951

# 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>

If you're on Ruby 1.9+, you can use source_location

如果您使用的是 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]

Note that this won't work on everything, like native compiled code. The Method classhas some neat functions, too, like Method#ownerwhich returns the file where the method is defined.

请注意,这不适用于所有内容,例如本机编译代码。该方法类有一些巧妙的功能,太像方法#主人,返回的定义方法的文件。

EDIT: Also see the __file__and __line__and notes for REE in the other answer, they're handy too. -- wg

编辑:另请参阅另一个答案中 REE的__file____line__和注释,它们也很方便。-- 工作组

回答by James Adam

You can actually go a bit further than the solution above. For Ruby 1.8 Enterprise Edition, there is the __file__and __line__methods on Methodinstances:

您实际上可以比上述解决方案走得更远。对于 Ruby 1.8 企业版,实例上有__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

For Ruby 1.9 and beyond, there is source_location(thanks Jonathan!):

对于 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]

回答by Alex D

I'm coming late to this thread, and am surprised that nobody mentioned Method#owner.

我来晚了,我很惊讶没有人提到Method#owner

class A; def hello; puts "hello"; end end
class B < A; end
b = B.new
b.method(:hello).owner
=> A

回答by Laas

Copying my answer from a newer similar questionthat adds new information to this problem.

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

Ruby 1.9has method called source_location:

Ruby 1.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)

返回包含此方法的 Ruby 源文件名和行号,如果此方法未在 Ruby 中定义(即本机),则返回 nil

This has been backported to 1.8.7by this gem:

这已被此 gem反向移植到1.8.7

So you can request for the method:

所以你可以请求方法:

m = Foo::Bar.method(:create)

And then ask for the source_locationof that method:

然后询问source_location该方法的 :

m.source_location

This will return an array with filename and line number. E.g for ActiveRecord::Base#validatesthis returns:

这将返回一个包含文件名和行号的数组。例如对于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]

For classes and modules, Ruby does not offer built in support, but there is an excellent Gist out there that builds upon source_locationto return file for a given method or first file for a class if no method was specified:

对于类和模块,Ruby 不提供内置支持,但是有一个很好的 Gist 可以在source_location没有指定方法的情况下返回给定方法的文件或类的第一个文件:

In action:

在行动:

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]

On Macs with TextMate installed, this also pops up the editor at the specified location.

在安装了 TextMate 的 Mac 上,这也会在指定位置弹出编辑器。

回答by Ken

This may help but you would have to code it yourself. Pasted from the blog:

这可能会有所帮助,但您必须自己编写代码。贴自博客:

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().

Ruby 提供了一个 method_ added() 回调,每次在类中添加或重新定义方法时都会调用该回调。它是 Module 类的一部分,每个类都是一个模块。还有两个相关的回调,称为 method_removed() 和 method_undefined()。

http://scie.nti.st/2008/9/17/making-methods-immutable-in-ruby

http://scie.nti.st/2008/9/17/making-methods-immutable-in-ruby

回答by Orion Edwards

If you can crash the method, you'll get a backtrace which will tell you exactly where it is.

如果您可以使该方法崩溃,您将获得一个回溯,它会告诉您它的确切位置。

Unfortunately, if you can't crash it then you can't find out where it has been defined. If you attempt to monkey with the method by overwriting it or overriding it, then any crash will come from your overwritten or overridden method, and it won't be any use.

不幸的是,如果你不能让它崩溃,那么你就无法找到它被定义的地方。如果您试图通过覆盖或覆盖该方法来玩弄该方法,那么任何崩溃都将来自您的覆盖或覆盖方法,并且不会有任何用处。

Useful ways of crashing methods:

崩溃方法的有用方法:

  1. Pass nilwhere it forbids it - a lot of the time the method will raise an ArgumentErroror the ever-present NoMethodErroron a nil class.
  2. If you have inside knowledge of the method, and you know that the method in turn calls some other method, then you can overrwrite the other method, and raise inside that.
  1. 通过nil它禁止它的地方 - 很多时候该方法将在 nil 类上引发ArgumentError或 永远存在NoMethodError
  2. 如果您了解该方法的内部知识,并且您知道该方法又会调用其他某个方法,那么您可以覆盖其他方法,并在其中引发。

回答by Samda

Maybe the #source_locationcan help to find where is the method come from.

也许#source_location可以帮助找到方法的来源。

ex:

前任:

ModelName.method(:has_one).source_location

Return

返回

[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

Return

返回

[project_path/vendor/ruby/version_number/gems/activerecord-number/lib/active_record/validations.rb", line_number_of_where_method_is]

回答by tig

Very late answer :) But earlier answers did not help me

很晚的答案:)但较早的答案对我没有帮助

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

回答by AShelly

You might be able to do something like this:

你也许可以做这样的事情:

foo_finder.rb:

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

Then ensure foo_finder is loaded first with something like

然后确保首先加载 foo_finder 类似

ruby -r foo_finder.rb railsapp

(I've only messed with rails, so I don't know exactly, but I imagine there's a way to start it sort of like this.)

(我只是弄乱了 Rails,所以我不知道确切,但我想有一种方法可以像这样启动它。)

This will show you all the re-definitions of String#foo. With a little meta-programming, you could generalize it for whatever function you want. But it does need to be loaded BEFORE the file that actually does the re-definition.

这将向您展示 String#foo 的所有重新定义。通过一点元编程,您可以将其概括为您想要的任何功能。但它确实需要在实际进行重新定义的文件之前加载。

回答by AShelly

You can always get a backtrace of where you are by using caller().

您始终可以通过使用caller().