Ruby-on-Rails Conditional Validation

Orange diamond sign featuring a black exclamation point

Looking to implement a new feature within our codebase, we were looking at adding some new fields and applying validation for these, which seemed simple enough.

However when working with legacy codebases there will always be considerations for existing requirements and ensuring that a new change does not cause a regression. Having a reliable set of tests here is great, because they can point out these regressions before your users do.


In a Ruby-on-Rails model, a validation for the presence of firstname and lastname can be added with:

class User < ApplicationRecord
  validates :firstname, presence: true
  validates :surname, presence: true
end

However, if this is being added to an existing model which is already in use and contains records without these fields. The business logic of the application may also require that creating and updating these User models without requiring either of the name fields.

In this case the validation may only be required if the user of the system has begun adding one of these fields, ensuring that if either one fields has a value, then the validation should be run. This is where conditional validation would apply.

With conditional validation, we can define the circumstances in which the validation rules should be applied. E.g. to ensure both firstname and surname are present if one has value, this could be added with:

class User < ApplicationRecord
  validates :firstname, presence: true, if: :has_name?
  validates :surname, presence: true, if: :has_name?

  def has_name?
    firstname.present? || surname.present?
  end
end

Here, the two validation rules are only conditionally applied if the has_name block is evaluated to true. In this case, the block evaluates to true if either firstname or surname is present.

Alternately, the validation can be handled as a group within a conditional block:

class User < ApplicationRecord
  with_options if: :has_name? do |user|
    user.validates :firstname, presence: true
    user.validates :surname, presence: true
  end

  def has_name?
    firstname.present? || surname.present?
  end
end

With these validations in place, a User record will be value with either both fields empty or both fields present, but invalid if only one of the fields is present.