module Paranoia

Constants

VERSION

Public Class Methods

default_sentinel_value() click to toggle source
# File lib/paranoia.rb, line 11
def self.default_sentinel_value
  @@default_sentinel_value
end
default_sentinel_value=(val) click to toggle source

Change ::default_sentinel_value in a rails initializer

# File lib/paranoia.rb, line 7
def self.default_sentinel_value=(val)
  @@default_sentinel_value = val
end
included(klazz) click to toggle source
# File lib/paranoia.rb, line 15
def self.included(klazz)
  klazz.extend Query
  klazz.extend Callbacks
end

Public Instance Methods

delete() click to toggle source
# File lib/paranoia.rb, line 94
def delete
  raise ActiveRecord::ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
  if persisted?
    # if a transaction exists, add the record so that after_commit
    # callbacks can be run
    add_to_transaction
    update_columns(paranoia_destroy_attributes)
  elsif !frozen?
    assign_attributes(paranoia_destroy_attributes)
  end
  self
end
deleted?()
Alias for: paranoia_destroyed?
destroy() click to toggle source
# File lib/paranoia.rb, line 76
def destroy
  transaction do
    run_callbacks(:destroy) do
      @_disable_counter_cache = deleted?
      result = delete
      next result unless result && ActiveRecord::VERSION::STRING >= '4.2'
      each_counter_cached_associations do |association|
        foreign_key = association.reflection.foreign_key.to_sym
        next if destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
        next unless send(association.reflection.name)
        association.decrement_counters
      end
      @_disable_counter_cache = false
      result
    end
  end
end
get_recovery_window_range(opts) click to toggle source
# File lib/paranoia.rb, line 133
def get_recovery_window_range(opts)
  return opts[:recovery_window_range] if opts[:recovery_window_range]
  return unless opts[:recovery_window]
  (deleted_at - opts[:recovery_window]..deleted_at + opts[:recovery_window])
end
paranoia_destroyed?() click to toggle source
# File lib/paranoia.rb, line 144
def paranoia_destroyed?
  send(paranoia_column) != paranoia_sentinel_value
end
Also aliased as: deleted?
really_destroy!() click to toggle source
# File lib/paranoia.rb, line 149
def really_destroy!
  transaction do
    run_callbacks(:real_destroy) do
      @_disable_counter_cache = deleted?
      dependent_reflections = self.class.reflections.select do |name, reflection|
        reflection.options[:dependent] == :destroy
      end
      if dependent_reflections.any?
        dependent_reflections.each do |name, reflection|
          association_data = self.send(name)
          # has_one association can return nil
          # .paranoid? will work for both instances and classes
          next unless association_data && association_data.paranoid?
          if reflection.collection?
            next association_data.with_deleted.each(&:really_destroy!)
          end
          association_data.really_destroy!
        end
      end
      write_attribute(paranoia_column, current_time_from_proper_timezone)
      destroy_without_paranoia
    end
  end
end
restore(opts = {})
Alias for: restore!
restore!(opts = {}) click to toggle source
# File lib/paranoia.rb, line 107
def restore!(opts = {})
  self.class.transaction do
    run_callbacks(:restore) do
      recovery_window_range = get_recovery_window_range(opts)
      # Fixes a bug where the build would error because attributes were frozen.
      # This only happened on Rails versions earlier than 4.1.
      noop_if_frozen = ActiveRecord.version < Gem::Version.new("4.1")
      if within_recovery_window?(recovery_window_range) && ((noop_if_frozen && !@attributes.frozen?) || !noop_if_frozen)
        @_disable_counter_cache = !deleted?
        write_attribute paranoia_column, paranoia_sentinel_value
        update_columns(paranoia_restore_attributes)
        each_counter_cached_associations do |association|
          if send(association.reflection.name)
            association.increment_counters
          end
        end
        @_disable_counter_cache = false
      end
      restore_associated_records(recovery_window_range) if opts[:recursive]
    end
  end

  self
end
Also aliased as: restore
within_recovery_window?(recovery_window_range) click to toggle source
# File lib/paranoia.rb, line 139
def within_recovery_window?(recovery_window_range)
  return true unless recovery_window_range
  recovery_window_range.cover?(deleted_at)
end

Private Instance Methods

each_counter_cached_associations() click to toggle source
Calls superclass method
# File lib/paranoia.rb, line 176
def each_counter_cached_associations
  !@_disable_counter_cache && defined?(super) ? super : []
end
paranoia_destroy_attributes() click to toggle source
# File lib/paranoia.rb, line 186
def paranoia_destroy_attributes
  {
    paranoia_column => current_time_from_proper_timezone
  }.merge(timestamp_attributes_with_current_time)
end
paranoia_restore_attributes() click to toggle source
# File lib/paranoia.rb, line 180
def paranoia_restore_attributes
  {
    paranoia_column => paranoia_sentinel_value
  }.merge(timestamp_attributes_with_current_time)
end
restore_associated_records(recovery_window_range = nil) click to toggle source

restore associated records that have been soft deleted when we called destroy

# File lib/paranoia.rb, line 198
def restore_associated_records(recovery_window_range = nil)
  destroyed_associations = self.class.reflect_on_all_associations.select do |association|
    association.options[:dependent] == :destroy
  end

  destroyed_associations.each do |association|
    association_data = send(association.name)

    unless association_data.nil?
      if association_data.paranoid?
        if association.collection?
          association_data.only_deleted.each do |record|
            record.restore(:recursive => true, :recovery_window_range => recovery_window_range)
          end
        else
          association_data.restore(:recursive => true, :recovery_window_range => recovery_window_range)
        end
      end
    end

    if association_data.nil? && association.macro.to_s == "has_one"
      association_class_name = association.klass.name
      association_foreign_key = association.foreign_key

      if association.type
        association_polymorphic_type = association.type
        association_find_conditions = { association_polymorphic_type => self.class.name.to_s, association_foreign_key => self.id }
      else
        association_find_conditions = { association_foreign_key => self.id }
      end

      association_class = association_class_name.constantize
      if association_class.paranoid?
        association_class.only_deleted.where(association_find_conditions).first
          .try!(:restore, recursive: true, :recovery_window_range => recovery_window_range)
      end
    end
  end

  clear_association_cache if destroyed_associations.present?
end
timestamp_attributes_with_current_time() click to toggle source
# File lib/paranoia.rb, line 192
def timestamp_attributes_with_current_time
  timestamp_attributes_for_update_in_model.each_with_object({}) { |attr,hash| hash[attr] = current_time_from_proper_timezone }
end