提问人:phoxley 提问时间:8/5/2020 更新时间:7/20/2022 访问量:2245
使用 Zeitwerk 自动加载器在 Rails 6 中命名服务对象
Namespacing service objects in Rails 6 with Zeitwerk autoloader
问:
Rails 6 切换到 Zeitwerk 作为默认的自动加载器。Zeitwerk 将加载 /app 文件夹中的所有文件,无需命名空间。这意味着,现在可以直接调用 app/services/demo/test_service.rb 中的 TestService 服务对象,例如 .TestService.new().call
然而,命名空间有助于在更复杂的 rails 应用程序中组织对象,例如 API::UsersController,或者对于我们使用 Registration::CreateAccount、Registration::AddDemoData 等的服务。
rails 指南建议的一种解决方案是从 application.rb 中的自动加载器路径中删除路径,例如 .然而,这感觉就像一个猴子补丁,用于硬塞旧方式或将对象组织到新的轨道方式中。config.autoload_paths -= Dir["#{config.root}/app/services/demo/"]
命名对象的正确方法是什么,或者是 rails 6 组织它的正确方法,而不仅仅是强迫 rails 采用旧方式?
答:
说Zeitwerk消除了“命名空间的需要”是不正确的。Zeitwerk 确实会自动加载 (除了 、 和 )。下面的任何目录都将加载到“root”命名空间中。但是,Zeitwerk 还为这些根目录下的任何目录“自动激活”模块。所以:app
assets
javascripts
views
app
/models/foo.rb => Foo
/services/bar.rb => Bar
/services/registration/add_demo_data.rb => Registration::AddDemoData
如果您已经习惯于从“非标准”目录加载常量(通过添加 ),通常不会有太大变化。不过,有几种情况确实需要一些调整。第一种是迁移仅将自身添加到自动加载路径的项目的位置。在经典(Rails 6 之前)中,这允许你使用 to contain ,而在 Zeitwerk 中,它希望它只包含 。这就是您上面提到的情况,建议从自动加载路径中排除该目录。另一种选择是简单地添加一个包装器目录,例如 .config.autoload_paths
app
app/api/base.rb
API::Base
Base
app/api/api/base.rb
第二个需要注意的问题是Zeitwerk如何从文件名推断常量。从 Rails 迁移指南中:
classic
模式从缺少的常量名称推断文件名 (下划线),而 Zeitwerk 模式从文件中推断常量名称 名称(骆驼)。这些帮助者并不总是彼此相反, 特别是如果涉及首字母缩略词。例如,是 ,但 是 ,不是 。"FOO".underscore
"foo"
"foo".camelize
"Foo"
"FOO"
所以,实际上等同于Zeitwerk,而不是./api/api/base.rb
Api::Base
API::Base
Zeitwerk 包含一个 rake 任务,用于验证项目中的自动加载:
% bin/rails zeitwerk:check
Hold on, I am eager loading the application.
expected file app/api/base.rb to define constant Base
评论
bin/rails Zeitwerk:check
bin/rails zeitwerk:check
编辑:
正如评论中所澄清的,您实际上不需要向 .这是 Zeitwerk 在 Rails 中的默认行为,当您将代码放在 的某个子目录下时。autoload_paths
app
原文答案:
我发布了单独的答案,但实际上接受的答案包含所有好信息。由于我的评论比允许的要大,我选择为那些正在为类似问题而苦苦挣扎的人添加单独的答案。
我们在应用程序下创建了“组件”,其中我们分隔了特定于域的命名空间/包。它们与一些“非组件”Rails 部件共存,这些部件很难在组件下移动。使用经典的自动加载器,我们添加了autoload_paths。#{config.root}/app
Zeitwerk 的此设置失败,从autoload_paths中删除也无济于事。rmlockerd 建议移动到 下 感动了我的想法,在此目录下创建单独的和移动所有组件,并将此路径添加到 .Zeitwerk 已点赞。"#{config.root}/app"
app/api/
/app/api/api
'app/components'
autoload_paths
评论
app/components
app/components
评论