在没有"autoload"的情况下在Ruby中自动加载类

mis*_*lav 21 ruby lazy-loading autoload

我喜欢Ruby的自动加载功能 ; 然而,它在未来的Ruby版本中消失了,因为它从来都不是线程安全的.

所以现在我想通过自己实现延迟加载机制来假装它已经消失并在没有它的情况下编写我的代码.我想以最简单的方式实现它(我现在不关心线程安全性).Ruby应该允许我们这样做.

让我们从扩充一个类开始const_missing:

class Dummy
  def self.const_missing(const)
    puts "const_missing(#{const.inspect})"
    super(const)
  end
end
Run Code Online (Sandbox Code Playgroud)

当我们尝试引用缺少的"Dummy"下的常量时,Ruby会调用这个特殊方法,例如,如果我们尝试引用"Dummy :: Hello",它将const_missing使用Symbol 调用:Hello.这正是我们所需要的,所以让我们进一步:

class Dummy
  def self.const_missing(const)
    if :OAuth == const
      require 'dummy/oauth'
      const_get(const)      # warning: possible endless loop!
    else
      super(const)
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

现在,如果我们引用"Dummy :: OAuth",它将需要"dummy/oauth.rb"文件,该文件应该定义"Dummy :: OAuth"常量.当我们调用时const_get(因为它可以在const_missing内部调用),有可能出现无限循环,但是防止这种情况超出了这个问题的范围.

最大的问题是,如果在顶级命名空间中存在名为"OAuth"的模块,则整个解决方案会崩溃.引用"Dummy :: OAuth"将跳过它const_missing,只返回顶层的"OAuth".大多数Ruby实现也会对此发出警告:

warning: toplevel constant OAuth referenced by Dummy::OAuth
Run Code Online (Sandbox Code Playgroud)

据报道,这在2003年是一个问题,但我找不到Ruby核心团队曾经关注这个问题的证据.今天,大多数流行的Ruby实现都具有相同的行为.

问题是,const_missing在顶级命名空间中默认跳过有利于常量的问题.如果使用Ruby的autoload功能声明"Dummy :: OAuth",则不会发生这种情况.任何想法如何解决这个问题?

小智 5

这是在一段时间之前在Rails票证中提出的,当我调查它时,似乎没有办法绕过它.问题是Ruby会在调用之前搜索祖先const_missing,因为所有类都有Object祖先,所以总会找到任何顶级常量.如果您可以限制自己只使用命名空间模块,那么它将起作用,因为它们没有Object作为祖先,例如:

>> class A; end
>> class B; end
>> B::A
(irb):3: warning: toplevel constant A referenced by B::A

>> B.ancestors
=> [B, Object, Kernel, BasicObject]

>> module C; end
>> module D; end
>> D::C
NameError: uninitialized constant D::C

>> D.ancestors
=> [D]
Run Code Online (Sandbox Code Playgroud)