poctek
poctek

Reputation: 256

Rails validations depending on the value that may be not present

I've got a service object which has several validations that validate two params. Everything works fine until these params are "". In this case, even though I validate their presence, later validations raise errors. How can I make my code validate the presence first and then, only if the values are present, continue the validations?

class SubscriptionPause
  include ActiveModel::Model
  extend ActiveModel::Naming

  attr_accessor :paused_from, :paused_till, :params, :id

  validates :paused_from, presence: true, allow_nil: true
  validates :paused_till, presence: true, allow_nil: true

  validate :paused_from_cant_be_later_than_subscription_ends
  validate :paused_from_cant_be_in_the_past
  validate :paused_till_is_later_than_paused_from

  def initialize(params)
    @params = params
    @paused_form = params[:paused_from]
    @paused_till = params[:paused_till]
  end

  def create
    if valid?
      ...
    else
      ...
    end
  end

  private

  def subscription
    @subscription || Subscription.find(params[:id])
  end

  def paused_from_cant_be_in_the_past
    if !paused_from.empty? && paused_from.to_date < Date.today
      errors.add(:paused_from, I18n.t("..."))
    end
  end

  def paused_till_is_later_than_paused_from
    if paused_from > paused_till
      errors.add :paused_from, I18n.t("...")
    end
  end

  def paused_from_cant_be_later_than_subscription_ends
    if !paused_from.empty? && subscription.expire_date < paused_from
      errors.add :paused_from, I18n.t("...")
    end
  end

end

Upvotes: 0

Views: 84

Answers (2)

joewoodward
joewoodward

Reputation: 263

Based on your comment above, it sounds like you never want the from or till to be nil so remove allow_nil: true. Then just add a conditional to the other validations as suggested by Rahul

validates :paused_from, presence: true
validates :paused_till, presence: true

validate :paused_from_cant_be_later_than_subscription_ends, if: :params_present?
validate :paused_from_cant_be_in_the_past, if: :params_present?
validate :paused_till_is_later_than_paused_from, if: :params_present?

def params_present?
  paused_from.present? && paused_till.present?
end

P.S. don't use and over && unless you know why (suggested by Rahul). && is better in nearly all cases. Difference between "and" and && in Ruby?

Upvotes: 1

Rahul Sharma
Rahul Sharma

Reputation: 5995

You could do something like this:

validate :paused_from_cant_be_later_than_subscription_ends, :if => :params_present?
validate :paused_from_cant_be_in_the_past, :if => :params_present?
validate :paused_till_is_later_than_paused_from, :if => :params_present?

def params_present?
  return params[paused_from].present? and params[paused_till].present?
end

Upvotes: 1

Related Questions