英文:
Uninitialized constant for Rails 6 with Zeitwerk
问题
我正在将我的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
# 定义...
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
会打印出"All is good!"。
然而,当运行rails server
并且达到MyClass
类的调用时:
obj = Container::MyModule::MyClass.new(...)
它会打印出:
uninitialized constant Container::MyModuleA
英文:
I'm upgrading my Rails app from v5 to v6.
The files are structured as:
lib
|- container
|- my_module_a
| |- my_class.rb
| |- class_two.rb
|- my_module_b
my_module directory has no files in it, only my_module_a
and my_module_b
directories.
class_one.rb contents:
module MyModuleA
class MyClass
# definitions...
end
end
config/application.rb has:
config.eager_load_paths += %W[
#{config.root}/lib/container
#{config.root}/lib/container/my_module_a
]
bin/rails zeitwerk:check
prints All is good!
However, when running the rails server
and it reaches the call of the MyClass
class
obj = Container::MyModule::MyClass.new(...)
it prints
uninitialized constant Container::MyModuleA
答案1
得分: 1
如果您想调用 Container::MyModuleA::MyClassA
,类的定义应该如下:
module Container
module MyModuleA
class MyClassA
# 定义...
end
end
end
您当前的目录结构和类的定义不同,container/my_module_a/my_class
与 MyModuleA::MyClassA
。最佳实践是在类名前加上与目录结构匹配的模块名。在开发中,Rails 会禁用急加载(eager loading),这意味着代码会在需要时动态加载(以减少启动时间)。当您调用 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-depth
英文:
If you want to call Container::MyModuleA::MyClassA
, the class definition should be:
module Container
module MyModuleA
class MyClass
# definitions...
end
end
end
Your current directory structure and the class definition are different container/my_module_a/my_class
vs MyModuleA::MyClass
, the best practice is to prefix your classes with a module name that matches the directory tree. In development rails is disabling eager loading meaning that code is loaded on the fly (to reduce booting time) and when you call Container::MyModuleA::MyClassA
, it will lookup for the container/my_module_a/my_class_a
file, check the content to find Container::MyModuleA::MyClassA
which is not defined right now are the class definition is not nested in the Container
module.
More information about autoloading vs eager loading here: https://www.bigbinary.com/books/learn-rubyonrails-book/loading-behavior-of-ruby-on-rails-in-depth
答案2
得分: 1
-
我怀疑上面的描述中存在多个拼写错误:
- 我没有看到一个
my_module
目录 - 由于
lib/container/my_module_a
在autoload路径中,该目录中的my_class.rb
文件应该定义一个顶级的MyClass
类,而不是如上所示的MyModuleA::MyClass
- 然而,
zeitwerk:check
通过了
很酷如果能提供真实的名称和真实的代码,以避免假设。如果不可能,至少要仔细检查并更新问题陈述。
- 我没有看到一个
-
需要考虑的主要事项是autoload路径必须符合文档中的项目结构约定。任何推送到急加载路径的内容也被视为autoload路径。
-
除非你知道自己在做什么并且真正需要嵌套根目录,否则应该移除对
#{config.root}/lib/container/my_module_a
的配置。 -
然后,如果你将
#{config.root}/lib/container
保留在配置中,上面显示的代码大部分是正确的,因为这个配置表示lib/container
是一个包含顶级常量的根目录。但是,现有的代码不应该相应地使用没有定义的Container
常量。 -
因此,
obj = Container::MyModule::MyClass.new(...)
与你拥有的不符,应该是
obj = MyModuleA::MyClass.new(...)
-
如果需要更多帮助,请不要犹豫跟进。
英文:
I suspect the description above has multiple typos:
- I don't see a
my_module
directory - Since
lib/container/my_module_a
is in the autoload paths, themy_class.rb
file in that directory is expected to define a top-levelMyClass
class, notMyModuleA::MyClass
as shown above - Yet,
zeitwerk:check
passes
Would be cool to have real names and real code to avoid assuming things. If not possible, at least double-check and update the problem statement.
The main thing to take into account is that autoload paths have to comply with the documented project structure conventions. Anything pushed to the eager load paths is considered to be an autoload path too.
You should remove the configuration for #{config.root}/lib/container/my_module_a
unless you know what you are doing and really need nested root directories.
Then, if you leave #{config.root}/lib/container
in the configuration, the code shown above is mostly good because this configuration says lib/container
is a root directory containing top-level constants. But then, existing code should accordingly NOT use a Container
constant nobody is defining.
So,
obj = Container::MyModule::MyClass.new(...)
does not square with what you have, should be
obj = MyModuleA::MyClass.new(...)
Please don't hesitate to follow up if more help is needed.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论