我正在将 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
如果要调用
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
我怀疑上面的描述有多个错别字:
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(...)
如果需要更多帮助,请随时跟进。
您尝试访问
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
。