David B.
David B.

Reputation: 838

Rails 5: Adding conditional custom validations

I would like to add a conditional custom validation to a model

Rails allows to create methods in order to create custom validations

class Invoice < ApplicationRecord
  validate :expiration_date_cannot_be_in_the_past

  def expiration_date_cannot_be_in_the_past
    if expiration_date.present? && expiration_date < Date.today
      errors.add(:expiration_date, "can't be in the past")
  end
end

It also allows to create conditional validations

class Order < ApplicationRecord
  validates :card_number, presence: true, if: :paid_with_card?

  def paid_with_card?
    payment_type == "card"
  end
end

How can I mix both ?

My guess would be something like

validate :condition, if: :other_condition

But this creates a SyntaxError:

syntax error, unexpected end-of-input, expecting keyword_end

Upvotes: 3

Views: 11958

Answers (4)

Sachin Metkari
Sachin Metkari

Reputation: 104

class User < ApplicationRecord
  validate :email_not_changeable, on: :update

  private

  def email_not_changeable
    return unless email_changed? && persisted?

    errors.add(:email, "can't be changed")
  end
end

Upvotes: 1

puneet18
puneet18

Reputation: 4427

You missed end, corrected code:

class Invoice < ApplicationRecord
  validate :expiration_date_cannot_be_in_the_past

  def expiration_date_cannot_be_in_the_past
    if expiration_date.present? && expiration_date < Date.today
      errors.add(:expiration_date, "can't be in the past")
    end # this end you missed in code
  end
end

Upvotes: 2

Ram
Ram

Reputation: 999

You can use each validator. For this you have to follow these steps:

  • Create a folder named validators inside your app directory.
  • Create a file named some_validator.rb
  • Write codes as like:

    class SomeValidator < ActiveModel::EachValidator
     def validate_each(object, attribute, value)
       return unless value.present?
       if some_condition1
         object.errors[attribute] << 'error msg for condition1'
       end
      object.errors[attribute] << 'error msg 2' if condition2
      object.errors[attribute] << 'error msg 3' if condition3
      object.errors[attribute] << 'error msg 4' if condition4
    end
    

    end

  • Now validates by this custom validator, as like: validates :attribute_name, some: true

  • Make sure your are giving the same name on validator. You can write the multiple conditions inside this custom validator.

Upvotes: 3

Andrey Deineko
Andrey Deineko

Reputation: 52357

When you fix the missing closing end to opened if in expiration_date_cannot_be_in_the_past, you can have the following working:

validate :expiration_date_cannot_be_in_the_past, if: :paid_with_card?

Upvotes: 5

Related Questions