Commit 36adfecb by Ben A. Morgan Committed by GitHub

Merge pull request #389 from edwardmp/AssociationNotSoftDestroyedValidator

Added association not soft destroyed validator
parents b63f446d d366099f
...@@ -2,6 +2,12 @@ ...@@ -2,6 +2,12 @@
## 2.2.2 (Unreleased) ## 2.2.2 (Unreleased)
* [#389](https://github.com/rubysherpas/paranoia/pull/389) Added association not soft destroyed validator
_Fixes [#380](https://github.com/rubysherpas/paranoia/issues/380)_
[Edward Poot (@edwardmp)](https://github.com/edwardmp)
## 2.2.1 (2017-02-15) ## 2.2.1 (2017-02-15)
* [#371](https://github.com/rubysherpas/paranoia/pull/371) Use ActiveSupport.on_load to correctly re-open ActiveRecord::Base * [#371](https://github.com/rubysherpas/paranoia/pull/371) Use ActiveSupport.on_load to correctly re-open ActiveRecord::Base
......
...@@ -189,6 +189,13 @@ Client.restore(id, :recursive => true. :recovery_window => 2.minutes) ...@@ -189,6 +189,13 @@ Client.restore(id, :recursive => true. :recovery_window => 2.minutes)
client.restore(:recursive => true, :recovery_window => 2.minutes) client.restore(:recursive => true, :recovery_window => 2.minutes)
``` ```
Note that by default paranoia will not prevent that a soft destroyed object can't be associated with another object of a different model.
A Rails validator is provided should you require this functionality:
``` ruby
validates :some_assocation, association_not_soft_destroyed: true
```
This validator makes sure that `some_assocation` is not soft destroyed. If the object is soft destroyed the main object is rendered invalid and an validation error is added.
For more information, please look at the tests. For more information, please look at the tests.
#### About indexes: #### About indexes:
......
...@@ -299,5 +299,14 @@ module ActiveRecord ...@@ -299,5 +299,14 @@ module ActiveRecord
class UniquenessValidator < ActiveModel::EachValidator class UniquenessValidator < ActiveModel::EachValidator
prepend UniquenessParanoiaValidator prepend UniquenessParanoiaValidator
end end
class AssociationNotSoftDestroyedValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
# if association is soft destroyed, add an error
if value.present? && value.deleted?
record.errors[attribute] << 'has been soft-deleted'
end
end
end
end end
end end
...@@ -21,6 +21,7 @@ def setup! ...@@ -21,6 +21,7 @@ def setup!
'paranoid_model_with_foreign_key_belongs' => 'parent_model_id INTEGER, deleted_at DATETIME, has_one_foreign_key_id INTEGER', 'paranoid_model_with_foreign_key_belongs' => 'parent_model_id INTEGER, deleted_at DATETIME, has_one_foreign_key_id INTEGER',
'paranoid_model_with_timestamps' => 'parent_model_id INTEGER, created_at DATETIME, updated_at DATETIME, deleted_at DATETIME', 'paranoid_model_with_timestamps' => 'parent_model_id INTEGER, created_at DATETIME, updated_at DATETIME, deleted_at DATETIME',
'not_paranoid_model_with_belongs' => 'parent_model_id INTEGER, paranoid_model_with_has_one_id INTEGER', 'not_paranoid_model_with_belongs' => 'parent_model_id INTEGER, paranoid_model_with_has_one_id INTEGER',
'not_paranoid_model_with_belongs_and_assocation_not_soft_destroyed_validator' => 'parent_model_id INTEGER, paranoid_model_with_has_one_id INTEGER',
'paranoid_model_with_has_one_and_builds' => 'parent_model_id INTEGER, color VARCHAR(32), deleted_at DATETIME, has_one_foreign_key_id INTEGER', 'paranoid_model_with_has_one_and_builds' => 'parent_model_id INTEGER, color VARCHAR(32), deleted_at DATETIME, has_one_foreign_key_id INTEGER',
'featureful_models' => 'deleted_at DATETIME, name VARCHAR(32)', 'featureful_models' => 'deleted_at DATETIME, name VARCHAR(32)',
'plain_models' => 'deleted_at DATETIME', 'plain_models' => 'deleted_at DATETIME',
...@@ -117,9 +118,9 @@ class ParanoiaTest < test_framework ...@@ -117,9 +118,9 @@ class ParanoiaTest < test_framework
model.remove_called_variables # clear called callback flags model.remove_called_variables # clear called callback flags
model.destroy model.destroy
assert_equal nil, model.instance_variable_get(:@update_callback_called) assert_nil model.instance_variable_get(:@update_callback_called)
assert_equal nil, model.instance_variable_get(:@save_callback_called) assert_nil model.instance_variable_get(:@save_callback_called)
assert_equal nil, model.instance_variable_get(:@validate_called) assert_nil model.instance_variable_get(:@validate_called)
assert model.instance_variable_get(:@destroy_callback_called) assert model.instance_variable_get(:@destroy_callback_called)
assert model.instance_variable_get(:@after_destroy_callback_called) assert model.instance_variable_get(:@after_destroy_callback_called)
...@@ -133,12 +134,12 @@ class ParanoiaTest < test_framework ...@@ -133,12 +134,12 @@ class ParanoiaTest < test_framework
model.remove_called_variables # clear called callback flags model.remove_called_variables # clear called callback flags
model.delete model.delete
assert_equal nil, model.instance_variable_get(:@update_callback_called) assert_nil model.instance_variable_get(:@update_callback_called)
assert_equal nil, model.instance_variable_get(:@save_callback_called) assert_nil model.instance_variable_get(:@save_callback_called)
assert_equal nil, model.instance_variable_get(:@validate_called) assert_nil model.instance_variable_get(:@validate_called)
assert_equal nil, model.instance_variable_get(:@destroy_callback_called) assert_nil model.instance_variable_get(:@destroy_callback_called)
assert_equal nil, model.instance_variable_get(:@after_destroy_callback_called) assert_nil model.instance_variable_get(:@after_destroy_callback_called)
assert_equal nil, model.instance_variable_get(:@after_commit_callback_called) assert_nil model.instance_variable_get(:@after_commit_callback_called)
end end
def test_delete_in_transaction_behavior_for_plain_models_callbacks def test_delete_in_transaction_behavior_for_plain_models_callbacks
...@@ -149,11 +150,11 @@ class ParanoiaTest < test_framework ...@@ -149,11 +150,11 @@ class ParanoiaTest < test_framework
model.delete model.delete
end end
assert_equal nil, model.instance_variable_get(:@update_callback_called) assert_nil model.instance_variable_get(:@update_callback_called)
assert_equal nil, model.instance_variable_get(:@save_callback_called) assert_nil model.instance_variable_get(:@save_callback_called)
assert_equal nil, model.instance_variable_get(:@validate_called) assert_nil model.instance_variable_get(:@validate_called)
assert_equal nil, model.instance_variable_get(:@destroy_callback_called) assert_nil model.instance_variable_get(:@destroy_callback_called)
assert_equal nil, model.instance_variable_get(:@after_destroy_callback_called) assert_nil model.instance_variable_get(:@after_destroy_callback_called)
assert model.instance_variable_get(:@after_commit_callback_called) assert model.instance_variable_get(:@after_commit_callback_called)
end end
...@@ -226,7 +227,7 @@ class ParanoiaTest < test_framework ...@@ -226,7 +227,7 @@ class ParanoiaTest < test_framework
end end
def test_default_sentinel_value def test_default_sentinel_value
assert_equal nil, ParanoidModel.paranoia_sentinel_value assert_nil ParanoidModel.paranoia_sentinel_value
end end
def test_without_default_scope_option def test_without_default_scope_option
...@@ -375,7 +376,7 @@ class ParanoiaTest < test_framework ...@@ -375,7 +376,7 @@ class ParanoiaTest < test_framework
model = CallbackModel.new model = CallbackModel.new
model.save model.save
model.delete model.delete
assert_equal nil, model.instance_variable_get(:@destroy_callback_called) assert_nil model.instance_variable_get(:@destroy_callback_called)
end end
def test_destroy_behavior_for_callbacks def test_destroy_behavior_for_callbacks
...@@ -948,8 +949,8 @@ class ParanoiaTest < test_framework ...@@ -948,8 +949,8 @@ class ParanoiaTest < test_framework
parent_model_with_counter_cache_column = ParentModelWithCounterCacheColumn.create parent_model_with_counter_cache_column = ParentModelWithCounterCacheColumn.create
related_model = parent_model_with_counter_cache_column.related_models.create related_model = parent_model_with_counter_cache_column.related_models.create
assert_equal nil, related_model.instance_variable_get(:@after_destroy_callback_called) assert_nil related_model.instance_variable_get(:@after_destroy_callback_called)
assert_equal nil, related_model.instance_variable_get(:@after_commit_on_destroy_callback_called) assert_nil related_model.instance_variable_get(:@after_commit_on_destroy_callback_called)
related_model.destroy related_model.destroy
...@@ -964,6 +965,18 @@ class ParanoiaTest < test_framework ...@@ -964,6 +965,18 @@ class ParanoiaTest < test_framework
related.valid? related.valid?
end end
def test_assocation_not_soft_destroyed_validator
notParanoidModel = NotParanoidModelWithBelongsAndAssocationNotSoftDestroyedValidator.create
parentModel = ParentModel.create
assert notParanoidModel.valid?
notParanoidModel.parent_model = parentModel
assert notParanoidModel.valid?
parentModel.destroy
assert !notParanoidModel.valid?
assert notParanoidModel.errors.full_messages.include? "Parent model has been soft-deleted"
end
# TODO: find a fix for Rails 4.1 # TODO: find a fix for Rails 4.1
if ActiveRecord::VERSION::STRING !~ /\A4\.1/ if ActiveRecord::VERSION::STRING !~ /\A4\.1/
def test_counter_cache_column_update_on_really_destroy def test_counter_cache_column_update_on_really_destroy
...@@ -982,8 +995,8 @@ class ParanoiaTest < test_framework ...@@ -982,8 +995,8 @@ class ParanoiaTest < test_framework
parent_model_with_counter_cache_column = ParentModelWithCounterCacheColumn.create parent_model_with_counter_cache_column = ParentModelWithCounterCacheColumn.create
related_model = parent_model_with_counter_cache_column.related_models.create related_model = parent_model_with_counter_cache_column.related_models.create
assert_equal nil, related_model.instance_variable_get(:@after_destroy_callback_called) assert_nil related_model.instance_variable_get(:@after_destroy_callback_called)
assert_equal nil, related_model.instance_variable_get(:@after_commit_on_destroy_callback_called) assert_nil related_model.instance_variable_get(:@after_commit_on_destroy_callback_called)
related_model.really_destroy! related_model.really_destroy!
...@@ -1265,6 +1278,11 @@ class NotParanoidModelWithBelong < ActiveRecord::Base ...@@ -1265,6 +1278,11 @@ class NotParanoidModelWithBelong < ActiveRecord::Base
belongs_to :paranoid_model_with_has_one belongs_to :paranoid_model_with_has_one
end end
class NotParanoidModelWithBelongsAndAssocationNotSoftDestroyedValidator < NotParanoidModelWithBelong
belongs_to :parent_model
validates :parent_model, association_not_soft_destroyed: true
end
class FlaggedModel < PlainModel class FlaggedModel < PlainModel
acts_as_paranoid :flag_column => :is_deleted acts_as_paranoid :flag_column => :is_deleted
end end
......
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