Module: Moderate::ContentFilterable

Extended by:
ActiveSupport::Concern
Defined in:
lib/moderate/models/concerns/content_filterable.rb

Overview

Pre-publication content filtering for a model's fields. Backs the moderates macro (and its documented equivalent, include Moderate::ContentFilterable + moderates_fields :body).

For each declared field the concern resolves the field's FilterPolicy (via Moderate.filter_policy_for, which walks the ancestor chain so an STI parent's policy covers its children) and enforces it in one of two ways, by mode:

:off   — nothing.
:block — a VALIDATION. If the classifier flags the value, the save is
       rejected with `errors.add(field, :objectionable_content)`. Runs
       synchronously, so it only works with a synchronous adapter — the
       Configuration validates that invariant (README: ":block requires a
       synchronous adapter").
:flag  — an AFTER_COMMIT side effect. The save SUCCEEDS, then (only if the
       field actually changed) the value is classified and a
       `Moderate::Flag` is filed when it trips. HOW it's classified
       depends on the adapter: a synchronous adapter (the wordlist)
       classifies inline right here; an ASYNC adapter (`synchronous? ==
       false` — any remote moderation API) is routed through
       `Moderate::ClassifyJob`, because blocking network I/O must never
       run inside the request that saved the content. The job re-reads
       the current value and files the Flag itself.

WHY :flag lives in after_commit and not in a validator (this is the whole reason :flag is a moderates mode you can't hand-roll with validates): validators must be side-effect-free, and a Flag created inside a transaction that later rolls back would silently vanish — you'd think you flagged something you didn't. after_commit guarantees the surrounding transaction committed before we write the Flag (and that a ClassifyJob never races a rollback). See docs/configuration.md (":flag never lives in a validator").

ACTIVE STORAGE ATTACHMENTS work out of the box: moderates :avatar, with: :your_image_adapter, mode: :flag on a has_one_attached :avatar model needs no extra wiring. AR dirty tracking can't see attachment writes — and Active Storage clears attachment_changes before after_commit — so the concern snapshots "these filtered attachments changed" in a before_save and consumes the snapshot at commit time. The overridable seam below still exists for richer cases (derived values, non-AS blobs).