Boolean attribute validation

Fomr time to time the question arises how to validate Boolean attributes.
The answer is: Better do not.

Antipattern Boolean validation

Unfortunately, there is still the recommendation alive to validate Boolean attributes with validates_inclusion_of. So whether the value is either true or false, like:

class User < ApplicationRecord
  validates :receive_newsletter, inclusion: [true, false] 

and the expectation:

RSpec.describe User, type: :model do
  it { validate_inclusion_of(:receive_newsletter).in_array([true, false]) }

However, the Shoulda Matchers test warns doing Boolean validation:

Warning from shoulda-matchers:

You are using `validate_inclusion_of` to assert that a boolean column
allows boolean values and disallows non-boolean ones. Be aware that it
is not possible to fully test this, as boolean columns will
automatically convert non-boolean values to boolean ones. Hence, you
should consider removing this test.


The solution is pretty simple: better do not validate the Booleans.
In fact, the 3-state-problem (Boolean attribute with possible value true, false and nil) should be avoided. That means the attribute is meant to only accept 2 states (true and false) and the undefined state is excluded by configuring the default value false on the database level:

class SetUsersReceiveNewsletterDefault < ActiveRecord::Migration
  change_column :users, :receive_newsletter, :boolean, default: false

By going with this convention, it is always clear that the default value is always false. Unless it is required to be true. Then the value should be explicitly set in a before_create callback.
With this approach, no one needs to check the default value, which might be set on the database level. And validating the attribute can be waived.

Acceptance validation (Boolean attribute)

A different case is the validation of Boolean attributes, that the expected value (true) is really set. For example this applies to general terms and conditions acceptance.
There is a dedicated validator for this in RubyOnRails, the ActiveModel::Validations::AcceptanceValidator:

class User < ApplicationRecord
  validates :terms_and_conditions, acceptance: true