From c53681ed1b393d70569f6a80235afdf71e5e6faf Mon Sep 17 00:00:00 2001 From: rbr Date: Sun, 12 Jun 2016 15:57:20 +0200 Subject: [PATCH] Add options for uniqueness validations. Resolves #319 --- README.md | 24 ++++++++++++++++++++++++ lib/paranoia.rb | 9 +++++++-- test/paranoia_test.rb | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5faafe82..a153c0e0 100644 --- a/README.md +++ b/README.md @@ -181,6 +181,30 @@ Client.restore(id, :recursive => true) client.restore(:recursive => true) ``` +If you want to validate uniqueness but want to include deleted records: + +``` ruby +class Client < ActiveRecord::Base + acts_as_paranoid + + validates :name, uniqueness: { paranoia: :with_deleted } + + ... +end +``` + +If you don't use the default_scope and want to validate uniqueness but want to exclude deleted records: + +``` ruby +class Client < ActiveRecord::Base + acts_as_paranoid without_default_scope: true + + validates :name, uniqueness: { paranoia: :without_deleted } + + ... +end +``` + For more information, please look at the tests. #### About indexes: diff --git a/lib/paranoia.rb b/lib/paranoia.rb index ec2c72b8..b03a4b87 100644 --- a/lib/paranoia.rb +++ b/lib/paranoia.rb @@ -212,8 +212,9 @@ def self.acts_as_paranoid(options={}) alias_method :destroy_without_paranoia, :destroy include Paranoia - class_attribute :paranoia_column, :paranoia_sentinel_value + class_attribute :paranoia_column, :paranoia_sentinel_value, :paranoid_by_default + self.paranoid_by_default = !options[:without_default_scope] self.paranoia_column = (options[:column] || :deleted_at).to_s self.paranoia_sentinel_value = options.fetch(:sentinel_value) { Paranoia.default_sentinel_value } def self.paranoia_scope @@ -221,7 +222,7 @@ def self.paranoia_scope end class << self; alias_method :without_deleted, :paranoia_scope end - unless options[:without_default_scope] + if paranoid_by_default default_scope { paranoia_scope } end @@ -266,6 +267,10 @@ module UniquenessParanoiaValidator def build_relation(klass, table, attribute, value) relation = super(klass, table, attribute, value) return relation unless klass.respond_to?(:paranoia_column) + + add_paranoia_scope = (klass.paranoid_by_default && options[:paranoia] != :with_deleted) || (!klass.paranoid_by_default && options[:paranoia] == :without_deleted) + return relation unless add_paranoia_scope + arel_paranoia_scope = klass.arel_table[klass.paranoia_column].eq(klass.paranoia_sentinel_value) if ActiveRecord::VERSION::STRING >= "5.0" relation.where(arel_paranoia_scope) diff --git a/test/paranoia_test.rb b/test/paranoia_test.rb index a1bceeee..5f73cddc 100644 --- a/test/paranoia_test.rb +++ b/test/paranoia_test.rb @@ -253,6 +253,24 @@ def test_active_column_model_with_uniqueness_validation_still_works_on_non_delet refute b.valid? end + def test_uniqueness_with_deleted + a = ParanoidWithUniquenessWithDeleted.create!(name: "A") + b = ParanoidWithUniquenessWithDeleted.new(name: "A") + refute b.valid? + end + + def test_uniqueness_without_default_scope_with_deleted + a = ParanoidWithoutDefaultScopeWithUniqueness.create!(name: "A") + b = ParanoidWithoutDefaultScopeWithUniqueness.new(name: "A") + refute b.valid? + end + + def test_uniqueness_without_default_scope_without_deleted + a = ParanoidWithoutDefaultScopeWithUniquenessWithoutDeleted.create!(name: "A") + b = ParanoidWithoutDefaultScopeWithUniquenessWithoutDeleted.new(name: "A") + refute b.valid? + end + def test_sentinel_value_for_custom_sentinel_models model = CustomSentinelModel.new assert_equal 0, model.class.count @@ -1105,6 +1123,24 @@ def paranoia_destroy_attributes end end +class ParanoidWithUniquenessWithDeleted < ActiveRecord::Base + self.table_name = 'featureful_models' + acts_as_paranoid + validates :name, uniqueness: { paranoia: :with_deleted } +end + +class ParanoidWithoutDefaultScopeWithUniqueness < ActiveRecord::Base + self.table_name = 'featureful_models' + acts_as_paranoid without_default_scope: true + validates :name, uniqueness: true +end + +class ParanoidWithoutDefaultScopeWithUniquenessWithoutDeleted < ActiveRecord::Base + self.table_name = 'featureful_models' + acts_as_paranoid + validates :name, uniqueness: { paranoia: :without_deleted } +end + class NonParanoidModel < ActiveRecord::Base end