Module: Ruact::ValidationErrorsCollector

Extended by:
ActiveSupport::Concern
Included in:
Controller, Server
Defined in:
lib/ruact/validation_errors_collector.rb

Overview

Story 13.3 (FR98) — the per-request validation-error collector, shared by Server (Bucket-2 JSON-body injection + redirect flash stash) and Controller (page-render errors prop + redirect-back flash read).

This is the opt-in half of the Inertia-style errors round-trip (design (B), the explicit-allowlist grain that 13.1/13.2 reinforced — NO auto-injection, NO globally reserved prop on every response). A host action opts in with a single call after its save attempt:

def create
@post = Post.new(post_params)
if @post.save
  redirect_to @post
else
  ruact_errors(@post)            # registers {attr => [full messages]}
  render :new                    # (Bucket 1) or fall through (Bucket 2)
end
end

Because the canonical shape derives from record.errors (empty on a valid record), the SAME ruact_errors(@post) call yields {} on success and the populated map on failure — the "always present, same code path" invariant of AC1, delivered per-action without reserving a global prop name or disturbing the Story 9.2 Bucket-2 204 No Content contract for an untouched collector.

The reader form (no argument) returns the always-present hash for binding to a form component on the page render:

#ruact_errors />

Constant Summary collapse

RUACT_ERRORS_FLASH_KEY =

Flash key the Bucket-1 redirect-back round-trip stashes the canonical errors under (single-use, session-backed — the exact Inertia semantics).

:ruact_errors
RESERVED_ASSIGN_KEYS =

view_assigns keys (ivar names without the leading @) that must NEVER serialize into a Bucket-2 body: the two collector internals (Rails only filters a fixed set of @_-named ivars, not every @__-prefixed one), and the reserved errors output key itself (a stray dev ivar literally named @errors is dropped so it can neither clobber the round-trip key nor trip strict serialization — the collector is the source of truth).

%w[__ruact_errors __ruact_errors_touched errors].freeze

Instance Method Summary collapse

Instance Method Details

#ruact_errors(record) ⇒ Hash{String=>Array<String>} #ruact_errorsHash{String=>Array<String>}

Dual-purpose helper.

ruact_errors(@post)  → COLLECT: normalize the record's errors into the
canonical `{attr => [full messages]}` shape, merge it into the
per-request collector, mark the collector touched, and return the
normalized hash (so it can also be used inline).
ruact_errors         → READ: return the always-present collector (`{}` by
default), for binding to a form component on the page render.

Merging across multiple records is additive per attribute (later calls union their messages onto earlier ones), so an action validating several records surfaces them under one errors object.

Overloads:

  • #ruact_errors(record) ⇒ Hash{String=>Array<String>}

    Returns the normalized errors for record.

    Parameters:

    • record (#errors, ActiveModel::Errors, Hash, nil)

      the failed record

    Returns:

    • (Hash{String=>Array<String>})

      the normalized errors for record

  • #ruact_errorsHash{String=>Array<String>}

    Returns the always-present collector.

    Returns:

    • (Hash{String=>Array<String>})

      the always-present collector



82
83
84
85
86
87
88
89
90
91
# File 'lib/ruact/validation_errors_collector.rb', line 82

def ruact_errors(record = NOT_GIVEN)
  return __ruact_errors if record.equal?(NOT_GIVEN)

  normalized = ServerFunctions::ValidationErrors.normalize(record)
  @__ruact_errors_touched = true
  __ruact_errors.merge!(normalized) do |_attribute, existing, incoming|
    existing | incoming
  end
  normalized
end