如何在Ruby中找到常量的定义位置?

35

使用pry,查找一个方法的来源并通过edit-method命令查看源代码非常简单。然而,对于类本身并没有相应的方法。当该类本身没有定义任何方法时,很难通过pry找到源代码。

类是常量,因此等同于询问在哪里找到特定Ruby常量定义的源代码。非常感谢。


4
你能接受之前问题的答案吗? - Arsen7
4
@Arsen7 哦,非常感谢您的提醒。我在这个社区还很新。我现在已经接受了它们。 - Minqi Pan
@Arsen7 哈哈哈,我一点也没有发现任何自私的地方 :) 天啊,我开始喜欢这个社区了。 - Minqi Pan
最新版本的 Pry 可以使用普通的 show-source 命令显示模块/类的源代码。但是,它要求该模块/类至少有一个已定义的方法。 - horseyguy
grep并不总是足够的。我刚遇到一个情况,Rails项目中的一个模型类既被用作类,又被用作命名空间。假设它是Foo。像class Foo::Bar这样定义嵌套类Bar会触发Foo类的自动加载并正确加载它。如果像class Foo; class Bar; end; end;这样做,就会导致Foo被定义为空类,实际的模型定义也没有被加载。幸运的是,我们找出了这个问题,但如果我们可以询问Ruby类的定义位置,那将会更加容易。搜索提及的内容返回了很多结果。 - Nathan Long
显示剩余2条评论
6个回答

21
从Ruby 2.7开始,有一种更好的方法可以做到这一点,那就是Module.const_source_location
> Admin.const_source_location(:LIMIT)
#=> ["SOME_PATH/user.rb", 2]

参考资料:

来自 @ulysse-bn 的额外奖励:

为了快速访问这个方法,你可以像这样将它添加到 IRB 配置中:

  • ~/.irbrc
# displays path of constant
#
# Usage:
#
#     _source_location(::ActiveRecord::Base)
#
def _source_location(const)
  Object.const_source_location(const.name)
end

4
又是一个有用的代码片段:Object.const_source_location('Admin::LIMIT') - itsnikolay
@itsnikolay 谢谢!这个直接放在我的dotfiles里了:https://github.com/BuonOmo/dotfiles/blob/571450877ec751eadcadf423a8bdc7edc4a3120b/.ruby/debug.rb#L127-L141。基本上就是添加了一个像`IRB::Color.csl # => /path/to/irb/color.rb:X`这样的方法。 - undefined
1
@UlysseBN 我喜欢!让我把它加到我的回答里 :beer: - undefined

19
在Ruby中,$"保存了通过Kernel.load加载的所有文件名。因此,您可以尝试像这样做:
constant = User
$".detect{|load_path|
  load_path.include?(constant.to_s.underscore)
}

注意:方法underscore是Rails/ActiveSupport的一部分。


这是一个很好的建议! - sandstrom
很棒的答案。我在 Ruby 2.4.2 中发现 underscore 在字符串中不可用。我用 downcase 解决了这个问题,但如果你的类名有多个单词,你需要想办法将它转换为蛇形命名法。 - Eric Hu

8

使用ack,有时如果我达到了Pry(和Ruby)的限制,我就会使用它。它的优点是你可以利用其shell集成功能从Pry内部调用它,通常只需键入.ack ClassName即可,但这需要在当前目录下的文件中定义类。

如果该类没有在当前目录中定义,则您可以始终查找其中一个方法,从那里获取源位置,然后使用Pry的cat命令显示它(带有语法高亮),或者使用Pry的edit命令直接跳转到其定义。

类没有定义任何实例方法的情况相当罕见--而且这样的类通常也不太有趣 :)

编辑:

Pry的最新版本(0.9.9)现在可以使用普通的show-source命令显示模块/类的源代码。不过,它要求模块/类至少有一个定义的方法。


考虑使用ripgrep而不是ack,它速度更快!ag也是一个很好的替代方案。 - Ulysse BN

5

调试时的一种“恶劣”的方法:启动控制台(例如使用 binding.irb),重新定义常量。您将获得以前定义路径的错误消息!

> Foo = ""
(irb):11: warning: already initialized constant Foo
/path/to/foo.rb:1: warning: previous definition of Foo was here
=> ""

哇,我从没想过那个,这对模块和类也适用,太棒了! - mswieboda
我也找到了一个与类和类方法相关的宝石,也许对某些人有帮助。链接为https://github.com/daveallie/where_is - mswieboda
1
这很棒,因为我正在开发的项目还没有升级到 Ruby 2.7。谢谢! - Nick

4
如果你要查找的常量有方法(是一个类或模块),你可以使用Method类来找出它的定义位置。
class Foo
  def bar
  end

  def self.baz
  end
end

Foo.instance_method(:bar).source_location
Foo.method(:baz).source_location

这是一个有点矫枉过正的方法,对于纯常量无济于事:class Foo; BAHZ = 2; end 但总比什么都没有好。


2

你可以尝试使用Module.constants(true),就像这里所述的那样。这可能比使用pry要多一些工作量,但它可以让你有机会窥探涉及的模块。


不过,从这个看起来似乎没有找到源位置的方法。 - Eric Hu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接