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

Defined Under Namespace

Classes: Base, Wordlist