how to validate a single attribute from a nested object

Go To StackoverFlow.com

0

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"=>""}}
2012-04-03 21:37
by flynfish


0

+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!

2012-04-04 15:48
by flynfish


1

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 
2012-04-03 23:55
by VelLes


1

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.

2012-06-07 02:28
by Vinny Osiro