SupremeA
SupremeA

Reputation: 1621

How can I validate acceptance of one boolean or another boolean

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

Answers (1)

max
max

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.

Edit.

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

Related Questions