rails: StiPreload implementation from Rails Guides results in uninitialized constant errors on Rails 7
When using the StiPreload module implementation suggested in the Rails Guides it results in NameError: uninitialized constant
for typical use cases with existing STI type
data.
I believe the fact that this issue pops up with Rails 7 stems from the changes here: https://github.com/rails/rails/commit/ffae3bd8d69f9ed1ae185e960d7a38ec17118a4d .
Steps to reproduce
I’ve added a repo to demonstrate the problem here: @dwillett/rails_7_sti_preload
$ bin/rails db:create
Created database 'db/development.sqlite3'
Created database 'db/test.sqlite3'
$ bin/rails db:migrate
== 20220124235533 AddUsers: migrating =========================================
-- create_table(:users)
-> 0.0022s
== 20220124235533 AddUsers: migrated (0.0023s) ================================
$ bin/rails r "Member.create(name: 'Foo')"
$ bin/rails r "puts Member"
Member
$ SHOWCASE_BUG=true bin/rails r "puts Member"
Please specify a valid ruby command or the path of a script to run.
Run 'rails runner -h' for help.
uninitialized constant Member
When the environment variable is present, self.descendants
will be invoked as part of requiring the User
base STI class. It appears that invoking Class.descendants
while the class is being loaded has implicitly been a circular problem with the StiPreload
module (In this example: Member
autoloads User
, which invokes .descendants
which tries to constantize Member
), but with the changes in Rails 7 it now invokes that method directly when setting up association callbacks instead of ActiveSupport::DescendantsTracker.descendants(self)
.
Expected behavior
Class should be autoloaded correctly without error.
Actual behavior
Class encounters unitialized constant error when attempting to autoload and preload class.
System configuration
Rails version: 7.0.1
Ruby version: 3.0.3
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 7
- Comments: 18 (14 by maintainers)
Here’s the updated module with the proper (private API) hook:
It only aims at solving the main issue which is querying. e.g. if you have
User > Member > VIPMember
, then in autoloading mode,Member.all
don’t includeVIPMember
records unlessVIPMember
is already load.This new module does fix this issue, which is pretty much we wanted to do in https://github.com/rails/rails/pull/36487
Note that it doesn’t try to make
descendants
orsubclasses
behave like they would in an eager loaded environment, as I don’t think it’s as much of a problem.