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 4ce092f0a..fe7466849 100644 --- a/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +++ b/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb @@ -818,9 +818,7 @@ def next_value_for(scope, previous_value) end def next_scalar_value_for(scope, previous_value) - column = column_for(scope) - - if column.type == :uuid + if uuid?(scope) SecureRandom.uuid elsif defined_as_enum?(scope) available_values = available_enum_values_for(scope, previous_value) @@ -838,6 +836,13 @@ def next_scalar_value_for(scope, previous_value) end end + def uuid?(scope) + [ + column_for(scope), + attribute_type_for(scope), + ].compact.map(&:type).include? :uuid + end + def all_scopes_are_booleans? @options[:scopes].all? do |scope| @all_records.map(&scope).all? { |s| boolean_value?(s) } @@ -931,6 +936,12 @@ def column_for(scope) model.columns_hash[scope.to_s] end + def attribute_type_for(scope) + if model.respond_to?(:attribute_types) + model.attribute_types[scope.to_s] + end + end + def column_limit_for(attribute) column_for(attribute).try(:limit) end diff --git a/spec/support/unit/helpers/active_model_versions.rb b/spec/support/unit/helpers/active_model_versions.rb index db7037de7..9d4d48d8e 100644 --- a/spec/support/unit/helpers/active_model_versions.rb +++ b/spec/support/unit/helpers/active_model_versions.rb @@ -28,5 +28,9 @@ def active_model_supports_absence_validation? def active_model_supports_strict? active_model_version >= 3.2 end + + def active_model_supports_attributes_api? + ::ActiveModel::VERSION::MAJOR >= 5 + end end end 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 c165b394c..2b5ebccf5 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 @@ -1475,6 +1475,49 @@ def name=(name) end end + if active_model_supports_attributes_api? + context 'Rails 5 attributes API' do + it 'builds uuid for attributes API type :uuid' do + scopes = { + scopes: [ + { + name: :uuid, + column_type: :string, + value_type: :string, + options: { + array: false, + }, + }, + ], + } + model = define_model_validating_uniqueness(scopes) + + module ActiveRecord + module Type + class Uuid < ActiveRecord::Type::String + def type + :uuid + end + end + end + end + + ActiveRecord::Type.register(:uuid, + ActiveRecord::Type::Uuid, + override: false,) + model.attribute(:uuid, :uuid) + + record = model.new(uuid: next_scalar_value_for(:uuid, '', model.new)) + expect(SecureRandom).to receive(:uuid).and_call_original + + expect(record).to validate_uniqueness. + scoped_to(:uuid). + ignoring_case_sensitivity + end + end + + end + let(:model_attributes) { {} } def default_attribute @@ -1560,6 +1603,12 @@ def dummy_scalar_value_for(attribute_type) end end + def next_scalar_value_for(scope, previous_value, record) + subject = validate_uniqueness + subject.instance_variable_set(:@given_record, record) + subject.send(:next_scalar_value_for, scope, previous_value) + end + def next_version_of(value, value_type) if value.is_a?(Array) [ next_version_of(value[0], value_type) ]