Commit 103c33b0 by Ben A. Morgan Committed by GitHub

Merge pull request #383 from netguru-hackathon/core

Add recovery window feature
parents ba48f196 4551c3b2
...@@ -181,6 +181,14 @@ Client.restore(id, :recursive => true) ...@@ -181,6 +181,14 @@ Client.restore(id, :recursive => true)
client.restore(:recursive => true) client.restore(:recursive => true)
``` ```
If you want to restore a record and only those dependently destroyed associated records that were deleted within 2 minutes of the object upon which they depend:
``` ruby
Client.restore(id, :recursive => true. :recovery_window => 2.minutes)
# or
client.restore(:recursive => true, :recovery_window => 2.minutes)
```
For more information, please look at the tests. For more information, please look at the tests.
#### About indexes: #### About indexes:
......
...@@ -105,14 +105,15 @@ module Paranoia ...@@ -105,14 +105,15 @@ module Paranoia
def restore!(opts = {}) def restore!(opts = {})
self.class.transaction do self.class.transaction do
run_callbacks(:restore) 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. # Fixes a bug where the build would error because attributes were frozen.
# This only happened on Rails versions earlier than 4.1. # This only happened on Rails versions earlier than 4.1.
noop_if_frozen = ActiveRecord.version < Gem::Version.new("4.1") noop_if_frozen = ActiveRecord.version < Gem::Version.new("4.1")
if (noop_if_frozen && !@attributes.frozen?) || !noop_if_frozen if within_recovery_window?(recovery_window_range) && ((noop_if_frozen && !@attributes.frozen?) || !noop_if_frozen)
write_attribute paranoia_column, paranoia_sentinel_value write_attribute paranoia_column, paranoia_sentinel_value
update_columns(paranoia_restore_attributes) update_columns(paranoia_restore_attributes)
end end
restore_associated_records if opts[:recursive] restore_associated_records(recovery_window_range) if opts[:recursive]
end end
end end
...@@ -120,6 +121,17 @@ module Paranoia ...@@ -120,6 +121,17 @@ module Paranoia
end end
alias :restore :restore! alias :restore :restore!
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
def within_recovery_window?(recovery_window_range)
return true unless recovery_window_range
recovery_window_range.cover?(deleted_at)
end
def paranoia_destroyed? def paranoia_destroyed?
send(paranoia_column) != paranoia_sentinel_value send(paranoia_column) != paranoia_sentinel_value
end end
...@@ -169,7 +181,7 @@ module Paranoia ...@@ -169,7 +181,7 @@ module Paranoia
# restore associated records that have been soft deleted when # restore associated records that have been soft deleted when
# we called #destroy # we called #destroy
def restore_associated_records def restore_associated_records(recovery_window_range = nil)
destroyed_associations = self.class.reflect_on_all_associations.select do |association| destroyed_associations = self.class.reflect_on_all_associations.select do |association|
association.options[:dependent] == :destroy association.options[:dependent] == :destroy
end end
...@@ -180,9 +192,11 @@ module Paranoia ...@@ -180,9 +192,11 @@ module Paranoia
unless association_data.nil? unless association_data.nil?
if association_data.paranoid? if association_data.paranoid?
if association.collection? if association.collection?
association_data.only_deleted.each { |record| record.restore(:recursive => true) } association_data.only_deleted.each do |record|
record.restore(:recursive => true, :recovery_window_range => recovery_window_range)
end
else else
association_data.restore(:recursive => true) association_data.restore(:recursive => true, :recovery_window_range => recovery_window_range)
end end
end end
end end
...@@ -200,7 +214,8 @@ module Paranoia ...@@ -200,7 +214,8 @@ module Paranoia
association_class = association_class_name.constantize association_class = association_class_name.constantize
if association_class.paranoid? if association_class.paranoid?
association_class.only_deleted.where(association_find_conditions).first.try!(:restore, recursive: true) association_class.only_deleted.where(association_find_conditions).first
.try!(:restore, recursive: true, :recovery_window_range => recovery_window_range)
end end
end end
end end
......
...@@ -580,6 +580,38 @@ class ParanoiaTest < test_framework ...@@ -580,6 +580,38 @@ class ParanoiaTest < test_framework
refute c.paranoia_destroyed? refute c.paranoia_destroyed?
end end
def test_restore_with_associations_using_recovery_window
parent = ParentModel.create
first_child = parent.very_related_models.create
second_child = parent.very_related_models.create
parent.destroy
second_child.update(deleted_at: parent.deleted_at + 11.minutes)
parent.restore!(:recursive => true)
assert_equal true, parent.deleted_at.nil?
assert_equal true, first_child.reload.deleted_at.nil?
assert_equal true, second_child.reload.deleted_at.nil?
parent.destroy
second_child.update(deleted_at: parent.deleted_at + 11.minutes)
parent.restore(:recursive => true, :recovery_window => 10.minutes)
assert_equal true, parent.deleted_at.nil?
assert_equal true, first_child.reload.deleted_at.nil?
assert_equal false, second_child.reload.deleted_at.nil?
second_child.restore
parent.destroy
first_child.update(deleted_at: parent.deleted_at - 11.minutes)
second_child.update(deleted_at: parent.deleted_at - 9.minutes)
ParentModel.restore(parent.id, :recursive => true, :recovery_window => 10.minutes)
assert_equal true, parent.reload.deleted_at.nil?
assert_equal false, first_child.reload.deleted_at.nil?
assert_equal true, second_child.reload.deleted_at.nil?
end
def test_restore_with_associations def test_restore_with_associations
parent = ParentModel.create parent = ParentModel.create
first_child = parent.very_related_models.create first_child = parent.very_related_models.create
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment