Skip to content

Encapsulate Grape::Validations::Validators::Base state behind readers#2725

Open
ericproulx wants to merge 1 commit into
masterfrom
refactor/base-validator-encapsulation
Open

Encapsulate Grape::Validations::Validators::Base state behind readers#2725
ericproulx wants to merge 1 commit into
masterfrom
refactor/base-validator-encapsulation

Conversation

@ericproulx
Copy link
Copy Markdown
Contributor

Summary

Grape::Validations::Validators::Base exposed its state as bare instance variables that every subclass reached into directly (@options, @scope, @required, @allow_blank, @exception_message, @attrs, @fail_fast). This PR routes all internal reads through accessor methods so the ivars are an implementation detail, and gives the boolean attributes consistent ? predicates.

Pure internal refactor — no behavior change.

What changed

Base

  • Added private attr_reader :options, :scope, :required, :allow_blank, :exception_message.

  • :attrs stays a public attr_reader (left intentionally — making it private would be an externally observable change for third-party code calling validator.attrs).

  • Boolean readers now have ? aliases, consistent with the existing fail_fast?:

    attr_reader :fail_fast, :options, :scope, :required, :allow_blank, :exception_message
    alias required?    required
    alias allow_blank? allow_blank
    alias fail_fast?   fail_fast
    public :fail_fast?
  • fail_fast? was a hand-written def; it's now an attr_reader + alias like the others. Only attrs and fail_fast? stay public — they are the only readers that cross the validator boundary (Grape::Endpoint#run_validators calls validator.validate / validator.fail_fast?); everything else is now private.

Subclasses — every direct @ivar read replaced with the reader: default_validator, coerce_validator, multiple_params_base, values_validator, length_validator, oneof_validator, at_least_one_of_validator, all_or_none_of_validator. Two locals that would have shadowed the new readers were renamed (attrsattributes in DefaultValidator#validate!, scopecurrent_scope in ValuesValidator#required_for_root_scope?).

The @option alias ivar (# TODO: remove in next major release) is deliberately left untouched — it's an externally-facing deprecated alias and folding it in is a separate decision.

Visibility (verified by introspection)

method visibility
attrs, fail_fast? public
fail_fast, options, scope, required, allow_blank, exception_message, required?, allow_blank? private

Testing

  • spec/grape/validations + spec/grape/endpoint_spec.rb: 795 examples, 0 failures
  • RuboCop: clean

🤖 Generated with Claude Code

Route all internal reads of the Base validator's instance variables
through accessor methods instead of touching @Ivars directly, in Base
and every subclass.

- Add private `attr_reader :options, :scope, :required, :allow_blank,
  :exception_message`; `:attrs` stays a public reader (unchanged
  external behavior).
- Booleans get `?` aliases consistent with `fail_fast?`:
  `required?`, `allow_blank?`, `fail_fast?` (now `attr_reader` + alias).
- Only `attrs`/`fail_fast?` remain public — the readers actually
  crossing the validator boundary (Grape::Endpoint#run_validators).
- Rename locals that shadowed the new readers (`attrs`/`scope`).

Pure internal refactor: no behavior change, full validation and
endpoint suites green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ericproulx ericproulx force-pushed the refactor/base-validator-encapsulation branch from 2dbf98c to c39cc2f Compare May 16, 2026 10:02
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 16, 2026

Danger Report

No issues found.

View run

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