module ActsAsParanoid::Core

def self.included(base)

def self.included(base)
  base.extend ClassMethods
end

def decrement_counters_on_associations

def decrement_counters_on_associations
  update_counters_on_associations :decrement_counter
end

def delete

Straight from ActiveRecord 5.1!
def delete
  self.class.delete(id) if persisted?
  stale_paranoid_value
  freeze
end

def deleted?

def deleted?
  return true if @destroyed
  if self.class.string_type_with_deleted_value?
    paranoid_value == paranoid_configuration[:deleted_value]
  elsif self.class.boolean_type_not_nullable?
    paranoid_value == true
  else
    !paranoid_value.nil?
  end
end

def deleted_fully?

def deleted_fully?
  @destroyed
end

def destroy

def destroy
  if !deleted?
    with_transaction_returning_status do
      run_callbacks :destroy do
        if persisted?
          # Handle composite keys, otherwise we would just use
          # `self.class.primary_key.to_sym => self.id`.
          self.class
            .delete_all([Array(self.class.primary_key), Array(id)].transpose.to_h)
          decrement_counters_on_associations
        end
        @_trigger_destroy_callback = true
        stale_paranoid_value
        self
      end
    end
  elsif paranoid_configuration[:double_tap_destroys_fully]
    destroy_fully!
  end
end

def destroy!

def destroy!
  destroy || raise(
    ActiveRecord::RecordNotDestroyed.new("Failed to destroy the record", self)
  )
end

def destroy_dependent_associations!

def destroy_dependent_associations!
  self.class.dependent_associations.each do |reflection|
    assoc = association(reflection.name)
    next unless (klass = assoc.klass).paranoid?
    klass
      .only_deleted.merge(get_association_scope(assoc))
      .each(&:destroy!)
  end
end

def destroy_fully!

def destroy_fully!
  with_transaction_returning_status do
    run_callbacks :destroy do
      destroy_dependent_associations!
      if persisted?
        # Handle composite keys, otherwise we would just use
        # `self.class.primary_key.to_sym => self.id`.
        self.class
          .delete_all!([Array(self.class.primary_key), Array(id)].transpose.to_h)
        decrement_counters_on_associations
      end
      @destroyed = true
      freeze
    end
  end
end

def each_counter_cached_association_reflection

def each_counter_cached_association_reflection
  _reflections.each do |_name, reflection|
    yield reflection if reflection.belongs_to? && reflection.counter_cache_column
  end
end

def get_association_scope(dependent_association)

def get_association_scope(dependent_association)
  ActiveRecord::Associations::AssociationScope.scope(dependent_association)
end

def increment_counters_on_associations

def increment_counters_on_associations
  update_counters_on_associations :increment_counter
end

def paranoid_value

def paranoid_value
  send(self.class.paranoid_column)
end

def paranoid_value=(value)

def paranoid_value=(value)
  write_attribute(self.class.paranoid_column, value)
end

def persisted?

def persisted?
  !(new_record? || @destroyed)
end

def recover(options = {})

def recover(options = {})
  return if !deleted?
  options = {
    recursive: self.class.paranoid_configuration[:recover_dependent_associations],
    recovery_window: self.class.paranoid_configuration[:dependent_recovery_window],
    raise_error: false
  }.merge(options)
  self.class.transaction do
    run_callbacks :recover do
      increment_counters_on_associations
      deleted_value = paranoid_value
      self.paranoid_value = self.class.recovery_value
      result = if options[:raise_error]
                 save!
               else
                 save
               end
      recover_dependent_associations(deleted_value, options) if options[:recursive]
      result
    end
  end
end

def recover!(options = {})

def recover!(options = {})
  options[:raise_error] = true
  recover(options)
end

def recover_dependent_association(reflection, deleted_value, options)

def recover_dependent_association(reflection, deleted_value, options)
  assoc = association(reflection.name)
  return unless (klass = assoc.klass).paranoid?
  if reflection.belongs_to? && attributes[reflection.association_foreign_key].nil?
    return
  end
  scope = klass.only_deleted.merge(get_association_scope(assoc))
  # We can only recover by window if both parent and dependant have a
  # paranoid column type of :time.
  if self.class.paranoid_column_type == :time && klass.paranoid_column_type == :time
    scope = scope.deleted_inside_time_window(deleted_value, options[:recovery_window])
  end
  recovered = false
  scope.each do |object|
    object.recover(options)
    recovered = true
  end
  assoc.reload if recovered && reflection.has_one? && assoc.loaded?
end

def recover_dependent_associations(deleted_value, options)

def recover_dependent_associations(deleted_value, options)
  self.class.dependent_associations.each do |reflection|
    recover_dependent_association(reflection, deleted_value, options)
  end
end

def stale_paranoid_value

def stale_paranoid_value
  self.paranoid_value = self.class.delete_now_value
  clear_attribute_changes([self.class.paranoid_column])
end

def update_counters_on_associations(method_sym)

def update_counters_on_associations(method_sym)
  each_counter_cached_association_reflection do |assoc_reflection|
    reflection_options = assoc_reflection.options
    next unless reflection_options[:counter_cache]
    associated_object = send(assoc_reflection.name)
    next unless associated_object
    counter_cache_column = assoc_reflection.counter_cache_column
    associated_object.class.send(method_sym, counter_cache_column,
                                 associated_object.id)
    associated_object.touch if reflection_options[:touch]
  end
end