从类对象获取类位置

时间:2012-10-22 13:04:19

标签: ruby

我要查看一堆代码,现在是调试时间。由于我从未成为Ruby调试器的粉丝,我正在寻找一种通过代码并阅读它的方法。

我想要做的是获取定义加载类的文件的位置:

Foo::Bar.create(:param) # how can I know file location in runtime?

对于规模较小,组织较好的项目,我只会搜索class Bar,但这里不可能,因为有许多名为Bar的类,更糟糕的是,其中一些是在相同的命名空间下。我知道,等待发生的麻烦。

注意:我使用的是Ruby 1.8.7。

7 个答案:

答案 0 :(得分:30)

对于MethodsProcs Ruby 1.9有一个名为source_location的方法:

  

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

所以你可以请求方法:

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上,以返回给定方法的文件或类的第一个文件:

编辑:对于Ruby 1.8.7,有一个gem向后移植source_location

答案 1 :(得分:15)

仅供参考,在Rails的Rails应用程序的控制台或调试会话中,您可以找到定义该特定类的文件的磁盘位置。像

> show-source Job

这会给你

From: /home/john/projects/iisifix/app/models/job.rb @ line 13:
Class name: Job
Number of monkeypatches: 6. Use the `-a` option to display all available monkeypatches
Number of lines: 66

class Job < ApplicationRecord
  belongs_to :quote_request
  belongs_to :garage

答案 2 :(得分:3)

这是一个简单的示例,展示了我如何跟踪代码中的位置。如果我需要知道模块中的位置:

class Foo
  attr_reader :initialize_loc
  def initialize
    @initialize_loc = [__FILE__, __LINE__]
    # do more stuff...
  end
end

如果我需要知道发生了什么事情:

require_relative 't1'

foo = Foo.new
# do lots of stuff until you want to know where something was initialized.
puts 'foo initialized at %s:%s' % foo.initialize_loc

当我运行代码时,我得到了:

FooBar:Desktop foobar ruby t2.rb 
foo initilized at /Users/foobar/Desktop/t1.rb:4

如果我不想弄乱模块的源代码,并希望调试器在需要时跳转,我会让调试器做到这一点:

require_relative 't1'
require 'ruby-debug'

debugger
foo = Foo.new
# do lots of stuff until you want to know where something was initilized.
puts 'foo initilized at %s:%s' % foo.initialize_loc

执行将停止,我将立即进入debugger后面一行的调试器:

[0, 9] in t2.rb
  1  require_relative 't1'
  2  require 'ruby-debug'
  3  
  4  debugger
=> 5  foo = Foo.new
  6  # do lots of stuff until you want to know where something was initilized.
  7  puts 'foo initilized at %s:%s' % foo.initialize_loc
  8  
t2.rb:5
foo = Foo.new
(rdb:1) 

一个简单的s将“引导”我进入下一行代码,该代码行位于initialize的{​​{1}}块中:

Foo

除此之外,使用(rdb:1) s [-1, 8] in /Users/foobar/Desktop/t1.rb 1 class Foo 2 attr_reader :initialize_loc 3 def initialize => 4 @initialize_loc = [__FILE__, __LINE__] 5 # do more stuff... 6 end 7 end 8 /Users/foobar/Desktop/t1.rb:4 @initialize_loc = [__FILE__, __LINE__] (rdb:1) 之类的工具递归搜索目录并列出与目标匹配的行的文件名和行号,将有助于找到您正在寻找的内容。

或者,使用Vim内部的grep -rn target_to_find path_to_search将返回您正在寻找的文件。

答案 3 :(得分:0)

坏消息!我想在运行时没有办法知道在Ruby 1.8.7中创建或定义了什么文件。

如果项目有像rails这样的结构,你就可以猜到它。

但是在Ruby中,多个文件可以为同一个类定义方法 甚至可以在运行时定义类(元编程)。

这意味着可能有多个地方定义了这个类。 你寻找的东西可以分布在多个文件中。

我猜你必须搜索Bar的所有定义并查看它们是否在模块Foo中,或者首先查找所有Foo定义并检查其中的内容。 如果代码是一团糟,我看不到一个简单的方法,你将不得不遵循spaguetti形式指向poi。 一个好的编辑器和多个文件搜索可能会有所帮助,但您需要阅读代码。

编辑:毕竟有些好消息。在Ruby 1.9中有source_location,看起来它有1.8.7的后端。但是,如果定义是在运行时通过eval进行的,那么我不确定它是否可行。我认为最简单的解决方案是像Rubymine这样的好编辑器,它通常可以告诉你代码的定义位置。

答案 4 :(得分:0)

坦率地说,鉴于您描述的代码组织,我认为ruby-debug是发现呼叫站点目的地的简单途径:只需设置断点并介入。如果您真的对调试器过敏,可以使用调用网站Kernel#set_trace_func

$max_trace = 10
set_trace_func proc { |event, file, line, id, binding, classname|
  printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
  $max_trace -= 1 
  set_trace_func(nil) unless $max_trace > 0
}

答案 5 :(得分:0)

我在更改对象的超类时出现此错误,修复程序是停止并启动弹簧。

答案 6 :(得分:0)

Klass.method(Klass.methods.first).source_location

使用source_location作为方法,我可以搜索类中定义的第一个方法。我认为由于元编程和其他黑客行为,这并非万无一失。