Home > Articles

  • Print
  • + Share This
This chapter is from the book

This chapter is from the book

8.4 Conditional Validation

Since validation methods are based on the Active Model Callback API, they all accept :if and :unless options to determine at runtime (not during class definition) whether the validation needs to be run or not.

The following three types of arguments can be supplied as parameters:

  • Symbol The name of a method to invoke as a symbol. This is probably the most common option and offers the best performance.

  • String A snippet of Ruby code to eval might be useful when the condition is really short, but keep in mind that evaling statements is relatively slow.

  • Proc A block of code to be instance_evaled, so that self is the current record. Perhaps the most elegant choice for one-line conditionals.

validates_presence_of :approver, if: -> { approved? && !legacy? }

8.4.1 Usage and Considerations

A primary use case for conditional validation is when an object can be validly persisted in more than one state. A very common example involves the User (or Person) model, used for login and authentication.

validates_presence_of :password, if: :password_required?
validates_presence_of :password_confirmation, if: :password_required?
validates_length_of :password, within: 4..40, if: :password_required?
validates_confirmation_of :password, if: :password_required?

This code is not DRY (meaning that it is repetitive). You can refactor it to make it a little dryer using the with_options method that Active Support mixes into Object.

with_options if: :password_required? do |user|
  user.validates_presence_of :password
  user.validates_presence_of :password_confirmation
  user.validates_length_of :password, within: 4..40
  user.validates_confirmation_of :password
end

The example validations check for the two cases when a (plaintext) password field should be required in order for the model to be valid.

def password_required?
  encrypted_password.blank? || !password.blank?
end

The first case is if the encrypted_password attribute is blank, because that means we are dealing with a new User instance that has not been given a password yet. The other case is when the password attribute itself is not blank; perhaps this is happening during an update operation when the user is attempting to reset a password.

8.4.2 Validation Contexts

Another way to accomplish conditional validation leverages support for validation contexts. Declare a validation and pass the name of an application-specific validation context as the value of the :on option. That validation will now only be checked when explicitly invoked using record.valid?(context_name).

Consider the following example involving a report generation app. Saving a report without a name is fine, but publishing one without a name is not.

class Report < ActiveRecord::Base
  validates_presence_of :name, on: :publish
end

class ReportsController < ApplicationController
  expose(:report)

  # POST /reports/1/publish
  def publish
    if report.valid? :publish
      redirect_to report, notice: "Report published"
    else
      flash.now.alert = "Can't publish unnamed reports!"
      render :show
    end
  end
end
  • + Share This
  • 🔖 Save To Your Account