Module: Moderate::Actor
- Extended by:
- ActiveSupport::Concern
- Includes:
- Reportable
- Defined in:
- lib/moderate/models/concerns/actor.rb
Overview
The “person who acts” in the Trust & Safety system: the model that reports other content, blocks other actors, gets reported, gets banned. Backs the ‘has_reporting_and_blocking` macro (and its documented equivalent, `include Moderate::Actor`).
This is the one model the gem treats as the actor/identity, configured via ‘config.user_class`. There’s normally exactly one such model per app (“User”, “Account”, “Member”), and it is BOTH an actor and itself reportable — Apple 1.2 and Google Play UGC both require reporting and blocking users, not just content (docs/compliance.md). So including Actor also includes Moderate::Reportable, giving a user the reportable contract with sensible actor-flavored defaults (a user IS its own ‘reported_owner`).
Everything host-specific (what “banned” means, whether a block tears down a pending invite) is delegated to the configured hooks via the Moderate facade, never hard-coded here — keeping the actor host-agnostic.
Instance Method Summary collapse
-
#block!(other) ⇒ Object
Block ‘other` (idempotent, audited, fires the `on_block` hook).
-
#blocked_by?(other) ⇒ Boolean
Did they block me? (the other direction).
-
#blocked_with?(other) ⇒ Boolean
Is there a block edge in EITHER direction? This is the predicate to check in product code (“can these two interact?”) — blocking is symmetric for visibility/reachability even though only one side pressed the button.
-
#blocks?(other) ⇒ Boolean
Did I block them? (one direction).
-
#report!(reportable, category:, details: nil, **attributes) ⇒ Object
File a report from this actor against a piece of content (or another actor — a user with ‘has_reporting_and_blocking` is itself reportable).
-
#report_visible_to?(viewer, field:) ⇒ Boolean
You can never report yourself, and a field still has to be reportable.
-
#reported_owner ⇒ Object
A user is responsible for themselves — so a report against a user (e.g. for impersonation) attributes to and notifies that same user.
-
#unblock!(other) ⇒ Object
Lift a block this actor placed on ‘other`.
Methods included from Reportable
#flagged?, #moderation_admin_path, #moderation_flags, #moderation_label, #moderation_return_path, #moderation_snapshot, #moderation_subject_url, #open_reports, #pending_moderation_flags, #removable_reported_field?, #remove_reported_field!, #reportable_field_allowed?, #reported?
Instance Method Details
#block!(other) ⇒ Object
Block ‘other` (idempotent, audited, fires the `on_block` hook). The actual create/audit/notify is owned by Moderate::Block.block! so there’s one block write path for the whole gem.
122 123 124 |
# File 'lib/moderate/models/concerns/actor.rb', line 122 def block!(other) Moderate::Block.block!(blocker: self, blocked: other) end |
#blocked_by?(other) ⇒ Boolean
Did they block me? (the other direction)
140 141 142 143 144 |
# File 'lib/moderate/models/concerns/actor.rb', line 140 def blocked_by?(other) return false if other.blank? moderate_received_blocks.exists?(blocker_id: other.id) end |
#blocked_with?(other) ⇒ Boolean
Is there a block edge in EITHER direction? This is the predicate to check in product code (“can these two interact?”) — blocking is symmetric for visibility/reachability even though only one side pressed the button. A self-check is never “blocked.”
150 151 152 153 154 |
# File 'lib/moderate/models/concerns/actor.rb', line 150 def blocked_with?(other) return false if other.blank? || other.id == id blocks?(other) || blocked_by?(other) end |
#blocks?(other) ⇒ Boolean
Did I block them? (one direction)
133 134 135 136 137 |
# File 'lib/moderate/models/concerns/actor.rb', line 133 def blocks?(other) return false if other.blank? moderate_initiated_blocks.exists?(blocked_id: other.id) end |
#report!(reportable, category:, details: nil, **attributes) ⇒ Object
File a report from this actor against a piece of content (or another actor —a user with ‘has_reporting_and_blocking` is itself reportable).
current_user.report!(@message, category: :harassment, details: "...")
current_user.report!(@other_user, category: :impersonation)
We build and persist a Moderate::Report; the Report model owns the rest of the lifecycle the README promises — snapshotting the offending content so evidence survives edits/deletes, inferring the responsible owner, sending the reporter a receipt, and dropping it into the queue. Keeping that logic IN the model (not here) means the public DSA notice intake and this in-app path share one source of truth.
‘details:` is the README’s name for the reporter’s free-text reason; it maps onto the Report’s ‘message`. Extra keyword args (e.g. `field:`) pass straight through, so this stays forward-compatible with the Report model’s attributes.
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/moderate/models/concerns/actor.rb', line 78 def report!(reportable, category:, details: nil, **attributes) attributes[:message] = details if details && !attributes.key?(:message) reported_field = attributes.delete(:reported_field) field = attributes.delete(:field) reported_field ||= field # An in-app reporter attests to good faith IMPLICITLY by choosing to report — # there's no separate checkbox in the in-app flow (that's the public DSA notice # form's job). The Report model requires `good_faith_confirmed` to be truthy # (Art. 16(2)(d)), so we set it here for the community path unless the caller # already passed it. (A host that wants an explicit in-app attestation can still # override by passing `good_faith_confirmed:` in `attributes`.) attributes[:good_faith_confirmed] = true unless attributes.key?(:good_faith_confirmed) report = Moderate::Report.new( reporter: self, reportable: reportable, category: category.to_s, intake_kind: "community", **attributes ) intake = Moderate::Services::IntakeReport.new( report: report, reporter: self, reportable: reportable, reported_field: reported_field ) return report if intake.save raise ActiveRecord::RecordInvalid, report end |
#report_visible_to?(viewer, field:) ⇒ Boolean
You can never report yourself, and a field still has to be reportable. This tightens Reportable’s default with the self-report guard, so the ‘moderate_report_link` helper hides the control on your own profile.
170 171 172 |
# File 'lib/moderate/models/concerns/actor.rb', line 170 def report_visible_to?(viewer, field:) viewer.present? && viewer.id != id && reportable_field_allowed?(field) end |
#reported_owner ⇒ Object
A user is responsible for themselves — so a report against a user (e.g. for impersonation) attributes to and notifies that same user.
163 164 165 |
# File 'lib/moderate/models/concerns/actor.rb', line 163 def reported_owner self end |