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"=>""}}
+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!
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.