将 Rails 升级到 7.0.1 时,STI_preload 上突然出现“未初始化常量 PurIssue”错误

sir*_*ros 5 ruby-on-rails sti preload uninitialized-constant ruby-on-rails-7

我正在将大约 2 年历史的 Rails 应用程序从 6.1 升级到 7.0.1

我有一个 STI 设置,其中 Pursuit 是主类,它还有其他几种类型作为后代:

# app/models/pursuit.rb
require 'sti_preload'

class Pursuit < ApplicationRecord
# ...
end
Run Code Online (Sandbox Code Playgroud)

后代类看起来都是这样的:

# app/models/pur_issue.rb
class PurIssue < Pursuit
# ...
end
Run Code Online (Sandbox Code Playgroud)

--

# app/models/pur_goal.rb
class PurGoal < Pursuit
# ...
end
Run Code Online (Sandbox Code Playgroud)

--

# app/models/pur_tracker.rb
class PurTracker < Pursuit
# ...
end
Run Code Online (Sandbox Code Playgroud)

我一直按照 Ruby 指南的建议使用STI 预加载片段,并且在 Rails 6.0 和 6.1 下都运行良好。

但现在我升级到 Rails 7.0.1,突然只出现 PurIssue子类的“未初始化常量”错误,而所有其他子类都加载正常:

Showing /Users/me/gits/rffvp/app/views/pursuits/_stats.html.slim where line #1 raised:

uninitialized constant PurIssue

Extracted source (around line #33):

33 types_in_db.each do |type|
34   logger.debug("Preloading STI type #{type}")
35   type.constantize
36 end
37 logger.debug("Types in database #{types_in_db}")


Trace of template inclusion: #<ActionView::Template app/views/layouts/_footer.html.slim locals=[]>, #<ActionView::Template app/views/layouts/application.html.slim locals=[]>

Rails.root: /Users/me/gits/rffvp
Application Trace | Framework Trace | Full Trace
lib/sti_preload.rb:33:in `block in preload_sti'
lib/sti_preload.rb:31:in `each'
lib/sti_preload.rb:31:in `preload_sti'
lib/sti_preload.rb:13:in `descendants'
app/models/pursuit.rb:71:in `<class:Pursuit>'
app/models/pursuit.rb:54:in `<main>'
app/models/pur_issue.rb:52:in `<main>'
app/views/pursuits/_stats.html.slim:1
app/views/layouts/_footer.html.slim:14
app/views/layouts/application.html.slim:13
Run Code Online (Sandbox Code Playgroud)

我似乎无法弄清楚为什么 PurIssue 不再加载,而所有其他子类都会加载。这破坏了我的整个应用程序,因为 PurIssues 是最重要的数据点。

有人可以指出 6.0、6.1 和 7.0 之间可能导致这种不同行为的 Rails 配置更改吗?

# lib/sti_preload.rb
module StiPreload
  unless Rails.application.config.eager_load
    extend ActiveSupport::Concern

    included do
      cattr_accessor :preloaded, instance_accessor: false
    end

    class_methods do
      def descendants
        preload_sti unless preloaded
        super
      end

      # Constantizes all types present in the database. There might be more on
      # disk, but that does not matter in practice as far as the STI API is
      # concerned.
      #
      # Assumes store_full_sti_class is true, the default.
      def preload_sti
        types_in_db = \
          base_class
          .unscoped
          .select(inheritance_column)
          .distinct
          .pluck(inheritance_column)
          .compact

        types_in_db.each do |type|
          logger.debug("Preloading STI type #{type}")
          type.constantize
        end
        logger.debug("Types in database #{types_in_db}")

        self.preloaded = true
      end
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

Zeitwerk 顺便显示了同样的错误:

$ rails zeitwerk:check        
Hold on, I am eager loading the application.
rails aborted!
NameError: uninitialized constant PurIssue
/Users/me/gits/rffvp/lib/sti_preload.rb:33:in `block in preload_sti'
/Users/me/gits/rffvp/lib/sti_preload.rb:31:in `each'
/Users/me/gits/rffvp/lib/sti_preload.rb:31:in `preload_sti'
/Users/me/gits/rffvp/lib/sti_preload.rb:13:in `descendants'
/Users/me/gits/rffvp/app/models/concerns/validateable.rb:10:in `block in <module:Validateable>'
/Users/me/gits/rffvp/app/models/pursuit.rb:68:in `include'
/Users/me/gits/rffvp/app/models/pursuit.rb:68:in `<class:Pursuit>'
/Users/me/gits/rffvp/app/models/pursuit.rb:54:in `<main>'
/Users/me/gits/rffvp/app/models/pur_issue.rb:1:in `<main>'
Tasks: TOP => zeitwerk:check
(See full trace by running task with --trace)
Run Code Online (Sandbox Code Playgroud)

小智 3

我看到同样的事情试图升级到 Rails 7。我相信它源于此更改:https://github.com/rails/rails/commit/ffae3bd8d69f9ed1ae185e960d7a38ec17118a4d

Class.descendants实际上,在内部关联回调方法中直接调用的更改暴露了Class.descendants自动加载期间调用的更长期的隐式问题,因为StiPreload实现可能会导致循环尝试常量自动加载的原始类的问题。我在 Rails 存储库中添加了一个问题https://github.com/rails/rails/issues/44252