Skip to content

Guard enum_for's pluralized accessor with respond_to? - #77

Merged
VSN2015 merged 1 commit into
masterfrom
fix/enum-for-respond-to-guard
Sep 29, 2026
Merged

VSN2015 merged 1 commit into
masterfrom
fix/enum-for-respond-to-guard

Conversation

@VSN2015

@VSN2015 VSN2015 commented Sep 28, 2026

Copy link
Copy Markdown
Owner

The bug

Generator#enum_for (lib/permittable/generator.rb, around line 517) decides how to reference an enum's keys in the drafted code — either the model's pluralized class accessor (Model.statuses.keys) or the always-safe Model.defined_enums["status"].keys fallback. It picks between them by checking only that the pluralized field name matches a Ruby identifier regex (METHOD_NAME).

This duplicates Permittable::ColumnGuard#enum_keys_expr (lib/permittable/column_guard.rb), which makes the identical choice for its own error messages — but that sibling copy also checks klass.respond_to?(reader), and enum_for does not.

A field name whose plural form matches the identifier regex but has no actual accessor on the model — the enum accessor was renamed or otherwise excluded — made enum_for still emit Model.pluralized_name.keys. That code gets written straight into the generated draft and raises NoMethodError the moment the draft loads, even though ColumnGuard's parallel logic already guards against exactly this case.

The fix

Add the same model.respond_to?(plural) check enum_keys_expr already carries, right alongside the existing identifier check, before emitting the pluralized-accessor expression:

accessor = if METHOD_NAME.match?(plural) && model.respond_to?(plural)
             "#{model.name}.#{plural}"
           else
             "#{model.name}.defined_enums[#{name.inspect}]"
           end

When the accessor isn't callable, enum_for now falls back to the same defined_enums[...] spelling enum_keys_expr uses in its own not-respond_to? case, which is always safe to call.

Verification

  • New test written first (spec/generator_spec.rb): an enum whose pluralized name (statuses) is undefined on the model (simulating a renamed/excluded accessor) — confirmed to fail before the fix (drafted GenOrderNoReader.statuses.keys, which the loaded draft can't answer to) and pass after (drafts GenOrderNoReader.defined_enums["status"].keys instead, and the loaded draft correctly accepts/rejects enum values)
  • bundle exec rspec — 852 examples, 0 failures
  • bundle exec rubocop lib/permittable/generator.rb spec/generator_spec.rb — no offenses

🤖 Generated with Claude Code

`Generator#enum_for` decided whether to emit `Model.pluralized_name.keys`
by checking only that the pluralized name matched a Ruby identifier
regex, duplicating (but not fully mirroring) ColumnGuard's
`enum_keys_expr`. A field whose plural form is a valid identifier but
has no actual accessor on the model — renamed or otherwise excluded —
made the generator emit a call that raises NoMethodError when the
draft loads, a case ColumnGuard's sibling code already guards against
via `klass.respond_to?(reader)`.

Add the same respond_to? check before emitting the pluralized-accessor
expression, falling back to the `defined_enums[...]` spelling exactly
as enum_keys_expr does.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@VSN2015
VSN2015 merged commit fa84b23 into master Sep 29, 2026
16 checks 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.

1 participant