Skip to content

Find the record's own row regardless of default scopes and key changes - #70

Merged
brendon merged 4 commits into
mainfrom
fix/record-scope
Oct 22, 2026
Merged

brendon merged 4 commits into
mainfrom
fix/record-scope

Conversation

@brendon

@brendon brendon commented Oct 16, 2026

Copy link
Copy Markdown
Owner

record_scope is how Mechanisms reads and moves the record's own row (position_was, move_out_of_the_way, and its previous scope and parent). It applied the model's default scope and used the in-memory primary key, which breaks in three ways. All three fail on main:

  1. Restoring a record hidden by a default scope. This is the soft-delete workaround from Positioning fails when recovering soft-deleted records with acts_as_paranoid #59: put the timestamp in the scope (positioned on: [:list, :archived_at]) alongside default_scope { where(archived_at: nil) }. Archiving works, but restoring raises NoMethodError: undefined method 'list' for nil, because the default scope filters out the record's own row.
  2. A default scope select that omits the scope columns (like the existing DefaultScopeItem fixture) raises ActiveModel::MissingAttributeError: missing attribute 'list_id' on any scope change.
  3. Changing part of a composite primary key in the same save as the position can't find the row and raises NoMethodError: undefined method '>' for nil.

Fix

base_class.unscoped.where(primary_key => [@positioned.id_in_database]), plus id_in_database in in_positioning_scope?.

This only affects queries about the record's own row. The list scope (positioning_scope) still applies the default scope. Whether it should is a separate question (multi-tenant default scopes, for example) that this doesn't touch.

Changelog: Fix scope changes for records hidden by, or partially selected by, a default scope, and when changing part of a composite primary key.

🤖 Generated with Claude Code

`record_scope` is how Mechanisms reads and moves the record's own row,
but it applied the model's default scope and used the in-memory primary
key:

- A default scope `where` that hides the row (e.g. the archived/soft
  deleted pattern from #59, with the timestamp in the positioning scope)
  meant restoring a record raised NoMethodError on nil.
- A default scope `select` that omits the scope columns raised
  MissingAttributeError on any scope change.
- Changing part of a composite primary key in the same save as the
  position meant the row couldn't be found and update_position raised.

Use `unscoped` and `id_in_database`; this only affects queries for the
record's own row, not the list scope.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brendon and others added 3 commits October 19, 2026 11:21
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@brendon
brendon merged commit 852651d into main Oct 22, 2026
36 checks passed
@brendon
brendon deleted the fix/record-scope branch October 22, 2026 01:45
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.

1 participant