diff --git a/NEWS.md b/NEWS.md index cf554860..bbf2f31e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,10 @@ * Add ability to test `:autosave` option on associations. +* Fix `validate_uniqueness_of(...).allow_nil` so that it can be used against an + non-password attribute which is in a model that `has_secure_password`. Doing + so previously would result in a "Password digest missing on new record" error. + # v 2.5.0 * Fix Rails/Test::Unit integration to ensure that the test case classes we are diff --git a/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb b/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb index 50b4ed38..4f074ce3 100644 --- a/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +++ b/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb @@ -115,10 +115,18 @@ module Shoulda # :nodoc: @subject.class.new.tap do |instance| instance.send("#{@attribute}=", value) + if has_secure_password? + instance.password = 'password' + instance.password_confirmation = 'password' + end instance.save(validate: false) end end + def has_secure_password? + @subject.class.ancestors.map(&:to_s).include?('ActiveModel::SecurePassword::InstanceMethodsOnActivation') + end + def set_scoped_attributes if @options[:scopes].present? @options[:scopes].all? do |scope| diff --git a/spec/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb b/spec/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb index 70689ef8..7c28f880 100644 --- a/spec/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb +++ b/spec/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb @@ -293,6 +293,18 @@ describe Shoulda::Matchers::ActiveModel::ValidateUniquenessOfMatcher do end end + if active_model_3_1? + context 'when the subject has a secure password' do + it 'allows nil on the attribute' do + model = define_model(:example, attr: :string, password_digest: :string) do |m| + validates_uniqueness_of :attr, allow_nil: true + has_secure_password + end.new + expect(model).to matcher.allow_nil + end + end + end + it "should create a nil and verify that it is allowed" do model = define_model_with_allow_nil expect(model).to matcher.allow_nil