From 7cdd896edb12599f82a0243d024edb1466a6f3a2 Mon Sep 17 00:00:00 2001 From: Mauro George Date: Tue, 31 Mar 2015 10:44:09 -0300 Subject: [PATCH] No more throws a error when use validate_uniqueness_of with scope of boolean column --- .../validate_uniqueness_of_matcher.rb | 20 +++++++-- .../validate_uniqueness_of_matcher_spec.rb | 41 +++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb b/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb index f72e26aa2..5a310858f 100644 --- a/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +++ b/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb @@ -257,6 +257,7 @@ def matches?(subject) @original_subject = subject @subject = subject.class.new @expected_message ||= :taken + @all_records = @subject.class.all set_scoped_attributes && validate_everything_except_duplicate_nils_or_blanks? && @@ -397,12 +398,11 @@ def model_class?(model_name) end def validate_after_scope_change? - if @options[:scopes].blank? + if @options[:scopes].blank? || all_scopes_are_booleans? true else - all_records = @subject.class.all @options[:scopes].all? do |scope| - previous_value = all_records.map(&scope).compact.max + previous_value = @all_records.map(&scope).compact.max next_value = if previous_value.blank? @@ -447,6 +447,8 @@ def dummy_scalar_value_for(column) DateTime.now when :uuid SecureRandom.uuid + when :boolean + true else 'dummy value' end @@ -474,11 +476,23 @@ def next_scalar_value_for(scope, previous_value) previous_value.next elsif previous_value.respond_to?(:to_datetime) previous_value.to_datetime.next + elsif boolean_value?(previous_value) + !previous_value else previous_value.to_s.next end end + def all_scopes_are_booleans? + @options[:scopes].all? do |scope| + @all_records.map(&scope).all? { |s| boolean_value?(s) } + end + end + + def boolean_value?(value) + value.in?([true, false]) + end + def defined_as_enum?(scope) @subject.class.respond_to?(:defined_enums) && @subject.defined_enums[scope.to_s] diff --git a/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb b/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb index 487472311..805477fd2 100644 --- a/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb +++ b/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb @@ -283,6 +283,43 @@ column_type: :string end + context 'when one of the scoped attributes is a boolean column' do + include_context 'it supports scoped attributes of a certain type', + column_type: :boolean + + context 'when there is more than one scoped attribute and all are boolean columns' do + it 'accepts when all the scoped attribute are true' do + record = build_record_validating_uniqueness( + scopes: [ + build_attribute(name: :scope1), + build_attribute(name: :scope2), + ] + ) + expect(record).to validate_uniqueness.scoped_to(:scope1, :scope2) + end + + it 'accepts when all the scoped attribute are false' do + record = build_record_validating_uniqueness( + scopes: [ + build_attribute(name: :scope1, value: false), + build_attribute(name: :scope2, value: false), + ] + ) + expect(record).to validate_uniqueness.scoped_to(:scope1, :scope2) + end + + it 'accepts when one scoped attribute is true and other is false' do + record = build_record_validating_uniqueness( + scopes: [ + build_attribute(name: :scope1), + build_attribute(name: :scope2, value: false), + ] + ) + expect(record).to validate_uniqueness.scoped_to(:scope1, :scope2) + end + end + end + context 'when one of the scoped attributes is an integer column' do include_context 'it supports scoped attributes of a certain type', column_type: :integer @@ -752,6 +789,8 @@ def dummy_scalar_value_for(attribute_type) Time.now when :uuid SecureRandom.uuid + when :boolean + true else raise ArgumentError, "Unknown type '#{attribute_type}'" end @@ -764,6 +803,8 @@ def next_version_of(value, value_type) SecureRandom.uuid elsif value.is_a?(Time) value + 1 + elsif value.in?([true, false]) + !value elsif value.respond_to?(:next) value.next end