【问题标题】:Get class location from class object从类对象获取类位置
【发布时间】:2012-10-12 07:16:04
【问题描述】:

我有一堆代码要看,现在是调试时间。因为我从来都不是 Ruby 调试器的粉丝,所以我正在寻找一种方法来遍历代码并阅读它。

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

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

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

注意:我使用的是 Ruby 1.8.7。

【问题讨论】:

  • 仅供参考,这与stackoverflow.com/questions/175655/… 非常相似,但他们的解决方案都不适合我。 __file____line__ 不适合我。
  • 什么意思,它不起作用?如何 ? (顺便说一句,它是__FILE____LINE__
  • 方法source_location计划在this bug report中向后移植到1.8.8
  • 这对代码检查来说很痛苦,但使用调试器很容易。只需在调用站点之前设置一个断点,然后进入该方法,调试器列表就会告诉您到达的位置。仅仅因为您不是粉丝并不意味着您不应该使用可用的工具。
  • @thoferon Link 我已经提到在Method 对象下调用__file__ 来获取源文件,但它不起作用。 Ofc __FILE__ 工作 :)

标签: ruby


【解决方案1】:

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

如果项目有一些类似rails的结构,你可以猜到。

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

这意味着定义类的地方可能不止一个。 而且您要查找的内容可以分布在多个文件中。

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

编辑:毕竟是个好消息。在 Ruby 1.9 中有 source_location 并且看起来它有 1.8.7 的反向移植。但是,如果定义是在运行时由 eval 进行的,那么我不确定它是否会起作用。我认为最简单的解决方案是像 Rubymine 这样的优秀编辑器,它通常可以告诉你代码是在哪里定义的。

【讨论】:

  • “我猜在运行时没有办法知道哪个文件创建或定义了一个类。”?那么__FILE____LINE__ 会做什么呢?
  • @theTinMan __LINE__ 告诉你代码中的值,OP 知道调用站点,而不是定义站点,所以你建议他把 __LINE__ 放在哪里他调用的方法是什么?
  • 查看我对问题的回答。
【解决方案2】:

对于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,有一个可以向后移植 source_location 的 gem:

【讨论】:

  • 是的,注意到它有点太晚了。 :-(
  • 刚刚尝试了 ruby​​18_source_location 并且它有效。很棒的东西,我同意其他人的观点,即调试是很好的方法,但这样我工作得更快。我还建议在以下更受欢迎的链接stackoverflow.com/questions/175655/… 上分享此内容。谢谢。
  • 了解您编写的语言中的调试器是一项非常重要的技能。我将它教给与我一起工作的人,因为它可以让您深入了解您的代码在做什么,并且当我们遇到问题并且需要知道在哪里以及为什么时,它是我使用的第一个工具。
  • AFAICT, Foo::Bar.method(:create) 将带您进入类方法 Foo::Bar.create,而不是实例方法 Foo::Bar#createActiveRecord::Base.method(:validates) 也是如此,它代表 ActiveRecord::Base.validates,而不是 ActiveRecord::Base#validates。要找出实例方法的位置,您可以使用Foo::Bar.instance_method(:create).source_location
【解决方案3】:

坦率地说,鉴于您描述的代码组织,我认为 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
}

【讨论】:

    【解决方案4】:

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

    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 将“引导”我进入下一行代码,这将位于Fooinitialize 块中:

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

    除此之外,使用像 grep -rn target_to_find path_to_search 这样的工具递归搜索目录并列出与目标匹配的文件名和行号,将大大有助于找到您要查找的内容。

    或者,在 Vim 中使用 :vim /target_to_find/ path_to_search 将返回您正在查找的文件。

    【讨论】:

    • 谢谢,实际上非常聪明的解决方案 :) 我将设置一个类用于此目的
    • 我不是 100% 确定。但是,当您使用@initialize_loc = [__FILE__, __LINE__] 定义(或重新定义)初始化方法时。意味着后者您稍后将获得该行和文件的“坐标”,与您放置它的位置无关,这是很多东西,但不是对于@Dolphin 的要求。他想找到一个从一开始就不知道在哪里的声明。
    • “独立于你把它放在哪里”?什么会独立于文件中的物理位置?
    【解决方案5】:

    仅供参考,在 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
    

    【讨论】:

    • 您需要先加载类,例如在控制台中调用Job。然后show-source 工作。大多数情况下。
    • 这需要在您的项目中包含并加载pry gem。
    【解决方案6】:

    我在更改对象的超类时遇到此错误,解决方法是停止并启动 spring。

    【讨论】:

      【解决方案7】:
      Klass.method(Klass.methods.first).source_location
      

      使用source_location 作为方法,我可以搜索类中定义的第一个方法。我想这不是万无一失的,因为元编程和其他黑客。

      【讨论】:

      • 方法可以被继承或混入,这不会可靠地工作
      • 这有点碰运气,尤其是当您尝试查找 Rails 类时。
      • 这引导我走向正确的方向:Klass.instance_method(Klass.instance_methods(false).first).source_location
      猜你喜欢
      • 2019-02-19
      • 2010-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-22
      相关资源
      最近更新 更多