Zeitwerk:将引擎/宝石目录添加到父母铁路应用程序的自动加载路径

发布于 2025-02-13 11:34:06 字数 915 浏览 0 评论 0原文

我正在尝试在现有的旧宝石(Rails ::引擎)中切换到Zeitwerk。到目前为止,所有文件均已手动需要 d和自动加载 ed。再加上引擎的LIB折叠器通过config.autoload_paths +=路径[“ lib”]。to_a in 类myEngine< Rails ::引擎

使用Zeitwerk的开关通过 readme

require "zeitwerk"
loader = Zeitwerk::Loader.for_gem
.
. --> more project specific stuff here
.
loader.setup # ready!

太好了!现在,我想在Rails应用程序中使用GEM,然后将发动机的目录添加到Rails应用程序的AutoLoad_Path中。以前通过上述config.autoload_paths进行了正常工作。如果我现在这样做,则会失败以下错误消息:

Zeitwerk::Error:
  loader

#<Zeitwerk::Loader:0x00000001094d4bd0
...

wants to manage directory /gems/<NameOfGem>/lib, which is already managed by

#<Zeitwerk::Loader:0x0000000106b2d728
...

将引擎的LIB目录添加到Rails应用程序的自动加载路径中的正确方法是什么?

谢谢你!

I'm trying to switch to Zeitwerk in an existing, older Gem (Rails::Engine). Until now all files have been manually required and autoloaded. Plus the engine's lib-folder was added to autoload_paths via config.autoload_paths += paths["lib"].to_a in class MyEngine < Rails::Engine.

The switch to use Zeitwerk worked fine via the described way on the Readme:

require "zeitwerk"
loader = Zeitwerk::Loader.for_gem
.
. --> more project specific stuff here
.
loader.setup # ready!

So far so good! Now I want to use the Gem in a Rails app and add the engine's directories to the autoload_path of the Rails application. This worked fine previously via the above mentioned config.autoload_paths. If I do this now, it fails with the following error message:

Zeitwerk::Error:
  loader

#<Zeitwerk::Loader:0x00000001094d4bd0
...

wants to manage directory /gems/<NameOfGem>/lib, which is already managed by

#<Zeitwerk::Loader:0x0000000106b2d728
...

What is the correct way to add the engine's lib-directories to the autoload path of the Rails application?

Thank you!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

心房敞 2025-02-20 11:34:06

rails 设置两个加载程序main and 一旦:

Rails.autoloaders.main
Rails.autoloaders.once

这些只是zeitwerk :: Loader的实例。 rails 还为您提供了一个配置,将根目录添加到这些加载程序中:

config.autoload_paths         # main
config.autoload_once_paths    # once

当Gem的lib目录通过以下一种配置之一添加到自动加载到AutoLoad, lib 成为根目录:

# config.autoload_paths += paths["lib"].to_a

>> Rails.autoloaders.main.root_dirs
=> 
...                    
 "/home/alex/code/stackoverflow/my_engine/lib"=>Object,
...

当称为GEM的类时, zeitwerk 使用注册的加载程序查找并加载与此类相对应的文件。

如果GEM然后设置自己的加载程序:

require "zeitwerk"
loader = Zeitwerk::Loader.for_gem
loader.setup

zeitwerk :: Loader的另一个实例是用自己的根目录创建的:

>> Zeitwerk::Registry.loaders.detect { |z| z.tag == "my_engine" }
=> 
#<Zeitwerk::GemLoader:0x00007fe5e53e0f80
...
 @root_dirs={"/home/alex/code/stackoverflow/my_engine/lib"=>Object},
...

# NOTE: these are the two loaders registered by rails
>> Zeitwerk::Registry.loaders.select { |z| z.tag =~ /rails/ }.count
=> 2

zeitwerk 不允许两个加载程序具有共享目录并提出一个错误,显示两个冲突的装载机。

因为宝石是a rails :: Engine 最好的选择是让 rails manage zeitwerk 加载器,然后删除 zeitwerk :: loader。 for_gem 设置。

# only use rails config
config.autoload_paths += paths["lib"].to_a

另一方面,已经设置了GEM Loader,并且不需要 config.autoload_paths

# NOTE: without any loaders
>> MyEngine::Test
# (irb):1:in `<main>': uninitialized constant MyEngine::Test (NameError)                                            
# MyEngine::Test                                
#         ^^^^^^

# NOTE: with gem loader
#
#   require "zeitwerk"
#   loader = Zeitwerk::Loader.for_gem
#   loader.setup                           
#
>> MyEngine::Test
=> MyEngine::Test

# NOTE: with rails `main` loader
#
#   config.autoload_paths += paths["lib"].to_a
#
>> MyEngine::Test
=> MyEngine::Test

# NOTE: with gem loader and rails loader
$ bin/rails c
# /home/alex/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/zeitwerk-2.6.0/lib/zeitwerk/loader.rb:480:in
# `block (3 levels) in raise_if_conflicting_directory':
# loader (Zeitwerk::Error)

更新

# Use rails loaders
# config.autoload_path          .->  Zeitwerk::Loader(@tag=rails.main)
# config.autoload_once_path     |->  Zeitwerk::Loader(@tag=rails.once)
#                               |
# Or create a new loader        |
# Zeitwerk::Loader.for_gem      |->  Zeitwerk::GemLoader(@tag=my_engine)
#                               |
# my_engine/lib can only be in one of these

zeitwerk 进行加载和重新加载。 rails 在这里只是另一个宝石。

如果您不使用Rails Config,则 zeitwerk 将通过zeitwerk :: gemloader(@tag = my_engine) 找到文件。

如果您使用Rails Config, Zeitwerk 将通过Zeitwerk :: Loader(@tag = rails.main)找到文件(Make Gemloader

如果 lib 是任何现有加载程序中的根目录,则无需在 lib 目录中具有任何需要或自动加载文件。除了在 zeitwerk 启动之前需要的东西,例如 myEngine ::引擎 from lib/my_engine/Engine.rb 。

Rails sets up two loaders main and once:

Rails.autoloaders.main
Rails.autoloaders.once

These are just instances of Zeitwerk::Loader. Rails also gives you a config to add root directories to these loaders:

config.autoload_paths         # main
config.autoload_once_paths    # once

When gem's lib directory is added to autoload through one of these configs, lib becomes a root directory:

# config.autoload_paths += paths["lib"].to_a

>> Rails.autoloaders.main.root_dirs
=> 
...                    
 "/home/alex/code/stackoverflow/my_engine/lib"=>Object,
...

When a class from the gem is called, zeitwerk uses registered loaders to look up and to load the file corresponding to this class.

If the gem then sets up its own loader:

require "zeitwerk"
loader = Zeitwerk::Loader.for_gem
loader.setup

another instance of Zeitwerk::Loader is created with its own root directories:

>> Zeitwerk::Registry.loaders.detect { |z| z.tag == "my_engine" }
=> 
#<Zeitwerk::GemLoader:0x00007fe5e53e0f80
...
 @root_dirs={"/home/alex/code/stackoverflow/my_engine/lib"=>Object},
...

# NOTE: these are the two loaders registered by rails
>> Zeitwerk::Registry.loaders.select { |z| z.tag =~ /rails/ }.count
=> 2

Zeitwerk doesn't allow two loaders to have a shared directory and raises an error showing two conflicting loaders.

Because the gem is a Rails::Engine the best option is to let rails manage zeitwerk loaders and remove Zeitwerk::Loader.for_gem setup.

# only use rails config
config.autoload_paths += paths["lib"].to_a

On the other hand, gem loader is already set up and config.autoload_paths is not needed.

# NOTE: without any loaders
>> MyEngine::Test
# (irb):1:in `<main>': uninitialized constant MyEngine::Test (NameError)                                            
# MyEngine::Test                                
#         ^^^^^^

# NOTE: with gem loader
#
#   require "zeitwerk"
#   loader = Zeitwerk::Loader.for_gem
#   loader.setup                           
#
>> MyEngine::Test
=> MyEngine::Test

# NOTE: with rails `main` loader
#
#   config.autoload_paths += paths["lib"].to_a
#
>> MyEngine::Test
=> MyEngine::Test

# NOTE: with gem loader and rails loader
$ bin/rails c
# /home/alex/.rbenv/versions/3.1.2/lib/ruby/gems/3.1.0/gems/zeitwerk-2.6.0/lib/zeitwerk/loader.rb:480:in
# `block (3 levels) in raise_if_conflicting_directory':
# loader (Zeitwerk::Error)

Update

# Use rails loaders
# config.autoload_path          .->  Zeitwerk::Loader(@tag=rails.main)
# config.autoload_once_path     |->  Zeitwerk::Loader(@tag=rails.once)
#                               |
# Or create a new loader        |
# Zeitwerk::Loader.for_gem      |->  Zeitwerk::GemLoader(@tag=my_engine)
#                               |
# my_engine/lib can only be in one of these

Zeitwerk does the loading and reloading. Rails is just another gem here.

If you don't use rails config, Zeitwerk will find files through Zeitwerk::GemLoader(@tag=my_engine), that the gem has created.

If you use rails config, Zeitwerk will find files through Zeitwerk::Loader(@tag=rails.main), that rails has created (making GemLoader unnecessary).

If lib is a root directory in any of the existing loaders there is no need to have any requires or autoloads for files in lib directory. Except for things that are needed before Zeitwerk kicks in, like MyEngine::Engine from lib/my_engine/engine.rb.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文