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).