2

我正在将一个大约 2 岁的 Rails 应用程序从 6.1 升级到 7.0.1

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

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

class Pursuit < ApplicationRecord
# ...
end

后代类都看起来像这样:

# app/models/pur_issue.rb
class PurIssue < Pursuit
# ...
end

--

# app/models/pur_goal.rb
class PurGoal < Pursuit
# ...
end

--

# app/models/pur_tracker.rb
class PurTracker < Pursuit
# ...
end

我一直在使用Ruby Guides 推荐的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

我似乎无法弄清楚为什么 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

顺便说一句,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)
4

1 回答 1

1

我看到同样的事情试图升级到 rails 7。我相信它源于这个变化:https ://github.com/rails/rails/commit/ffae3bd8d69f9ed1ae185e960d7a38ec17118a4d

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

于 2022-01-25T20:18:39.353 回答