Leave a singular association with nested attributes unwrapped - #54
Merged
Conversation
accepts_nested_attributes_for assigns through the public reader and asks whatever it finds there whether it is a new record. A None answers nil? but is not nil, so it reached that question and raised, taking out build, update and destroy for the association — on an optional belongs_to as much as on a has_one. ActiveRecord needs a bare nil here, the way it needs the raw value behind encrypts, so this takes the same shape: the association is recorded as an exclusion and keeps its plain reader.
This was referenced Aug 12, 2026
allizad
approved these changes
Aug 12, 2026
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.
Third of the stack, based on #53 — review that first, and this diff will shrink to its own commit once #53 merges.
Nested attributes on a singular association raise today
accepts_nested_attributes_forassigns through the public reader, and ActiveRecord asks whatever it finds there whether it is a new record. ANoneanswersnil?but is notnil, so it reaches that question and raises:This is not new to the
has_onework in #53: it is true on the released gem for anyoptional: true belongs_to, and it takes out build, update and destroy for the association. In the application driving this rollout it blocks six models that are otherwise ready —account.service_agreement,order_form_item.price,price.plan,cluster.space,cluster.vault,member_invitation.user.Same shape as the encrypts exclusion
ActiveRecord needs a bare
nilhere, the way its encryption machinery needs the raw value behindencrypts. So this takes the shape already established for that: the association is excluded and keeps its plain reader.The exclusion is read from
nested_attributes_options, ActiveRecord's own register, rather than recorded as the macro goes past — the same way theencryptsexclusion is read fromencrypted_attributes. That matters after #52: exclusions are computed when a reader is about to be wrapped, so anything that tried to append to the computed list would be silently dropped.The concern's compromise 5 is reworded from "attributes declared with encrypts" to the general rule it now expresses: readers that ActiveRecord's own machinery reads raw. Still five compromises, not six.
Testing
rake— 30 tests, 81 assertions, and 118 doctests, all passing. The new cases cover the full nested cycle on ahas_one(build, update, destroy), build on anoptional: true belongs_to, and thaterrgonomic_optionalsanderrgonomic_optional_exclusionsreport the association as unwrapped so a conversion can be checked.