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.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)
* [#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)
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.
#### About indexes:
......
......@@ -299,5 +299,14 @@ module ActiveRecord
class UniquenessValidator < ActiveModel::EachValidator
prepend UniquenessParanoiaValidator
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
......@@ -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_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_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',
'featureful_models' => 'deleted_at DATETIME, name VARCHAR(32)',
'plain_models' => 'deleted_at DATETIME',
......@@ -117,9 +118,9 @@ class ParanoiaTest < test_framework
model.remove_called_variables # clear called callback flags
model.destroy
assert_equal nil, model.instance_variable_get(:@update_callback_called)
assert_equal nil, model.instance_variable_get(:@save_callback_called)
assert_equal nil, model.instance_variable_get(:@validate_called)
assert_nil model.instance_variable_get(:@update_callback_called)
assert_nil model.instance_variable_get(:@save_callback_called)
assert_nil model.instance_variable_get(:@validate_called)
assert model.instance_variable_get(:@destroy_callback_called)
assert model.instance_variable_get(:@after_destroy_callback_called)
......@@ -133,12 +134,12 @@ class ParanoiaTest < test_framework
model.remove_called_variables # clear called callback flags
model.delete
assert_equal nil, model.instance_variable_get(:@update_callback_called)
assert_equal nil, model.instance_variable_get(:@save_callback_called)
assert_equal nil, model.instance_variable_get(:@validate_called)
assert_equal nil, model.instance_variable_get(:@destroy_callback_called)
assert_equal 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(:@update_callback_called)
assert_nil model.instance_variable_get(:@save_callback_called)
assert_nil model.instance_variable_get(:@validate_called)
assert_nil model.instance_variable_get(:@destroy_callback_called)
assert_nil model.instance_variable_get(:@after_destroy_callback_called)
assert_nil model.instance_variable_get(:@after_commit_callback_called)
end
def test_delete_in_transaction_behavior_for_plain_models_callbacks
......@@ -149,11 +150,11 @@ class ParanoiaTest < test_framework
model.delete
end
assert_equal nil, model.instance_variable_get(:@update_callback_called)
assert_equal nil, model.instance_variable_get(:@save_callback_called)
assert_equal nil, model.instance_variable_get(:@validate_called)
assert_equal 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(:@update_callback_called)
assert_nil model.instance_variable_get(:@save_callback_called)
assert_nil model.instance_variable_get(:@validate_called)
assert_nil model.instance_variable_get(:@destroy_callback_called)
assert_nil model.instance_variable_get(:@after_destroy_callback_called)
assert model.instance_variable_get(:@after_commit_callback_called)
end
......@@ -226,7 +227,7 @@ class ParanoiaTest < test_framework
end
def test_default_sentinel_value
assert_equal nil, ParanoidModel.paranoia_sentinel_value
assert_nil ParanoidModel.paranoia_sentinel_value
end
def test_without_default_scope_option
......@@ -375,7 +376,7 @@ class ParanoiaTest < test_framework
model = CallbackModel.new
model.save
model.delete
assert_equal nil, model.instance_variable_get(:@destroy_callback_called)
assert_nil model.instance_variable_get(:@destroy_callback_called)
end
def test_destroy_behavior_for_callbacks
......@@ -948,8 +949,8 @@ class ParanoiaTest < test_framework
parent_model_with_counter_cache_column = ParentModelWithCounterCacheColumn.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_equal nil, related_model.instance_variable_get(:@after_commit_on_destroy_callback_called)
assert_nil related_model.instance_variable_get(:@after_destroy_callback_called)
assert_nil related_model.instance_variable_get(:@after_commit_on_destroy_callback_called)
related_model.destroy
......@@ -964,6 +965,18 @@ class ParanoiaTest < test_framework
related.valid?
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
if ActiveRecord::VERSION::STRING !~ /\A4\.1/
def test_counter_cache_column_update_on_really_destroy
......@@ -982,8 +995,8 @@ class ParanoiaTest < test_framework
parent_model_with_counter_cache_column = ParentModelWithCounterCacheColumn.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_equal nil, related_model.instance_variable_get(:@after_commit_on_destroy_callback_called)
assert_nil related_model.instance_variable_get(:@after_destroy_callback_called)
assert_nil related_model.instance_variable_get(:@after_commit_on_destroy_callback_called)
related_model.really_destroy!
......@@ -1265,6 +1278,11 @@ class NotParanoidModelWithBelong < ActiveRecord::Base
belongs_to :paranoid_model_with_has_one
end
class NotParanoidModelWithBelongsAndAssocationNotSoftDestroyedValidator < NotParanoidModelWithBelong
belongs_to :parent_model
validates :parent_model, association_not_soft_destroyed: true
end
class FlaggedModel < PlainModel
acts_as_paranoid :flag_column => :is_deleted
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