Module: ConcernsOnRails::Models::SoftDeletable::ClassMethods

Includes:
Support::ColumnGuard
Defined in:
lib/concerns_on_rails/models/soft_deletable.rb

Overview

A real module (not class_methods do) so the batch helpers and their private fast-path predicate aren't constrained by Metrics/BlockLength (the Stateable/Auditable precedent). ActiveSupport::Concern auto-extends it.

Instance Method Summary collapse

Methods included from Support::ColumnGuard

#column_migration_hint, #ensure_columns!, #ensure_columns_on!, #schema_reachable?

Instance Method Details

#destroy_allObject

Override destroy_all to soft delete. Kept for backwards compatibility, but prefer the explicit soft_delete_all — silently redefining a standard AR method is a known footgun (and unlike AR's destroy_all this returns a count, not the records).



88
89
90
# File 'lib/concerns_on_rails/models/soft_deletable.rb', line 88

def destroy_all
  soft_delete_all
end

#really_destroy_allObject

Hard-delete every record matching the CURRENT relation — including soft-deleted rows (only the soft-delete column's predicates are peeled off). Note that unscope also drops a caller's own condition on that column, so only_deleted.really_destroy_all widens to the whole relation — use soft_deleted.delete_all to purge trash only. (Before 1.22 this ignored the relation entirely and hard-deleted the complete table.)



99
100
101
# File 'lib/concerns_on_rails/models/soft_deletable.rb', line 99

def really_destroy_all
  all.unscope(where: soft_delete_field).delete_all
end

#restore_allObject

Restore every soft-deleted record (mirror of soft_delete_all): Integer count, RecordNotSaved + rollback on failure, single UPDATE when the fast path applies.



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/concerns_on_rails/models/soft_deletable.rb', line 106

def restore_all
  return soft_deleted.update_all(soft_delete_field => nil) if soft_delete_batch_fast_path?(:restore)

  transaction do
    count = 0
    soft_deleted.find_each do |record|
      record.restore! ||
        raise(ActiveRecord::RecordNotSaved.new(
                "ConcernsOnRails::Models::SoftDeletable: failed to restore record", record
              ))
      count += 1
    end
    count
  end
end

#soft_deletable_by(field = nil, touch: true, default_scope: true) ⇒ Object

Define soft delete field and options. Example:

soft_deletable_by :deleted_at, touch: false
soft_deletable_by :deleted_at, default_scope: false  # don't hide deleted rows from .all


50
51
52
53
54
55
# File 'lib/concerns_on_rails/models/soft_deletable.rb', line 50

def soft_deletable_by(field = nil, touch: true, default_scope: true)
  self.soft_delete_field = field || :deleted_at
  self.soft_delete_touch = touch
  self.soft_delete_default_scope = default_scope
  ensure_columns!("ConcernsOnRails::Models::SoftDeletable", soft_delete_field, types: :datetime)
end

#soft_delete_allObject

Soft-delete every matching record. Returns the Integer count of records transitioned (already-deleted rows are skipped and keep their original timestamp). A record that fails raises ActiveRecord::RecordNotSaved and rolls the whole batch back — before 1.22 the rollback happened silently and the method returned nil. With touch: false and no overridden hooks this is a single UPDATE.



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/concerns_on_rails/models/soft_deletable.rb', line 63

def soft_delete_all
  if soft_delete_batch_fast_path?(:soft_delete)
    return all.where(soft_delete_field => nil).update_all(soft_delete_field => Time.zone.now)
  end

  # find_each streams in PK batches instead of materializing the whole
  # relation; filtering deleted rows DB-side also skips loading them at
  # all. Updated rows leave the filtered set, but pagination is strictly
  # forward by id, so nothing is skipped or revisited.
  transaction do
    count = 0
    all.where(soft_delete_field => nil).find_each do |record|
      record.soft_delete! ||
        raise(ActiveRecord::RecordNotSaved.new(
                "ConcernsOnRails::Models::SoftDeletable: failed to soft-delete record", record
              ))
      count += 1
    end
    count
  end
end