我有一个 Rails 7.1 应用程序和多个(Rails Engine)Gems,它们的变形规则相互冲突。
Gem1::Api::Gem1Controller
下有app/controllers/gem1/api/gem1_controller.rb
和 Gem2::API::Gem2Controller
下的 app/controllers/gem2/api/gem2_controller.rb
。
据我了解,Zeitwerk 对整个 Rails 应用程序使用全局自动加载器,包括所有 Rails 引擎(或多或少被视为主应用程序的一部分)。
默认情况下,Zeitwerk 可以加载
Gem1::Api::Gem1Controller
,但无法加载 Gem2::API::Gem2Controller
,因为缺少 "api" => "API"
的变形规则。
但是,使用添加自定义变形规则的“正常”方法将不起作用,因为那样它将无法加载
Gem1::Api::Gem1Controller
,因为它期望找到 Gem1::API::Gem1Controller
。
# config/initializers/zeitwerk.rb
Rails.application.autoloaders.each do |autoloader|
autoloader.inflector.inflect("api" => "API") # works for Gem2, but breaks Gem1 as a result
end
有没有办法定义包含整个命名空间的变形规则?类似的东西
# config/initializers/zeitwerk.rb
Rails.application.autoloaders.each do |autoloader|
autoloader.inflector.inflect("gem2/api/gem2_controller" => "Gem2::API::Gem2Controller")
end
或者也许有一种方法可以定义每个 Gem 或每个 Rails-Engine 变形器?请记住,它仍然需要作为 Rails 引擎工作。
我通过自定义 Inflector 找到了解决此问题的方法。常规的
Rails::Autoloader::Inflector#camelize
会忽略它的第二个参数,但是将绝对文件路径合并到驼峰化中使我能够同时使用 Gem1::Api
和 Gem2::API
。
# config/initializers/autoloaders.rb
class PathSuffixInflector
def initialize
@overrides = {}
end
def camelize(basename, abspath)
return basename.camelize if @overrides.empty?
filenames = Pathname.new(abspath).each_filename.to_a[0..-2] + [basename]
@max_override_depth.downto(1).each do |suffix_size|
suffix = File.join(filenames.last(suffix_size))
return @overrides[suffix] if @overrides.key?(suffix)
end
return basename.camelize
end
def inflect(overrides)
@overrides.merge!(overrides)
@max_override_depth = @overrides.keys.map { _1.count("/") }.max + 1
end
end
Rails.application.autoloaders.each do |autoloader|
autoloader.inflector = PathSuffixInflector.new
autoloader.inflector.inflect("gem2/api" => "API")
end