aarkerio
aarkerio

Reputation: 2364

Rails acceptance validation always correct

I have this Rails 6.1 model:

class BankAccount < ApplicationRecord
  belongs_to :donor

  before_validation -> { 
      puts #AccAuth: #{account_authorization}"  
  }
  validates :account_authorization, acceptance: { accept: [true, 'true', '1'] }
end

The attribute "account_authorization" doesn't exist in the database, is not a SQL column, is a field from a JSON payload and must be one of the values in the array to be sure the bank answered successfully.

I'm trying to test in my Rspec file like:

let(:invalid_bank_account) do
  Fabricate.build(:bank_account, account_authorization: nil, donor: Fabricate(:donor))
end

it 'is not valid record without a truty account_authorization value' do
  puts "valid? #{invalid_bank_account.valid?}"
  expect(invalid_bank_account).not_to be_valid
end

The result is:

### bank_account.rb >> AccAuth 
  >> nil 
 ### bank_account_spec.rb >> valid? 
 >> true
 Failure/Error: expect(invalid_bank_account).not_to be_valid

The validation is always successful even if there is no "account_authorization" value in the Model initializer. I'm validating a non-SQL attribute in the correct way?

Upvotes: 1

Views: 77

Answers (2)

BenFenner
BenFenner

Reputation: 1078

I like Alex's answer for how to use the acceptance validator in your situation. However, you could also use the Rails inclusion validator if you'd prefer. I find it a bit clearer and cleaner:

validates :account_authorization, inclusion: { in: [true, 'true', '1'] }

Also, double check to see if you also want to allow the integer one (not just the string) as valid.

To set the verbiage for the validation failure message, you can set it explicitly as mentioned in the docs, or better, put it in the locale file:

#config/locales/en.yml
en:
  errors:
    format: "%{message}"
    header: 'The form entry has errors; please correct the errors below and re-submit.'
    messages:
      accepted:     "The %{attribute} was not accepted; please accept the %{attribute}."
      inclusion:    "The chosen %{attribute} is not available; please choose an available option." # Rails 4-
      inclusion_of: "The chosen %{attribute} is not available; please choose an available option." # Rails 5+

Above copied from my more complete example here: https://stackoverflow.com/a/73837042/14837782

Upvotes: 1

Alex
Alex

Reputation: 30036

If attribute is nil it is ignored:

...This check is performed only if terms_of_service is not nil.

https://api.rubyonrails.org/classes/ActiveModel/Validations/HelperMethods.html#method-i-validates_acceptance_of

You have to specifically disallow nil:

class Account < ApplicationRecord
  validates :account_authorization, acceptance: {allow_nil: false}
end
>> Account.new(account_authorization: nil).valid?
=> false
>> Account.new(account_authorization: "1").valid?
=> true

Note, that this will force you to supply account_authorization value every time you're making an update.

Upvotes: 5

Related Questions