Skip to content

Fix ActiveRecord STI format lookup and guard __use__ cycles - #173

Open
eitoball wants to merge 4 commits into
masterfrom
worktree-issue-161-sti-use-cycles
Open

Fix ActiveRecord STI format lookup and guard __use__ cycles#173
eitoball wants to merge 4 commits into
masterfrom
worktree-issue-161-sti-use-cycles

Conversation

@eitoball

Copy link
Copy Markdown
Contributor

What

Resolves comma_formats from the class hierarchy at call time instead of a stale snapshot, and guards Comma::Extractor#__use__ against circular style references.

Why

Closes #161. Object.comma_formats used class_attribute plus an inherited-hook shallow dup, so a subclass's format table was a snapshot frozen at the moment the subclass was defined — any later change to the superclass's comma blocks (e.g. reopening the class) silently failed to reach subclasses, including ActiveRecord STI subclasses. Separately, __use__ re-evaluated named styles with no cycle detection, so a circular :a uses :b, :b uses :a reference would stack-overflow instead of raising a clear error.

Changes

  • lib/comma/object.rb: drop class_attribute :comma_formats and the inherited dup hook; store each class's own DSL entries in a private, never-auto-inherited @own_comma_formats ivar, and resolve the effective comma_formats by walking selfsuperclass → ... at call time, merging so a subclass's own style entries override the same-named parent style while other styles still fall through. Restore an instance-level comma_formats reader (self.class.comma_formats) to preserve the pre-existing public behavior for receivers that are themselves Class objects (see the #94 regression spec).
  • lib/comma/extractor.rb: add Comma::CircularStyleReference < StandardError and a @style_stack that __use__ pushes/pops around each nested expansion, raising immediately if a style already on the stack is requested again.
  • spec/comma/comma_spec.rb: add a regression spec proving a superclass comma block reopened after a subclass is defined is still picked up (ChildClassNoComma-style, but redefining the parent block post-hoc), and two new specs for a two-style cycle and a direct self-reference, both expecting Comma::CircularStyleReference.
  • spec/comma/rails/active_record_spec.rb: remove the stale # FIXME comment on the Cat#to_comma STI example — it now passes.

Verified against the base spec suite plus the active_record-only appraisal gemfiles for 6.0.6, 6.1.7.6, 7.0.8, and 7.1.3 (all green), and rubocop on the changed files (no offenses).

Replace the class_attribute + inherited-hook dup snapshot with a
per-class instance variable and a call-time walk up the superclass
chain, so a subclass's comma_formats always reflects the current
state of its ancestors instead of a stale copy taken when the
subclass was first defined.
Cat#to_comma correctly returns Super-Kitty now that comma_formats
walks the class hierarchy at call time instead of relying on a
snapshot taken when the subclass was defined.
… stack overflow

Track the chain of styles currently being expanded and raise a
dedicated error the moment __use__ is asked to re-enter a style
already on the stack, instead of recursing until the interpreter's
stack overflows.
Use the symbol-to-proc form for the value transform and mark the
STI describe block as an accepted long block, matching the existing
convention elsewhere in this file.
Copilot AI lite review requested due to automatic review settings August 16, 2026 08:56

Copilot AI 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.

Pull request overview

This PR fixes two correctness gaps in Comma’s DSL execution: (1) STI subclasses now resolve comma formats from the live class hierarchy at call time (instead of an inherited snapshot), and (2) __use__ style composition is protected against circular references by raising a dedicated Comma::CircularStyleReference error.

Changes:

  • Reworked format storage/lookup to merge per-class format definitions across the superclass chain at call time (fixes STI/reopened-superclass cases).
  • Added circular __use__ detection with a new exception type and expanded specs to cover both indirect and direct cycles.
  • Updated ActiveRecord STI spec by removing the now-stale FIXME comment (test expectation remains the same).

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
lib/comma/object.rb Replaces inherited snapshotting with call-time hierarchy merge for comma_formats.
lib/comma/extractor.rb Adds CircularStyleReference and a style stack to detect __use__ cycles.
spec/comma/comma_spec.rb Adds regression tests for reopened superclass formats and circular __use__ chains.
spec/comma/rails/active_record_spec.rb Removes outdated comment now that STI format lookup is fixed.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread lib/comma/extractor.rb
Comment on lines +30 to +32
@style_stack.push(style)
instance_eval(&@formats[style])
@style_stack.pop
Comment thread lib/comma/object.rb
Comment on lines 49 to 52
def extract_with(extractor_class, style = :default)
raise_unless_style_exists(style)
extractor_class.new(self, style, self.comma_formats).results
extractor_class.new(self, style, comma_formats).results
end
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.

Fix ActiveRecord STI format lookup and guard __use__ cycles

2 participants