Module: Moderate::Filters
- Defined in:
- lib/moderate/filters/base.rb,
lib/moderate/filters/wordlist.rb
Overview
The built-in filter adapters live under Moderate::Filters. They're the
gem's own implementations of the ONE adapter contract the whole filtering
design hinges on:
adapter.classify(value) -> Moderate::Result
...where value is a piece of user content (a String of text, or an image
reference) and the returned Moderate::Result answers "is this allowed?" and,
if not, "why?" (the per-Moderate::Label detail, mapped onto the gem's single
canonical taxonomy — see Moderate::Label).
── How adapters are invoked ────────────────────────────────────────────────
The configuration registry (Moderate::Configuration#adapters) stores each
adapter as EITHER a live object the host registered, OR a class-NAME String the
gem constantizes lazily. The one built-in is seeded as the string
"Moderate::Filters::Wordlist", and Configuration#resolve_adapter returns the
CLASS itself for a String/Class entry. That means the gem calls
SomeAdapterClass.classify(value) and SomeAdapterClass.synchronous? — i.e. the
built-in exposes CLASS methods, not instance methods. (A host's own adapter
registered as an instance — including the reference adapters in examples/ —
exposes the same #classify/#synchronous? on that instance — same duck type,
both work.)
Base gives the built-in both halves of that duck type from a single source:
subclasses implement the work as an INSTANCE method (#classify), and Base
provides the CLASS-level classify/synchronous?/async? that the registry
resolution path calls, delegating the class call to a fresh instance. So one
implementation satisfies both call styles and there's no copy-paste. (It's the
base for the bundled wordlist; the examples/ reference adapters don't need it —
any object answering #classify is a valid adapter.)
── Sync vs. async (why it matters for :block) ──────────────────────────────
Configuration#validate! enforces the README's rule: a :block-mode filter
MUST use a synchronous adapter, because you can't reject a save on a result
that's still computing in a background job. The validator probes the adapter
with synchronous? and treats anything that doesn't answer, or answers truthy,
as synchronous (the safe default that keeps simple adapters working); only an
adapter that explicitly returns synchronous? == false is rejected for :block.
We model this once here as async? (default false — the built-in wordlist is
sync) and derive synchronous? from it, so a subclass flips ONE flag
(def self.async? = true) to declare itself background-only. The wordlist leaves
the default; a network-backed reference adapter (see examples/) declares itself
async via its own synchronous? == false, which the spine honors regardless of
whether the adapter inherits from Base.