Rails 5: Adding conditional custom validations

10,482

Solution 1

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?

Solution 2

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.

Solution 3

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

Solution 4

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
Share:
10,482
David B.
Author by

David B.

Updated on June 04, 2022

Comments

  • David B.
    David B. almost 2 years

    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
    
  • craigcaulfield
    craigcaulfield over 5 years
    Code is always good, but it also helps to add some comments or context about how this solves the original question.
  • Greenstick
    Greenstick over 5 years
    Echoing @craigcaulfield; please add some comments around how this solves the original question.