Reputation: 1621
I need to validate that one boolean or another boolean is accepted in a form.
I tried
validates_acceptance_of :seller_accepted || :buyer_accepted
but this does not work
Upvotes: 1
Views: 189
Reputation: 102046
Rails validators are metaprogramming macros. The are read in when the class is evaluated. They add a list a validation rules (callbacks) to be run when valid?
is called to the class.
This is why you have to use the if
and unless
options with either a lambda, proc or a symbol which is actually called when the instance is validated.
Thats one of the reasons your code will not work. Using an if
or unless
condition does not work here either - Rails would add and error to seller_accepted
before it evaluates if: :buyer_accepted
.
In this case it the cleanest way to solve it may be to create a custom validation method:
validate :one_of_two_accepted
def one_of_two_accepted
unless acceptable?(seller_accepted) || acceptable?(buyer_accepted)
errors.add(:seller_accepted, "Must accept buyer or seller.")
end
end
def acceptable?(val)
['1', true, 'yes'].include?(val)
end
The acceptable?
method simulates the typecasting done by validates_acceptance_of
.
This is complete, minimal, verified example:
class Thing < ActiveRecord::Base
attr_accessor :seller_accepted
attr_accessor :buyer_accepted
validate :one_of_two_accepted
def acceptable?(val)
['1', true].include?(val)
end
def one_of_two_accepted
unless acceptable?(seller_accepted) || acceptable?(buyer_accepted)
errors.add(:seller_accepted, "Must accept buyer or seller.")
end
end
end
require 'rails_helper'
RSpec.describe Thing, type: :model do
let(:thing) { Thing.new }
describe 'acceptable?' do
it "accepts '1'" do
expect(thing.acceptable?('1')).to be_truthy
end
it "accepts 'true' " do
expect(thing.acceptable?(true)).to be_truthy
end
it "rejects others" do
expect(thing.acceptable?('gobbeligook')).to be_falsy
expect(thing.acceptable?('no')).to be_falsy
expect(thing.acceptable?(nil)).to be_falsy
expect(thing.acceptable?(0)).to be_falsy
end
end
describe 'custom validation' do
it 'is invalid if neither seller or buyer is accepted' do
expect(thing.valid?).to be_falsy
expect(thing.errors[:seller_accepted]).to include "Must accept buyer or seller."
end
it 'is valid if seller_accepted' do
thing.seller_accepted = true
thing.valid?
expect(thing.errors).to_not have_key :seller_accepted
end
it 'is valid if buyer_accepted' do
thing.buyer_accepted = true
thing.valid?
expect(thing.errors).to_not have_key :seller_accepted
end
end
end
Upvotes: 2