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).



81
82
83
# File 'lib/concerns_on_rails/models/soft_deletable.rb', line 81

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.)



92
93
94
# File 'lib/concerns_on_rails/models/soft_deletable.rb', line 92

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.



99
100
101
102
103
104
105
106
107
108
109
# File 'lib/concerns_on_rails/models/soft_deletable.rb', line 99

def restore_all
  deleted = all.public_send(soft_delete_scope_names.fetch(:soft_deleted))
  return deleted.update_all(soft_delete_field => nil) if soft_delete_batch_fast_path?(:restore)

  ConcernsOnRails::Support::BatchOps.run(
    deleted,
    label: "ConcernsOnRails::Models::SoftDeletable",
    message: "failed to restore record",
    &:restore!
  )
end

#soft_deletable_by(field = nil, touch: true, default_scope: true, prefix: nil, suffix: nil) ⇒ 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


48
49
50
51
52
53
54
55
56
57
58
# File 'lib/concerns_on_rails/models/soft_deletable.rb', line 48

def soft_deletable_by(field = nil, touch: true, default_scope: true, prefix: nil, suffix: nil)
  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)
  return unless prefix || suffix

  define_soft_delete_scopes(prefix, suffix)
  ConcernsOnRails::Support::Affix.retire!(self, soft_delete_captured_scopes,
                                          label: "ConcernsOnRails::Models::SoftDeletable")
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.



66
67
68
69
70
71
72
73
74
75
76
# File 'lib/concerns_on_rails/models/soft_deletable.rb', line 66

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

  ConcernsOnRails::Support::BatchOps.run(
    pending,
    label: "ConcernsOnRails::Models::SoftDeletable",
    message: "failed to soft-delete record",
    &:soft_delete!
  )
end