Skip to content

Models with encrypted columns can use Options now - #49

Merged
nz merged 3 commits into
mainfrom
fix/encrypts-exclusion
Aug 11, 2026
Merged

Models with encrypted columns can use Options now#49
nz merged 3 commits into
mainfrom
fix/encrypts-exclusion

Conversation

@nz

@nz nz commented Aug 11, 2026

Copy link
Copy Markdown
Member

Fixes #41.

Before, a model with an encrypted column could not save at all once it used this gem: every save raised an error. That is because Rails adds its own hidden length check to encrypted columns, and that check cannot read a value inside an Option. Now encrypted columns are simply left alone, so saves work. You can also name other columns to leave alone with errgonomic_optional_except.

Detail

  • ActiveRecord Encryption registers a length validator outside Model.validators (encryptable_record.rb:126-142: load_schema!validate_column_sizevalidates_length_of), whose validate_each calls to_s on the value — and Option#to_s raises by design.
  • Commit 1: the concern excludes Array(encrypted_attributes) when computing the wrapped set. That alone misses the reported case, where encrypts is declared after the include (the Set is still nil at include time), so the concern also overrides encrypts to call super then un-wrap the named reader (remove_method lets dispatch fall back to AR's attribute methods). Both declaration orders are tested.
  • Commit 2: errgonomic_optional_except :attr, ..., callable before the include, as an escape hatch for whatever conflict shows up next.
  • Commit 3: the encrypted-attribute exclusion is named compromise 5 in the concern header and README, per the compromise-list rule that new integration exceptions get a design discussion and a list entry. errgonomic_optional_except is deliberately kept off the list: it is configuration, not a semantic exception.

Review note: the encrypts override forwards Rails 8.1's (*names, **options) signature and relies on super reaching EncryptableRecord::ClassMethods; it is the most brittle piece of this batch and worth a close look.

Tests: encrypted-column model saves, encrypted reader returns the raw value, sibling nullable column still wrapped, absent encrypted value round-trips, errgonomic_optional_except skips named attributes.

nz added 3 commits August 10, 2026 19:19
Including the concern in a model that also uses encrypts made every save
raise. Encryption registers a length validator on each encrypted column,
and the validator measures the value with to_s, which an Option refuses.
The validator never appears in Model.validators, only in the runtime
callback chain, so the cause was invisible from the model.

Skip encrypted attributes when computing the wrapped set. Applications
declare encrypts after the include as often as before it, so also hook
the class method and give a late-declared attribute its plain reader
back.
Wrapping was all or nothing per model, so a single attribute the
surrounding machinery insists on reading raw blocked the whole model
from adopting the concern. errgonomic_optional_except names attributes
to leave alone. It sits with delegate_optional, on every model, because
it has to be callable before the include that computes the wrapped set.
The compromise list promised that a new integration exception gets a
design discussion and a place on the list rather than a quiet patch;
this is that entry. errgonomic_optional_except is deliberately kept
off the list as configuration rather than a semantic exception.
@nz
nz enabled auto-merge August 11, 2026 00:21

@lutzcc1 lutzcc1 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although practical, this Options exception seems a bit obscure.

@nz
nz merged commit 3d0aa21 into main Aug 11, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ActiveRecordOptional: encrypts-ed attributes make every save raise SerializeError

2 participants