“Uninitialized constant for Rails 6 with Zeitwerk”

huangapple go评论59阅读模式
英文:

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_amy_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_classMyModuleA::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, the my_class.rb file in that directory is expected to define a top-level MyClass class, not MyModuleA::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.

huangapple
  • 本文由 发表于 2023年2月6日 21:05:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/75361699.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定