Rails 7.1 解决了相互冲突的 Zeitwerk 变形规则

问题描述 投票:0回答:1

我有一个 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 引擎工作。

ruby-on-rails autoload rails-engines zeitwerk inflector
1个回答
0
投票

我通过自定义 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
© www.soinside.com 2019 - 2024. All rights reserved.