Class: Moderate::Result
- Inherits:
-
Data
- Object
- Data
- Moderate::Result
- Defined in:
- lib/moderate/result.rb
Overview
The single return type of every filter adapter: ‘adapter.classify(value) → Moderate::Result`.
This is the gem’s content-filtering value object — immutable, frozen at construction. It answers the two questions the rest of the gem asks (“is this allowed?” and “if not, why?”) and carries the per-label detail for the moderation queue, the DSA statement of reasons, and the transparency counters.
The public surface follows the README’s “Content filtering” section verbatim:
result.allowed? # => false
result.flagged? # => true (the inverse — convenience for the validator)
result.categories # => [:hate, :"hate/threatening"] (canonical slugs)
result.scores # => { "hate" => 0.97, "hate/threatening" => 0.81 }
result.labels # => [#<Moderate::Label ...>, ...]
result.source # => "wordlist" / "openai" / your adapter name
result.raw # => the untouched provider response (for debugging/audit)
Built on ‘Data.define` (Ruby 3.2+) for a frozen value object. We expose a keyword `.new` whose contract is forgiving: an adapter can hand us either a rich `labels:` array OR the flatter `categories:`/`scores:` shape (the simpler shape a deterministic adapter naturally returns), and we reconcile both into a coherent Result.
Instance Attribute Summary collapse
-
#allowed ⇒ Object
readonly
Returns the value of attribute allowed.
-
#labels ⇒ Object
readonly
Returns the value of attribute labels.
-
#raw ⇒ Object
readonly
Returns the value of attribute raw.
-
#source ⇒ Object
readonly
Returns the value of attribute source.
Class Method Summary collapse
-
.allowed(source: nil, raw: nil) ⇒ Object
Convenience builder for the most common deterministic case: nothing matched.
Instance Method Summary collapse
- #allowed? ⇒ Boolean
-
#categories ⇒ Object
Canonical category slugs as symbols, e.g.
-
#flagged? ⇒ Boolean
The inverse of ‘allowed?`.
-
#initialize(allowed: nil, labels: nil, categories: nil, scores: nil, source: nil, raw: nil) ⇒ Result
constructor
A new instance of Result.
-
#scores ⇒ Object
slug => score, e.g.
Constructor Details
#initialize(allowed: nil, labels: nil, categories: nil, scores: nil, source: nil, raw: nil) ⇒ Result
Returns a new instance of Result.
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/moderate/result.rb', line 42 def initialize(allowed: nil, labels: nil, categories: nil, scores: nil, source: nil, raw: nil) scores_hash = normalize_scores(scores) built_labels = build_labels(labels, categories, scores_hash) # Infer `allowed` when the adapter didn't say: any flagged label ⇒ denied. # This lets a deterministic adapter return just `categories: [...]` and have # the verdict fall out correctly, without having to compute `allowed` itself. resolved_allowed = if allowed.nil? built_labels.none?(&:flagged) else allowed ? true : false end super( allowed: resolved_allowed, labels: built_labels.freeze, source: (source || "unknown").to_s, raw: raw ) end |
Instance Attribute Details
#allowed ⇒ Object (readonly)
Returns the value of attribute allowed
27 28 29 |
# File 'lib/moderate/result.rb', line 27 def allowed @allowed end |
#labels ⇒ Object (readonly)
Returns the value of attribute labels
27 28 29 |
# File 'lib/moderate/result.rb', line 27 def labels @labels end |
#raw ⇒ Object (readonly)
Returns the value of attribute raw
27 28 29 |
# File 'lib/moderate/result.rb', line 27 def raw @raw end |
#source ⇒ Object (readonly)
Returns the value of attribute source
27 28 29 |
# File 'lib/moderate/result.rb', line 27 def source @source end |
Class Method Details
.allowed(source: nil, raw: nil) ⇒ Object
Convenience builder for the most common deterministic case: nothing matched.
65 66 67 |
# File 'lib/moderate/result.rb', line 65 def self.allowed(source: nil, raw: nil) new(allowed: true, labels: [], source: source, raw: raw) end |
Instance Method Details
#allowed? ⇒ Boolean
69 |
# File 'lib/moderate/result.rb', line 69 def allowed? = allowed |
#categories ⇒ Object
Canonical category slugs as symbols, e.g. [:hate, :“hate/threatening”]. Only flagged labels count — an adapter may return a full score map including non-tripping categories, and those shouldn’t show up as “the categories this tripped”. De-duplicated, order-preserving.
80 81 82 |
# File 'lib/moderate/result.rb', line 80 def categories flagged_labels.map { |label| label.slug.to_sym }.uniq end |
#flagged? ⇒ Boolean
The inverse of ‘allowed?`. The validator and the `moderates` concern read this; it’s spelled out (rather than ‘!allowed`) because “flagged?” is the word everyone reaches for.
74 |
# File 'lib/moderate/result.rb', line 74 def flagged? = !allowed |
#scores ⇒ Object
slug => score, e.g. { “hate” => 0.97, “hate/threatening” => 0.81 }. String keys to match OpenAI’s wire format and what we persist on the Flag. Skips labels with a nil score (a deterministic adapter may not provide one).
87 88 89 90 91 |
# File 'lib/moderate/result.rb', line 87 def scores flagged_labels.each_with_object({}) do |label, acc| acc[label.slug] = label.score unless label.score.nil? end end |