module SoftDeletion::Core

Public Class Methods

included(base) click to toggle source
# File lib/soft_deletion/core.rb, line 3
def self.included(base)
  unless base.ancestors.include?(ActiveRecord::Base)
    raise "You can only include this if #{base} extends ActiveRecord::Base"
  end
  base.extend(ClassMethods)

  base.define_model_callbacks :soft_delete
  base.define_model_callbacks :soft_undelete
  base.cattr_accessor :soft_delete_default_scope
end

Public Instance Methods

deleted?() click to toggle source
# File lib/soft_deletion/core.rb, line 55
def deleted?
  deleted_at.present?
end
mark_as_deleted() click to toggle source
# File lib/soft_deletion/core.rb, line 59
def mark_as_deleted
  self.deleted_at ||= Time.now
end
mark_as_undeleted() click to toggle source
# File lib/soft_deletion/core.rb, line 63
def mark_as_undeleted
  self.deleted_at = nil
end
soft_delete(*args) click to toggle source
# File lib/soft_deletion/core.rb, line 71
def soft_delete(*args)
  _run_soft_delete(:soft_delete, args) { save(*args) }
end
soft_delete!(*args) click to toggle source
# File lib/soft_deletion/core.rb, line 67
def soft_delete!(*args)
  _run_soft_delete(:soft_delete!, args) { save!(*args) } || soft_delete_hook_failed(:before_soft_delete)
end
soft_delete_dependencies() click to toggle source
# File lib/soft_deletion/core.rb, line 83
def soft_delete_dependencies
  self.class.soft_delete_dependents.map { |dependent| SoftDeletion::Dependency.new(self, dependent) }
end
soft_undelete() click to toggle source
# File lib/soft_deletion/core.rb, line 75
def soft_undelete
  _run_soft_undelete{ save }
end
soft_undelete!() click to toggle source
# File lib/soft_deletion/core.rb, line 79
def soft_undelete!
  _run_soft_undelete{ save! } || soft_delete_hook_failed(:before_soft_undelete)
end

Protected Instance Methods

_run_soft_delete(method, args, &block) click to toggle source
# File lib/soft_deletion/core.rb, line 99
def _run_soft_delete(method, args, &block)
  result = false
  self.class.transaction do
    internal = lambda do
      mark_as_deleted
      raise ActiveRecord::Rollback unless soft_delete_dependencies.all? { |dep| dep.execute_soft_delete(method, args) }
      result = block.call
      raise ActiveRecord::Rollback unless result
      update_soft_delete_counter_caches(-1)
    end

    run_callbacks :soft_delete, &internal

    result
  end
  result
end
_run_soft_undelete(&block) click to toggle source
# File lib/soft_deletion/core.rb, line 117
def _run_soft_undelete(&block)
  raise "#{self.class} is not deleted" unless deleted_at

  result = false
  limit = deleted_at - 1.hour
  internal = lambda do
    mark_as_undeleted
    soft_delete_dependencies.each { |m| m.soft_undelete!(limit) }
    result = block.call
    update_soft_delete_counter_caches(1)
  end

  self.class.transaction do
    run_callbacks :soft_undelete, &internal
  end

  result
end
soft_delete_hook_failed(hook) click to toggle source
# File lib/soft_deletion/core.rb, line 136
def soft_delete_hook_failed(hook)
  error = (errors.full_messages.presence || ["None"]).join(", ")
  message = "#{hook} hook failed, errors: #{error}"
  # not passing record (self) as 2nd argument to be rails 3/4.1 compatible
  raise ActiveRecord::RecordNotSaved.new(message)
end
update_soft_delete_counter_caches(value) click to toggle source
# File lib/soft_deletion/core.rb, line 89
def update_soft_delete_counter_caches(value)
  each_counter_cached_associations do |association|
    association.load_target unless association.loaded?
    if association.target
      target = association.target
      target.class.update_counters(target.id, association.reflection.counter_cache_column => value)
    end
  end
end