Rails Model Validation Conditional allow_nil? - ruby-on-rails

So I was wondering if we can have a conditional allow_nil option for a validation on a rails model.
What I would like to do is to be able to allow_nil depending upon some logic(some other attribute)
So I have a product model which can be saved as draft. and when being saved as draft the price can be nil, but when not a draft save the price should be numerical. how can I achieve this. The following doesn't seem to work. It works for draft case but allows nil even when status isn't draft.
class Product<ActiveRecord::Base
attr_accessible :status, price
validates_numericality_of :price, allow_nil: :draft?
def draft?
self.status == "draft"
end
end
Looking at rails docs I looks like there isn't an option to pass a method to allow_nil?
One possible solution is to do separate validations for both cases
with_options :unless => :draft? do |normal|
normal.validates_numericality_of :price
end
with_options :if => :draft? do |draft|
draft.validates_numericality_of :price, allow_nil: true
end
Any other ways to get this working ?
Thanks

You can use if and unless to do the following
class Product<ActiveRecord::Base
attr_accessible :status, price
validates_numericality_of :price, allow_nil: true, if: :draft?
validates_numericality_of :price, allow_nil: false, unless: :draft?
def draft?
self.status == "draft"
end
end
With the above, you will have 2 validations set up, one that applies when draft? == true, which will allow nils, and one where draft? == false which will not allow nils

You don't need allow_nil then, just use if:
class Product < ActiveRecord::Base
attr_accessible :status, price
validates_numericality_of :price, if: -> { (draft? && !price.nil?) || !draft? }
def draft?
self.status == "draft"
end
end

Related

Validate uniquiness before save, with condition [duplicate]

On Rails 5.
I have an Order model with a description attribute. I only want to validate it's presence if one of two conditions is met: if the current step is equal to the first step OR if require_validation is equal to true.
I can easily validate based on one condition like this:
validates :description, presence: true, if: :first_step?
def first_step?
current_step == steps.first
end
but I am not sure how to go about adding another condition and validating if one or the other is true.
something like:
validates :description, presence: true, if: :first_step? || :require_validation
Thanks!
You can use a lambda for the if: clause and do an or condition.
validates :description, presence: true, if: -> {current_step == steps.first || require_validation}
Can you just wrap it in one method? According to the docs
:if - Specifies a method, proc or string to call to determine if the validation should occur (e.g. if: :allow_validation, or if: Proc.new { |user| user.signup_step > 2 }). The method, proc or string should return or evaluate to a true or false value.
validates :description, presence: true, if: :some_validation_check
def some_validation_check
first_step? || require_validation
end
You can pass a lambda to be evaluated as the if condition.
Try:
validates :description, presence: true, if: -> { first_step? || require_validation }
If you don't want to add one method as Jared say then you can try use lambda
validates :description, presence: true, if: ->{ first_step? || require_validation }
If you have a lot case , you can design for validates
validates_presence_of :price_tech_fee, if: :price_tech_fee_require?, :message => :required
validates_presence_of :percentage_tech_fee, if: :percentage_tech_fee_require?, :message => :required
def percentage_tech_fee_require?
is_active? && is_transaction_percentage? && is_premium?
end
def is_active?
!self.is_deleted && self.is_active
end
def is_transaction_percentage?
self.is_per_transaction && self.is_percentage
end
def is_premium?
....
end

Rails - Validation :if one condition is true

On Rails 5.
I have an Order model with a description attribute. I only want to validate it's presence if one of two conditions is met: if the current step is equal to the first step OR if require_validation is equal to true.
I can easily validate based on one condition like this:
validates :description, presence: true, if: :first_step?
def first_step?
current_step == steps.first
end
but I am not sure how to go about adding another condition and validating if one or the other is true.
something like:
validates :description, presence: true, if: :first_step? || :require_validation
Thanks!
You can use a lambda for the if: clause and do an or condition.
validates :description, presence: true, if: -> {current_step == steps.first || require_validation}
Can you just wrap it in one method? According to the docs
:if - Specifies a method, proc or string to call to determine if the validation should occur (e.g. if: :allow_validation, or if: Proc.new { |user| user.signup_step > 2 }). The method, proc or string should return or evaluate to a true or false value.
validates :description, presence: true, if: :some_validation_check
def some_validation_check
first_step? || require_validation
end
You can pass a lambda to be evaluated as the if condition.
Try:
validates :description, presence: true, if: -> { first_step? || require_validation }
If you don't want to add one method as Jared say then you can try use lambda
validates :description, presence: true, if: ->{ first_step? || require_validation }
If you have a lot case , you can design for validates
validates_presence_of :price_tech_fee, if: :price_tech_fee_require?, :message => :required
validates_presence_of :percentage_tech_fee, if: :percentage_tech_fee_require?, :message => :required
def percentage_tech_fee_require?
is_active? && is_transaction_percentage? && is_premium?
end
def is_active?
!self.is_deleted && self.is_active
end
def is_transaction_percentage?
self.is_per_transaction && self.is_percentage
end
def is_premium?
....
end

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
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?
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

Rails validation with if

I am having trouble getting my validation working properly. I am trying to check if the user has checked :noship then :weight must equal 0.
With the code below I get an "Undefined local variable noship" error
snippet from models/package.rb
class Package < ActiveRecord::Base
belongs_to :campaign
validates_presence_of :weight, :campaign
validates :weight, numericality: { equal_to: 0 }, :if => :noship_test?
def noship_test?
noship == true
end
rails_admin do
object_label_method do
:custom_name
end
end
The :noship and :weight values are working and being saved correctly to my database
Since noship belongs to campaign model, you'll need to do something like:
def noship_test?
campaign && campaign.noship
end
However having such a method seems redundant, just pass a lambda to if key:
validates :weight, numericality: { equal_to: 0 }, :if => ->(r) { r.campaign && r.campaign.noship }

How do I use an if statement in Rails validation?

I'm trying to create a validation statement that validates an object if service_area is present UNLESS service_area_radius==0
Here's the statement I created, which doesn't work:
validates :service_area, :presence => true, unless: "service_area_radius==0"
http://railscasts.com/episodes/41-conditional-validations
Like this:
validates_presence_of :password, :if => :should_validate_password?
validates_presence_of :country
validates_presence_of :state, :if => :in_us?
attr_accessor :updating_password
def in_us?
country == 'US'
end
def should_validate_password?
updating_password || new_record?
end
validates :service_area,
presence: {message: "Area Radius is missing."}, if: :radius_found?
private
def radius_found?
service_area_radius > 0
end
The validation for service_area will be executed if radius_found? returns true.
radius_found? will return true when the service_area_radius(attribute) hold value > 0.
Adding a custom message with message: option, when the validation fails.

Resources