Summary
lib/solid_objects/engine.rb loads SolidObjects::Record (a subclass of ActiveRecord::Base) at require time:
# lib/solid_objects/engine.rb
require_relative "../../app/models/solid_objects/record"
Because lib/solid_objects.rb is required during Bundler.require, this forces ActiveRecord::Base to load before the host Rails app boots. That changes the timing of every ActiveSupport.on_load(:active_record) / on_load(:active_record_encryption) hook in the host app from "first real AR use, after initializers" to "immediately, during railtie setup" — with at least one concrete breakage:
Observed breakage: Active Record encryption silently loses its keys
Rails applies encryption config via (activerecord/lib/active_record/railtie.rb):
initializer "active_record_encryption.configuration" do |app|
ActiveSupport.on_load(:active_record_encryption) do
ActiveRecord::Encryption.configure(
primary_key: app.credentials.dig(:active_record_encryption, :primary_key),
...
**app.config.active_record.encryption
)
end
end
Normally that hook fires on first AR use, long after config/initializers/* have populated config.active_record.encryption. With solid_objects in the bundle, AR is already loaded when the railtie registers the hook, so it fires right there — before config/initializers run. Any app that assigns its encryption keys in an initializer (a common pattern, e.g. to support env-var key rotation with a credentials fallback) gets Encryption.configure called with nils, the initializer's later assignment is silently discarded, and every encrypted attribute raises at first use:
ActiveRecord::Encryption::Errors::Configuration:
Missing Active Record encryption credential: active_record_encryption.deterministic_key
The failure is far from the cause (model specs unrelated to solid_objects), which made it expensive to trace. Verified on solid_objects 0.13.0, Rails 8.1.3.1, Ruby 4.0.6: adding the gem to the Gemfile breaks these specs with no other change; removing it (or moving the app's key assignment into the application class body) fixes them.
Encryption is just the visible symptom — pre-loading AR at require time shifts the semantics of every deferred on_load(:active_record) hook in the host app and other gems.
Suggested fix
Defer the model load until Active Record actually loads, the way engines conventionally do:
# lib/solid_objects/engine.rb
module SolidObjects
class Engine < ::Rails::Engine
...
initializer "solid_objects.record" do
ActiveSupport.on_load(:active_record) do
require_relative "../../app/models/solid_objects/record"
end
end
end
end
(and similarly audit lib/solid_objects.rb's top-level require "active_record" — requiring the framework is fine, but nothing should touch ActiveRecord::Base until on_load fires). The other models under app/models/solid_objects/ appear to load lazily via the engine's autoload paths already; Record is the one eager exception.
Host-app workaround
Move encryption key assignment out of config/initializers into the Application class body in config/application.rb, which executes before railtie initializers regardless of AR load timing.
Found while evaluating solid_objects for a production Rails 8.1 e-commerce app (the payment-link recovery actor worked beautifully otherwise — thanks for the library).
Summary
lib/solid_objects/engine.rbloadsSolidObjects::Record(a subclass ofActiveRecord::Base) at require time:Because
lib/solid_objects.rbis required duringBundler.require, this forces ActiveRecord::Base to load before the host Rails app boots. That changes the timing of everyActiveSupport.on_load(:active_record)/on_load(:active_record_encryption)hook in the host app from "first real AR use, after initializers" to "immediately, during railtie setup" — with at least one concrete breakage:Observed breakage: Active Record encryption silently loses its keys
Rails applies encryption config via (
activerecord/lib/active_record/railtie.rb):Normally that hook fires on first AR use, long after
config/initializers/*have populatedconfig.active_record.encryption. With solid_objects in the bundle, AR is already loaded when the railtie registers the hook, so it fires right there — beforeconfig/initializersrun. Any app that assigns its encryption keys in an initializer (a common pattern, e.g. to support env-var key rotation with a credentials fallback) getsEncryption.configurecalled with nils, the initializer's later assignment is silently discarded, and every encrypted attribute raises at first use:The failure is far from the cause (model specs unrelated to solid_objects), which made it expensive to trace. Verified on solid_objects 0.13.0, Rails 8.1.3.1, Ruby 4.0.6: adding the gem to the Gemfile breaks these specs with no other change; removing it (or moving the app's key assignment into the application class body) fixes them.
Encryption is just the visible symptom — pre-loading AR at require time shifts the semantics of every deferred
on_load(:active_record)hook in the host app and other gems.Suggested fix
Defer the model load until Active Record actually loads, the way engines conventionally do:
(and similarly audit
lib/solid_objects.rb's top-levelrequire "active_record"— requiring the framework is fine, but nothing should touchActiveRecord::Baseuntil on_load fires). The other models underapp/models/solid_objects/appear to load lazily via the engine's autoload paths already;Recordis the one eager exception.Host-app workaround
Move encryption key assignment out of
config/initializersinto theApplicationclass body inconfig/application.rb, which executes before railtie initializers regardless of AR load timing.Found while evaluating solid_objects for a production Rails 8.1 e-commerce app (the payment-link recovery actor worked beautifully otherwise — thanks for the library).