Class: Moderate::Services::IntakeReport

Inherits:
Object
  • Object
show all
Defined in:
lib/moderate/services/intake_report.rb

Overview

IntakeReport — the single persistence path for an in-app community report (a logged-in actor tapping “Report” on a piece of content or another user).

WHY a service object and not just ‘Report.create`:

- Intake is more than a save. After the row commits we must (a) acknowledge
  receipt, (b) emit the `report_received` event (the reporter's receipt +
  the admin ping), and (c) write the immutable audit record. Keeping all of
  that in one object means the model stays a plain validating record and the
  side effects live in exactly one place — easy to test, impossible to
  accidentally skip from a second call site.
- The same shape is reused by the public DSA notice path (see IntakeNotice),
  which delegates here once it has assembled a notice-kind Report. One intake,
  two front doors.

This object is HOST-AGNOSTIC: it never references a concrete content type. The ‘has_reportable_content` is any `Moderate::Reportable` record (polymorphic), the actor is whatever `Moderate.user_class` resolves to, and notification/audit go through the configured hooks — never a hard-wired mailer.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(report:, reporter: nil, reportable: nil, reported_field: nil, actor: :reporter) ⇒ IntakeReport

Returns a new instance of IntakeReport.

Parameters:

  • report (Moderate::Report)

    an unsaved Report the caller has already populated (category, message, etc.). Letting the caller build the record keeps this service free of the (host-specific) strong-params shape — the controller/macro decides which attributes are permitted; we own the save + side effects.

  • reporter (user_class, nil) (defaults to: nil)

    who filed it. nil for an anonymous public notice (the DSA path), present for an in-app report.

  • reportable (Moderate::Reportable, nil) (defaults to: nil)

    the reported content/record.

  • reported_field (String, Symbol, nil) (defaults to: nil)

    which field was reported.

  • actor (user_class, nil) (defaults to: :reporter)

    who triggered the intake for the audit/event envelope (defaults to the reporter — they’re the same person for an in-app report; a public notice may have no actor).



36
37
38
39
40
41
42
43
44
45
46
# File 'lib/moderate/services/intake_report.rb', line 36

def initialize(report:, reporter: nil, reportable: nil, reported_field: nil, actor: :reporter)
  @report = report
  @report.assign_attributes(
    reporter: reporter,
    reportable: reportable,
    reported_field: reported_field&.to_s
  )
  # ":reporter" sentinel means "use the reporter as the actor" — the common
  # case — while still letting a caller pass `actor: nil` explicitly.
  @actor = actor == :reporter ? reporter : actor
end

Instance Attribute Details

#reportObject (readonly)

Returns the value of attribute report.



48
49
50
# File 'lib/moderate/services/intake_report.rb', line 48

def report
  @report
end

Instance Method Details

#saveObject

Persist + run side effects. Returns true on success, false if validation failed (the report carries its errors, Rails-conventionally) so a controller can ‘if intake.save … else render :new` exactly like a bare model save.



53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/moderate/services/intake_report.rb', line 53

def save
  return false unless report.save

  # DSA Art. 16(4): the provider must confirm receipt "without undue delay."
  # `acknowledge!` stamps `acknowledged_at` — the durable, on-record proof of
  # receipt — BEFORE we attempt the (best-effort, possibly-undelivered) email,
  # so the legal obligation is met by the database fact, not by a mailer that
  # might not be wired. See: https://eur-lex.europa.eu/eli/reg/2022/2065/oj
  report.acknowledge!

  deliver_receipt
  audit_intake
  true
end