How to validate a field only if another has been filled out? - ruby-on-rails

How would I validate a field only if another has been filled out In Ruby on Rails 2.3.5?

class Model < ActiveRecord::Base
validates_presence_of :address, :if => :city?
end
:address and :city are both attributes of Model.

validates_presence_of accepts an if attribute that take one of three things, according to the documentation: a string, a method, or a proc.
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.
I ended up needing to use a proc, as I wanted to make sure that a certain parameter was filled out before validating:
validates_presence_of :bar, :if => Proc.new { |foo| !foo.age.blank? }

Related

Rails validate uniqueness only if conditional

I have a Question class:
class Question < ActiveRecord::Base
attr_accessible :user_id, :created_on
validates_uniqueness_of :created_on, :scope => :user_id
end
A given user can only create a single question per day, so I want to force uniqueness in the database via a unique index and the Question class via validates_uniqueness_of.
The trouble I'm running into is that I only want that constraint for non-admin users. So admins can create as many questions per day as they want. Any ideas for how to achieve that elegantly?
You can make a validation conditional by passing either a simple string of Ruby to be executed, a Proc, or a method name as a symbol as a value to either :if or :unless in the options for your validation. Here are some examples:
Prior to Rails version 5.2 you could pass a string:
# using a string:
validates :name, uniqueness: true, if: 'name.present?'
From 5.2 onwards, strings are no longer supported, leaving you the following options:
# using a Proc:
validates :email, presence: true, if: Proc.new { |user| user.approved? }
# using a Lambda (a type of proc ... and a good replacement for deprecated strings):
validates :email, presence: true, if: -> { name.present? }
# using a symbol to call a method:
validates :address, presence: true, if: :some_complex_condition
def some_complex_condition
true # do your checking and return true or false
end
In your case, you could do something like this:
class Question < ActiveRecord::Base
attr_accessible :user_id, :created_on
validates_uniqueness_of :created_on, :scope => :user_id, unless: Proc.new { |question| question.user.is_admin? }
end
Have a look at the conditional validation section on the rails guides for more details: http://edgeguides.rubyonrails.org/active_record_validations.html#conditional-validation
The only way I know of to guarantee uniqueness is through the database (e.g. a unique index). All Rails-only based approaches involve race conditions. Given your constraints, I would think the easiest thing would be to establish a separate, uniquely indexed column containing a combination of the day and user id which you'd leave null for admins.
As for validates_uniqueness_of, you can restrict validation to non-admins through use of an if or unless option, as discussed in http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of
Just add a condition to the validates_uniqueness_of call.
validates_uniqueness_of :created_on, scope: :user_id, unless: :has_posted?
def has_posted
exists.where(user_id: user_id).where("created_at >= ?", Time.zone.now.beginning_of_day)
end
But even better, just create a custom validation:
validate :has_not_posted
def has_not_posted
posted = exists.where(user: user).where("DATE(created_at) = DATE(?)", Time.now)
errors.add(:base, "Error message") if posted
end

how to perform a complex validation check on related model prior to performing an action on a model?

I am building a simple Ruby on Rails app for problem management. I have a problem model as follows:
class Problem < ActiveRecord::Base
attr_accessible :active, :impact, :incident_number, :issue_description, :root_cause, :user_id, :problem_summary, :incident_priority, :timeline_enabled
attr_accessor :enable_timeline
validates :problem_summary, :length => { :in => 10..100 }
belongs_to :user
has_one :timeline
has_many :actionitems
end
which has a has_many belongs_to association with the model for actionitems:
class Actionitem < ActiveRecord::Base
attr_accessible :completion_date, :description, :initial_due_date, :notes, :problem_id, :revised_due_date, :status, :user_id
belongs_to :problem
end
I would like to be able to update the problem record and save it with some set of limited validations (I still need to add those). However, I would like to have a "Complete problem investigation" button that would trigger a method on the problem controller to set the :active attribute on the problem record to false. I would like to be able to run a different, more complete set of validations on the problem record prior to performing this action and also to validate that all actionitems (if any) that were associated with this problem record are in :status "completed".
The two questions that I have:
How do I perform a specific set of validations only on a given action?
How can I validate that related instances of Actionitem are in status "complete" prior to performing an action on Problem?
This task seems very complex to me. If you could please point me to what I need to utilize in order to be able to achieve this that would be greatly appreciated! (I read on validates :on => :save etc and accepts_nested_attributes_for but I am not sure how to put all of this together to achieve the behavior that I want).
Many thanks for all your help!
try this
validates_length_of :problem_summary, :in => 10..100, :if => :status_active?
def status_active?
self.active == true
end
see in details - validations & validates_length_of
U need to apply checking conditions on validations like
validate :xyz , length => {:in => 1..12}, :if => , :if => lambda {self.active == true }
this validation will only run when aCTIVE IS TRUE. similarly you can add more validation with checking

how to validate a single attribute from a nested object

I am new to Rails and Ruby. On my view, I have 2 radio buttons that ask if the person is a resident of the US. If they are, a state select is shown. If they aren't, a country select is shown.
I am trying to validate that a state was selected, if the person is a resident of the US.
How can I create a validation and access the state out of the addresses_attributes?
Here is my model:
class Person < ActiveRecord::Base
has_many :addresses, :as => :addressable
has_one :user
accepts_nested_attributes_for :user, :allow_destroy => true
accepts_nested_attributes_for :addresses
attr_accessor :resident
attr_accessible :campaign_id,
:first_name,
:last_name,
:user_attributes,
:addresses_attributes,
:resident
validates :first_name, :presence => true
validates :last_name, :presence => true
validates_presence_of :resident, :message => "must be selected"
end
These are the relevant parameters being sent:
"resident"=>"true",
"addresses_attributes"=>{"0"=>{"country_code"=>"",
"state"=>""}}
You need custom validation method.
validate :check_state_presence
def check_state_presence
if self.resident && !self.addresses.state.present?
self.errors[:base] << "You need to Select State if you are a US resident."
end
end
You can sort it out using validates_inclusion_of instead.
Ruby API says:
If you want to validate the presence of a boolean field (where the real values are true and >false), you will want to use validates_inclusion_of :field_name, :in => [true, false].
This is due to the way Object#blank? handles boolean values: false.blank? # => true.
+1 to #VelLes for the help in pointing me in the right direction. I am answering my own question because I had to change #VelLes example a bit to get it to work and I want other people to see the full solution.
Since I am using attr_accessor as a virtual attribute, when the true/false value comes in from the radio button, it gets stored as a string. Therefore if self.resident = "false", it will get evaluated to true.
You can do self.resident == 'false' or convert to a boolean and add a new self.resident? method. I chose the latter.
The boolean conversion came from this blog post, add to a file in config/initializers
class String
def to_bool
return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
return false if self == false || self.blank? || self =~ (/(false|f|no|n|0)$/i)
raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
end
end
My final code is:
validate :check_state_presence
def resident?
resident.to_bool
end
def check_state_presence
if self.resident? && !self.addresses[0].state.present?
#the inline version of the error message
self.addresses[0].errors.add(:state, "must be selected")
end
end
Please let me know if there is a better 'rails' way to do this!

Rails - How to validate a field only if a another field has a certain value?

In my form I have a Select with three values: Apple, Banana and Cherry. If I choose Apple from the select I hide another Select- and a Text-field with some Javascript, because when Apple is chosen, there is no need to fill in these other two fields anymore.
So now I have a problem with validating my form when it's submitted.
I've found some similar problems for example in the case of
"Only validate a field if another is blank."
This problem was solved like this:
validates_presence_of :mobile_number, :unless => :home_phone?
So I've just tried the first thing which popped into my mind:
validates_presence_of :state, :granted_at, :if => :type != 1
But when I run it, I get this error:
undefined method `validate' for true:TrueClass
How can I conditionally perform a validation on a field based on whether or not another field has a particular value?
Because it is executable code you need to wrap it in a lambda or a Proc object like so:
validates_presence_of :state, :granted_at, :if => lambda { |o| o.type != 1 }
# alternatively:
..., :if => lambda { self.type != 1 }
..., :if => Proc.new { |o| o.type != 1 }
..., :if ->(o) { o.type != 1 }
You can use if flag and lambda:
validates_presence_of :state, :granted_at, :if => lambda {self.type != 1}
Or just create private method:
validates_presence_of :state, :granted_at, :if => :valid_type?
private
def valid_type?
type != 1
end
although the above mentioned ways are best practices but you can also make it simple like this:
validates_presence_of :state, :granted_at, :if => "type!=1"
Building on the previous answers, you can also use a shorter "arrow" syntax
validates :state, :granted_at, presence: true, if: ->(o) { o.type != 1 }

RoR: Model validation question

I have a basic ActiveRecord model in which i have two fields that i would like to validate. The requirement is that at least one of the fields must have a value. Both can have values, but at least one needs a value.
How do i express this with
validates_presence_of
statements? For example:
validates_presence_of :main_file
validates_presence_of :alt_file
i don't want an error to be generated if only one of them is empty, only if both are empty.
validates_presence_of :main_file, :if => Proc.new { |p| p.alt_file.blank? }
validates_presence_of :alt_file, :if => Proc.new { |p| p.main_file.blank? }
changing .nil? to .blank? does the trick!

Resources