Module: Paranoia

Defined in:
lib/paranoia.rb,
lib/paranoia/version.rb

Defined Under Namespace

Modules: Query

Constant Summary collapse

VERSION =
'3.0.0'.freeze
@@default_sentinel_value =
nil

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.default_sentinel_valueObject



16
17
18
# File 'lib/paranoia.rb', line 16

def self.default_sentinel_value
  @@default_sentinel_value
end

.default_sentinel_value=(val) ⇒ Object

Change default_sentinel_value in a rails initializer



12
13
14
# File 'lib/paranoia.rb', line 12

def self.default_sentinel_value=(val)
  @@default_sentinel_value = val
end

.included(klazz) ⇒ Object



20
21
22
# File 'lib/paranoia.rb', line 20

def self.included(klazz)
  klazz.extend Query
end

Instance Method Details

#get_recovery_window_range(opts) ⇒ Object



134
135
136
137
138
# File 'lib/paranoia.rb', line 134

def get_recovery_window_range(opts)
  return opts[:recovery_window_range] if opts[:recovery_window_range]
  return unless opts[:recovery_window]
  (deletion_time - opts[:recovery_window]..deletion_time + opts[:recovery_window])
end

#paranoia_deleteObject Also known as: delete

Raises:

  • (ActiveRecord::ReadOnlyRecord)


94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/paranoia.rb', line 94

def paranoia_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

#paranoia_destroyObject Also known as: destroy



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

def paranoia_destroy
  with_transaction_returning_status do
    result = run_callbacks(:destroy) do
      @_disable_counter_cache = paranoia_destroyed?
      result = paranoia_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
      @_trigger_destroy_callback = true
      @_disable_counter_cache = false
      result
    end
    raise ActiveRecord::Rollback, "Not destroyed" unless paranoia_destroyed?
    result
  end || false
end

#paranoia_destroy!Object



85
86
87
88
# File 'lib/paranoia.rb', line 85

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

#paranoia_destroyed?Boolean Also known as: deleted?

Returns:

  • (Boolean)


145
146
147
# File 'lib/paranoia.rb', line 145

def paranoia_destroyed?
  paranoia_column_value != paranoia_sentinel_value
end

#really_destroy!(update_destroy_attributes: true) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/paranoia.rb', line 150

def really_destroy!(update_destroy_attributes: true)
  with_transaction_returning_status do
    run_callbacks(:real_destroy) do
      @_disable_counter_cache = paranoia_destroyed?
      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.find_each { |record|
              record.really_destroy!(update_destroy_attributes: update_destroy_attributes)
            }
          end
          association_data.really_destroy!(update_destroy_attributes: update_destroy_attributes)
        end
      end
      update_columns(paranoia_destroy_attributes) if update_destroy_attributes
      destroy_without_paranoia
    end
  end
end

#restore!(opts = {}) ⇒ Object Also known as: restore



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/paranoia.rb', line 108

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 = !paranoia_destroyed?
        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

#trigger_transactional_callbacks?Boolean

Returns:

  • (Boolean)


90
91
92
# File 'lib/paranoia.rb', line 90

def trigger_transactional_callbacks?
  super || @_trigger_destroy_callback && paranoia_destroyed?
end

#within_recovery_window?(recovery_window_range) ⇒ Boolean

Returns:

  • (Boolean)


140
141
142
143
# File 'lib/paranoia.rb', line 140

def within_recovery_window?(recovery_window_range)
  return true unless recovery_window_range
  recovery_window_range.cover?(deletion_time)
end