Class: Moderate::Flag
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Moderate::Flag
- Defined in:
- lib/moderate/models/flag.rb
Overview
A system/auto-filter flag: the record an adapter (or a manual action) leaves behind when content is flagged for review rather than rejected outright.
Flags are what :flag-mode filtering produces AFTER COMMIT — the write
succeeds, then a Flag lands in the queue. This is intentional and load-bearing:
a flag must NEVER be created inside a validator/transaction, because a validator
is supposed to be side-effect-free and a Flag written inside a transaction that
later rolls back would silently vanish (you'd "moderate" content that was never
saved). The Filterable concern enforces the after_commit timing; this model is
just the record + the queue + the "content flagged" notification.
The same pending scope serves BOTH a human admin queue and an automated
consumer (e.g. a job that auto-actions high-confidence flags), so the gem
doesn't presume a human is the only reviewer.
Constant Summary collapse
- STATUSES =
%w[pending actioned dismissed].freeze
- SOURCES =
Built-in/generic source names. Host-registered adapter names are also valid sources (see
.sourcesbelow) becauseModerate.classifystamps the adapter name onto the Result when the adapter does not set one explicitly. This is why a host can register:openaior:imageand see that exact name in the queue. %w[text_filter image_filter external_classifier manual].freeze
- MODES =
What the flag WOULD do.
:flagallowed the write and queued it;:blockrejected the write (a block-mode trip can also be recorded as a flag for the audit trail). Validated by the model (inclusion), not a DB constraint. %w[flag block].freeze
Class Method Summary collapse
-
.flag!(flaggable:, field:, owner:, source:, mode:, excerpt:, categories:, scores:, context:) ⇒ Object
The single entry point the Filterable concern uses to file a flag.
- .sources ⇒ Object
Instance Method Summary collapse
-
#action!(note:, by: nil) ⇒ Object
Close this flag as ACTIONED — someone (or an automated policy) took action on the flagged content.
- #closed? ⇒ Boolean
-
#dismiss!(note:, by: nil) ⇒ Object
Close this flag with NO action taken.
-
#flaggable_label ⇒ Object
A label for the flagged thing in the queue.
- #pending? ⇒ Boolean
Class Method Details
.flag!(flaggable:, field:, owner:, source:, mode:, excerpt:, categories:, scores:, context:) ⇒ Object
The single entry point the Filterable concern uses to file a flag. Centralizes
coercion (categories → Array, scores/context → Hash) so callers can hand us
whatever shape a Moderate::Result exposed without each one re-normalizing.
context is freeform JSON for audit (which policy fired, the raw classifier
payload, etc.) — never relied on by the gem's own logic.
74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/moderate/models/flag.rb', line 74 def self.flag!(flaggable:, field:, owner:, source:, mode:, excerpt:, categories:, scores:, context:) create!( flaggable: flaggable, field: field, owner: owner, source: source, mode: mode, excerpt: excerpt, categories: Array(categories), scores: scores.to_h, context: context.to_h ) end |
Instance Method Details
#action!(note:, by: nil) ⇒ Object
Close this flag as ACTIONED — someone (or an automated policy) took
action on the flagged content. Model-level sugar mirroring
Report#resolve!/#dismiss! so hosts stop hand-writing status updates.
by: is the reviewing user when a human decided; leave it nil for
automated closes — recording a human who never looked would poison the
audit trail (DSA statements of reasons read these rows).
106 107 108 |
# File 'lib/moderate/models/flag.rb', line 106 def action!(note:, by: nil) close!(new_status: "actioned", note: note, by: by) end |
#closed? ⇒ Boolean
96 97 98 |
# File 'lib/moderate/models/flag.rb', line 96 def closed? status.in?(%w[actioned dismissed]) end |
#dismiss!(note:, by: nil) ⇒ Object
Close this flag with NO action taken. The canonical automated use is
SUPERSEDED content: when the flagged field changes (text edited, photo
replaced/reverted), a still-pending flag is about content that is no
longer live — leaving it pending keeps flagged? true and mislabels
the NEW content in any host UI keyed on it. Hosts should dismiss with a
note saying what superseded it.
116 117 118 |
# File 'lib/moderate/models/flag.rb', line 116 def dismiss!(note:, by: nil) close!(new_status: "dismissed", note: note, by: by) end |
#flaggable_label ⇒ Object
A label for the flagged thing in the queue. Asks the flaggable for its own
moderation_label (Moderate::Reportable interface); falls back to a generic
"Type id" string for content that doesn't implement it.
123 124 125 126 127 |
# File 'lib/moderate/models/flag.rb', line 123 def flaggable_label return flaggable.moderation_label if flaggable.respond_to?(:moderation_label) "#{flaggable_type} #{flaggable_id}" end |
#pending? ⇒ Boolean
92 93 94 |
# File 'lib/moderate/models/flag.rb', line 92 def pending? status == "pending" end |