Rails 'forgets' class 在初始化器和查询之间

Rails 'forgets' class between initializer and queries

我在 Rails 上遇到了一个特殊问题。

我们已经设置了一个 Hook class 来处理我们应用程序内部的一个小 pub/sub 机制,它是这样的

class Hook
  @subscriptions = {}

  class << self
    def subscribe(message, &block)
      @subscriptions[message] ||= []
      @subscriptions[message] << block
    end

    def publish(message)
      @subscriptions[message].each(&:call)
    end
  end
end

(有点充实,但你明白了)。

问题是:

在初始化器中我们有 Hook.subscribe(:change) { ... } 但是当模型调用 Hook.publish(:change) 时,没有任何反应。快速检查表明,在模型再次调用 Hook 之前,defined? Hook returns nil.

模型中的代码为

after_commit do
  # byebug
  Hook.publish(:change)
end

甚至更奇怪:在初始化程序中我设置了一个全局变量来保存常量 $hook = Hook,该常量确实在模型中的 $hook 变量中,但与 Hook 不匹配它会自动加载。

# inside a debugger in the model

> defined? Hook
=> nil

> $hook
=> Hook

> Hook # will trigger autoloading
=> Hook

> defined? Hook
=> "constant"

> Hook == $hook
=> false

> Hook.instance_variable_get(:@subscriptions)
=> {}

> $hook.instance_variable_get(:@subscriptions)
=> { change: [<Proc:...>] }

> Hook.object_id == $hook.object_id
=> false

如果我通过调试器将它设置回常量 Hook = $hook,一切都会再次正常运行 — 常量在请求中使用适当的变量维护,甚至。

我已经无计可施了。有人知道会发生什么吗?

我终于找到了一个修复程序,即使我不完全理解它(我仍然不太清楚 Rails 如何处理自动加载我猜 - 我需要更深入地调查当我有时间)。

Hook class 在一个模块 (OurApp::Hook) 中,为了清楚起见,我在我分享的示例中省略了它(显然是我的错误)。 我注意到 OurApp.constants 丢失了一些除 Hook 之外的常量。 我进入 lib/our_app.rb 并对其进行了修改:

module OurApp
  autoload :Hook, 'lib/our_app/hook.rb'
  # ...
end

这似乎可以解决问题,没有更多问题了。

谢谢大家!