在一个测试应用程序(Rails 7.0,Ruby 3.2)中,我目前正在试验一种不同于默认结构的模型。想法是有这样的东西,模型的所有相关代码都在一起,每个组对支持文件使用相同的命名:
app/
└── models/
├── accounts
│ ├── account.rb
│ └── support
│ └── greetable.rb
└── users
├── user.rb
└── support
└── greetable.rb
这就是我对这个设置文件的想象:
# models/accounts/account.rb
class Account < ApplicationRecord
include Greetable
end
# models/accounts/support/greetable.rb
module Greetable
def greet
"Hello, Account!"
end
end
# models/users/user.rb
class User < ApplicationRecord
include Greetable
end
# models/users/support/greetable.rb
module Greetable
def greet
"Hello, User!"
end
end
知道如何为此设置定义自动加载配置吗?希望 Zeitwerk 发挥它的魔力并自动找到最近的 Greetable 模块,但这不是它的工作方式。
非常感谢围绕此主题的所有想法/想法/讨论。
Zeitwerk 的基本前提是根目录(在 Rails 中称为“自动加载路径”)代表一个具体的、固定的名称空间。默认情况下,该命名空间是顶级命名空间,
Object
.
该子树中的预期命名是相对于该根的。因此,在默认设置下,
app/models
是 Object
,而 app/models/accounts
是 Accounts
.
Zeitwerk 的一个特点是为了组织目的将文件分组在一起,而不引入名称空间。那就是崩溃.
你想完成的事情可以通过折叠(未经测试)来实现:
# config/initializers/autoloading.rb
main = Rails.autoloaders.main
main.collapse("#{Rails.root}/app/models/*")
main.collapse("#{Rails.root}/app/models/*/support")
如文档所述,这些 glob 模式会在启动时进行评估,并在每次重新加载时再次评估,因此如果您在
app/models
下添加新组,它们将被拾取。
你不需要配置任何东西。你只需要了解它是如何工作的。
Zeitwerk 的工作方式是它希望自动加载根目录(任何子文件夹或应用程序和
/app/models/concerns
,/app/controllers/concerns
)的子目录中的任何文件嵌套在模块(或类)中,名称根据文件夹:
# models/users/support/greetable.rb
module Users
module Support
module Greetable
def greet
"Hello, User!"
end
end
end
end
Zeitwerk 将自动创建隐式名称空间(模块),因此即使您的文件仅包含:
module Greetable
def greet
"Hello, User!"
end
end
Zeitwerk 将常量嵌套为
Users::Support::Greetable
.
这不仅是 Zeitwerk 所期望的,也是其他开发人员所期望的,并且是组织代码的公认方法,因为模块嵌套提供的封装可防止冲突。这实际上也不是 Zeitwerk 独有的东西——它实际上只是编纂了在它发明之前很久就使用的约定。
将所有内容放入 Rails 中的全局命名空间是我们为了方便而做的事情,同时忽略了我们脑海中响起的警钟。它远非组织代码的理想方式。
虽然您可以从组织的角度通过使用折叠或添加额外的自动加载根来使这个嵌套文件常量成为顶级常量,但我真的会质疑为什么您会想要像这样违反最小惊喜原则,因为它没有提供任何实际好处。