我正在将一个已有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指南推荐的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)
我在尝试升级到rails 7时看到了同样的事情。我相信它源于这种变化:https://github.com/rails/rails/commit/ffae3bd8d69f9ed1ae185e960d7a38ec17118a4d
实际上,在内部关联回调方法中直接调用Class.descendants
的更改暴露了在自动加载期间调用Class.descendants
的更长期的隐式问题,因为StiPreload
实现可能会导致循环尝试保持自动加载的原始类不变的问题。我在Rails回购中添加了一个问题https://github.com/rails/rails/issues/44252