Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails date_select helper and validation

I have a date field in a model backed form in my Rails App:

<%= f.date_select :birthday,
  {:start_year => Time.now.year,
  :end_year => 1900,
  :use_short_month => true,
  :order => [:month, :day, :year],
  :prompt => {:month => 'Month', :day => 'Day', :year => 'Year'}},
  {:class => 'year',
  :id => 'user_birthday'}
%>

It is being validated in the model code using:

validates_presence_of :birthday, :message => 'is a required field'

Unfortunately, if the user enters a partial value such as just the year, the form still submits without an error. Instead a funky date value gets written to the db. How do I make all three fields be mandatory?

I'd like to write a custom validation for this, but I don't know how to properly access the indvidual pieces of the birthday element. How can I do this?

Thanks! Moe

like image 810
Moe Avatar asked Jul 21 '10 00:07

Moe


2 Answers

I think you would have to create the validation in the controller itself.

The date parts are being passed to birthday(1i), birthday(2i) and birthday(3i). The problem here is that they are assigned immediately when passing the attributes and thus before any validations occur.

You could also overwrite the attributes= method to create your own validation there, but I would not suggest you to do that.

Keep in mind that if you do validations, it might be good to validate against any incorrect date as well. (for instance 31st of February, which when passed will yield 2nd of March and not an error).

I think the main issue here is that ActiveRecord is actually replacing the empty values with 1 before creating the Date, which also means that if the visitor pass only the year, the date will be created on the 1st of January that year. I guess that is an expected behaviour to allow use of only one of year/month/day select and still create a useful date.

like image 52
Jimmy Stenke Avatar answered Nov 11 '22 01:11

Jimmy Stenke


Related to this post, this is the best solution I've found. However I should add :day, :month, :year as attr_accessible, thing I don't understand why.. (because of validation? please let me know..)

User.rb

MONTHS = ["January", 1], ["February", 2], ...
DAYS = ["01", 1], ["02", 2], ["03", 3], ...
START_YEAR = Time.now.year - 100
END_YEAR = Time.now.year
YEAR_RANGE = START_YEAR..END_YEAR

attr_accessible :day, :month, :year
attr_accessor :day, :month, :year

before_save :prepare_birthday

validate :validate_birthday

private

def prepare_birthday
  begin
    unless year.blank? # in order to avoid Year like 0000
      self.birthday = Date.new(self.year.to_i, self.month.to_i, self.day.to_i)
    end
      rescue ArgumentError
    false
  end
end

def validate_birthday
  errors.add(:birthday, "Birthday is invalid") unless prepare_birthday
end

user registration form

<%= f.select :month, options_for_select(User::MONTHS), :include_blank => "Month" %>
<%= f.select :day, options_for_select(User::DAYS), :include_blank => "Day" %>
<%= f.select :year, options_for_select(User::YEAR_RANGE), :include_blank =>"Year" %>
like image 1
benoitr Avatar answered Nov 10 '22 23:11

benoitr