I am in the process of upgrading a ~2 year old Rails app from 6.1 to 7.0.1
I have an STI setup where Pursuit is the main class and it has several other types as descendants:
# app/models/pursuit.rb
require 'sti_preload'
class Pursuit < ApplicationRecord
# ...
end
Descendant classes all look like this:
# 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
I have been using STI preload snippet as recommended by the Ruby Guides, and all worked well under Rails 6.0 and 6.1.
But now that I am upgrading to Rails 7.0.1, I suddenly get an "Uninitialized Constant" error for only the PurIssue subclass whereas all the other subclasses load fine:
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
I can't seem to figure out why PurIssue will not load anymore whereas all the other subclasses will. This is breaking my entire app since PurIssues are the most important data points.
Can someone point me to a Rails configuration change between 6.0, 6.1 and 7.0 that might cause this different behavior?
# 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 shows the same error by the way:
$ 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)