Rails 6 与 Zeitwerk 的未初始化常量

问题描述 投票:0回答:3

我正在将 Rails 应用程序从 v5 升级到 v6。 文件的结构如下:

lib
 |- container
   |- my_module_a
   |  |- my_class.rb
   |  |- class_two.rb
   |- my_module_b

my_module 目录中没有文件,只有

my_module_a
my_module_b
目录。

class_one.rb的内容:

module MyModuleA
  class MyClass
    # definitions...
  end
end

config/application.rb 有:

 config.eager_load_paths += %W[
    #{config.root}/lib/container
    #{config.root}/lib/container/my_module_a
 ]

bin/rails zeitwerk:check
打印一切都很好!

但是,当运行

rails server
并到达
MyClass

的调用时
obj = Container::MyModule::MyClass.new(...)

打印出来了

uninitialized constant Container::MyModuleA
ruby-on-rails upgrade uninitialized-constant zeitwerk
3个回答
1
投票

如果要调用

Container::MyModuleA::MyClassA
,类定义应该是:

module Container
  module MyModuleA
    class MyClass
      # definitions...
    end
  end
end

您当前的目录结构和类定义与

container/my_module_a/my_class
MyModuleA::MyClass
不同,最佳实践是为您的类添加与目录树匹配的模块名称前缀。在开发中,Rails 禁用急切加载,这意味着代码会动态加载(以减少启动时间),当您调用
Container::MyModuleA::MyClassA
时,它将查找
container/my_module_a/my_class_a
文件,检查内容以查找不是的
Container::MyModuleA::MyClassA
现在定义的类定义未嵌套在
Container
模块中。

有关自动加载与预先加载的更多信息:https://www.bigbinary.com/books/learn-rubyonrails-book/loading-behavior-of-ruby-on-rails-in-deep


1
投票

我怀疑上面的描述有多个错别字:

  • 我没有看到
    my_module
    目录
  • 由于
    lib/container/my_module_a
    位于自动加载路径中,因此该目录中的
    my_class.rb
    文件应定义顶级
    MyClass
    类,而不是如上所示的
    MyModuleA::MyClass
  • 然而,
    zeitwerk:check
    过去了

拥有真实姓名和真实代码以避免假设事情会很酷。如果不可能,至少仔细检查并更新问题陈述。

要考虑的主要事情是自动加载路径必须遵守记录的项目结构约定。任何推送到急切加载路径的内容也被视为自动加载路径。

您应该删除

#{config.root}/lib/container/my_module_a
的配置,除非您知道自己在做什么并且确实需要 嵌套根目录

然后,如果将

#{config.root}/lib/container
留在配置中,上面显示的代码大部分都很好,因为此配置表示
lib/container
是包含顶级常量的根目录。但是,现有代码不应使用无人定义的
Container
常量。

那么,

obj = Container::MyModule::MyClass.new(...)

与你所拥有的不相符,应该是

obj = MyModuleA::MyClass.new(...)

如果需要更多帮助,请随时跟进。


0
投票

您尝试访问

MyClass
类的方式似乎可能存在问题。在您的示例中,您尝试以
Container::MyModule::MyClass
的形式访问它,但您的模块结构实际上是
MyModuleA::MyClass

您应该如何访问它:

obj = MyModuleA::MyClass.new(...)

如果您想以

Container::MyModule::MyClass
的方式访问它,您需要重新组织模块结构以匹配它。具体方法如下:

lib
 |- container
   |- my_module
      |- a.rb
      |  |- my_class.rb
      |  |- class_two.rb
      |- b.rb

a.rb
内部,您可以像这样定义您的
MyClass
类:

module Container
  module MyModule
    class MyClass
      # definitions...
    end
  end
end

config/application.rb
内部,您可以像这样加载
my_module
目录:

config.eager_load_paths += %W[
  #{config.root}/lib/container
  #{config.root}/lib/container/my_module
]

现在,您可以通过

MyClass
访问
Container::MyModule::MyClass

obj = Container::MyModule::MyClass.new(...)

如果您决定按照上述方式重新组织模块,请务必将代码中对

MyModuleA
的任何引用更新为
MyModule

© www.soinside.com 2019 - 2024. All rights reserved.