Class: Clickwrap::DispositionPlan
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- Clickwrap::DispositionPlan
- Defined in:
- lib/clickwrap/models/disposition_plan.rb
Overview
A reviewable plan to delete something.
Deletion here is always two steps: plan, then apply. The plan is immutable, scoped, and expiring, and it is rechecked at apply time. A newly placed legal hold, a changed policy, a changed eligibility, or a stale plan stops the run rather than deleting a broader set than the person who reviewed it agreed to.
The plan itself never decides whether an erasure request overrides a retention duty, a legal claim, or a hold. It shows what would happen.
Constant Summary collapse
- KINDS =
%w[retention actor_privacy].freeze
- STATES =
%w[open applying applied applied_with_errors superseded].freeze
- DEFAULT_LIFETIME =
24.hours
Instance Method Summary collapse
- #applied? ⇒ Boolean
- #claim_for_application!(by_reference:, recover_if_stale_after: nil, because_recovery_is_needed: nil) ⇒ Object
- #digest_verified? ⇒ Boolean
-
#ensure_usable!(at = Clickwrap.now) ⇒ Object
Raises with the specific reason this plan can no longer be applied, so an operator sees "the plan expired" or "this was already applied" rather than a generic refusal.
- #expired?(at = Clickwrap.now) ⇒ Boolean
- #finish_application!(outcome_summary:, had_errors: false) ⇒ Object
- #supersede!(because:, by:) ⇒ Object
- #to_s ⇒ Object
- #usable?(at = Clickwrap.now) ⇒ Boolean
Instance Method Details
#applied? ⇒ Boolean
37 |
# File 'lib/clickwrap/models/disposition_plan.rb', line 37 def applied? = state == "applied" |
#claim_for_application!(by_reference:, recover_if_stale_after: nil, because_recovery_is_needed: nil) ⇒ Object
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/clickwrap/models/disposition_plan.rb', line 60 def claim_for_application!(by_reference:, recover_if_stale_after: nil, because_recovery_is_needed: nil) if by_reference.to_s.strip.empty? raise DispositionPlanInvalid, "Applying a disposition plan needs the stable reference of the operator doing it." end with_lock do if state == "applying" reclaim_stale_application!( by_reference: by_reference, stale_after: recover_if_stale_after, because: because_recovery_is_needed ) else ensure_usable! start_application_attempt!(by_reference) end end self end |
#digest_verified? ⇒ Boolean
112 113 114 |
# File 'lib/clickwrap/models/disposition_plan.rb', line 112 def digest_verified? Digest.secure_compare?(plan_digest.to_s, compute_plan_digest) end |
#ensure_usable!(at = Clickwrap.now) ⇒ Object
Raises with the specific reason this plan can no longer be applied, so an operator sees "the plan expired" or "this was already applied" rather than a generic refusal.
46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/clickwrap/models/disposition_plan.rb', line 46 def ensure_usable!(at = Clickwrap.now) raise DispositionPlanInvalid, "Disposition plan #{id} failed its immutable plan digest." unless digest_verified? return true if usable?(at) raise DispositionPlanInvalid, case state when "applied", "applied_with_errors" then "Disposition plan #{id} was already applied at #{applied_at}." when "applying" then "Disposition plan #{id} is already being applied." when "superseded" then "Disposition plan #{id} was superseded by a newer plan." else "Disposition plan #{id} expired at #{expires_at}. Run the plan again and " \ "review the current set before applying it." end end |
#expired?(at = Clickwrap.now) ⇒ Boolean
36 |
# File 'lib/clickwrap/models/disposition_plan.rb', line 36 def expired?(at = Clickwrap.now) = expires_at <= at |
#finish_application!(outcome_summary:, had_errors: false) ⇒ Object
82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/clickwrap/models/disposition_plan.rb', line 82 def finish_application!(outcome_summary:, had_errors: false) with_lock do unless state == "applying" raise DispositionPlanInvalid, "Disposition plan #{id} is not currently being applied." end update_columns( state: had_errors ? "applied_with_errors" : "applied", applied_at: Clickwrap.now, application_outcome: outcome_summary ) end self end |
#supersede!(because:, by:) ⇒ Object
97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/clickwrap/models/disposition_plan.rb', line 97 def supersede!(because:, by:) raise DispositionPlanInvalid, "Superseding a plan needs a `because:`." if because.to_s.strip.empty? with_lock do ensure_usable! update_columns( state: "superseded", superseded_at: Clickwrap.now, superseded_by_reference: Reference.actor(by), superseded_reason: because ) end self end |
#to_s ⇒ Object
116 |
# File 'lib/clickwrap/models/disposition_plan.rb', line 116 def to_s = "#{kind} disposition plan #{id} (#{item_count} items)" |