Load the record class when Active Record loads - #44
Merged
Conversation
The engine required app/models/solid_objects/record.rb at the top of the file, so `require "solid_objects"` defined a subclass of ActiveRecord::Base. Bundler.require runs that before the host application boots, which loaded ActiveRecord::Base during railtie setup instead of on first use. That moved every deferred on_load(:active_record) hook in the host application forward. The Active Record railtie registers its encryption configuration inside on_load(:active_record_encryption), and the constant was already loaded, so the hook ran at once, before config/initializers. An application that assigns its encryption keys in an initializer got Encryption.configure with nils, and every encrypted attribute then raised a missing credential error far from the cause. The engine now requires the record class from an on_load(:active_record) hook, so nothing touches ActiveRecord::Base until the application does. The record class also configures its own connection now. The initializer could no longer call configure_connection: Zeitwerk can load record.rb first, and the superclass expression there is what loads ActiveRecord::Base, so the hook would run while SolidObjects::Record was still undefined. Reading the configuration in the class body also survives a development reload, which the initializer did not. Fixes #38
Greptile SummaryThe PR defers loading
Confidence Score: 5/5The PR appears safe to merge with no actionable correctness, security, or repository-rule violations identified. The new hook preserves Active Record’s deferred initialization, the class-body connection setup handles both hook-driven and autoload-driven class definition, and the packaged gem retains the required model path. Important Files Changed
Sequence DiagramsequenceDiagram
participant Host as Host application
participant Engine as SolidObjects::Engine
participant Init as config/initializers
participant AR as Active Record
participant Record as SolidObjects::Record
Host->>Engine: Load Solid Objects engine
Engine->>AR: Register on_load(:active_record)
Host->>Init: Run application initializers
Init->>Host: Set encryption keys and connects_to
Host->>AR: Load ActiveRecord::Base
AR->>Engine: Run active_record load hook
Engine->>Record: Require record.rb
Record->>Record: configure_connection
Reviews (1): Last reviewed commit: "Load the record class when Active Record..." | Re-trigger Greptile |
Bumps the version constant, dates the changelog section for the deferred Active Record load, and refreshes the lockfile.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #38.
Cause
lib/solid_objects/engine.rbrequiredapp/models/solid_objects/record.rbatthe top of the file, and
SolidObjects::Recordis a subclass ofActiveRecord::Base.Bundler.requireruns that before the host applicationboots, so
ActiveRecord::Baseloaded during railtie setup instead of on firstuse.
That moved every deferred
on_load(:active_record)hook in the hostapplication forward. The Active Record railtie registers its encryption
configuration inside
on_load(:active_record_encryption). The constant wasalready loaded, so that hook ran at once, before
config/initializers. Anapplication that assigns its encryption keys in an initializer got
Encryption.configurewith nils, and the later assignment was discarded.Change
ActiveSupport.on_load(:active_record)hook, so nothing touches
ActiveRecord::Baseuntil the application does.connects_toin its class body.The initializer could no longer call
configure_connection: Zeitwerk canload
record.rbfirst, and the superclass expression there is what loadsActiveRecord::Base, so the hook would run whileSolidObjects::Recordwasstill undefined. Reading the configuration in the class body also survives a
development reload, which the initializer did not.
Tests
test/integration/active_record_load_timing_test.rbis new. One test asks afresh process whether
require "solid_objects"leaves the:active_recordload hooks deferred. The other boots the dummy application, which now assigns
encryption keys in
config/initializers, and reads the key back.test/integration/engine_test.rbgains a test that boots the dummyapplication with
connects_toconfigured in an initializer and asserts therecord class owns its own connection pool. That test passes on
main, so itrecords the behaviour the class-body change had to keep.
Observed failures before the fix:
With the engine fix in place but
configure_connectionremoved from the recordclass body:
Validation
bundle exec rake(tests, Standard Ruby, RuboCop, RBS, Steep, Brakeman):535 runs, 1768 assertions, 0 failures, 0 errors, 15 skips. The skip count
matches
main.