我正在尝试切换到现有的、较老的Gem (Rails::Engine)中的Zeitwerk。到目前为止,所有文件都是手动的required和autoloaded。加上引擎的lib文件夹是通过autoload_paths中的config.autoload_paths += paths["lib"].to_a添加到class MyEngine < Rails::Engine中的。
通过在自述上描述的方式,使用Zeitwerk的开关工作得很好
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
...将引擎的库目录添加到Rails应用程序的自动路径的正确方法是什么?
谢谢!
发布于 2022-07-06 21:47:48
Rails设置两个加载程序main和once
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使用注册加载程序来查找和加载与该类相对应的文件。
如果创业板然后设置了自己的加载程序:
require "zeitwerk"
loader = Zeitwerk::Loader.for_gem
loader.setupZeitwerk::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
=> 2Zeitwerk不允许两个加载程序有一个共享目录,并引发一个错误,显示两个冲突的加载程序。
因为gem是Rails::Engine,所以最好的选择是让rails管理zeitwerk加载器并删除Zeitwerk::Loader.for_gem设置。
# only use rails config
config.autoload_paths += paths["lib"].to_a另一方面,gem加载器已经被设置,并且不需要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齐特沃克负责装货和重装。Rails只是这里的另一块宝石。
如果不使用rails配置,Zeitwerk将通过Zeitwerk::GemLoader(@tag=my_engine)找到gem创建的文件。
如果使用rails配置,Zeitwerk将通过Zeitwerk::Loader(@tag=rails.main)找到rails创建的文件(使GemLoader变得不必要)。
如果lib是现有加载程序中的根目录,则不需要对lib目录中的文件有任何要求或自动加载。除了在Zeitwerk启动之前需要的东西,比如MyEngine::Engine来自lib/my_engine/engine.rb。
https://stackoverflow.com/questions/72887860
复制相似问题