Date validation in rails 3

consider scenario,

User model with name, birth_date fields (here birth_date is not
mandatory
field)

inside view form birth_date is assigned as ‘31/31/1985’ which is invalid

ideally user object should be invalid and while save raise an error on
birth_date field but that’s not happening and user object gets saved
with
birth_date as blank which is completely misleading.

After debugging found that while assigning attributes birth_date value
it
gets assigned as blank and as birth_date is optional object gets saved.

class User < ActiveRecord::Base

def initialize(args={})
logger.info args[:birth_date] #invalid value comes upto here but
vanishes afterwords

super

end
end

Any clue how to get validation working properly for not mandatory date
fields ??


sαη∂ιρ Rαηѕιηg


twitter, github @sandipransing

Hi Sandip,

You can provide model level validation for the date as follows.

class User < ActiveRecord::Base

validate :validate_birth_date

def initialize(args={})

super

end

def validate_birth_date
valid_date_format = //
if !birth_date.blank? && !birth_date.match(valid_date_format)
errors.add(:base, “Birth date is invalid”)
end
end

end

You can explore more about errors in
http://guides.rubyonrails.org/active_record_validations_callbacks.html#working_with_validation_errors-errors

Thanks,

Neethu

thanks for reply but its unusable bcz value gets blank before
validation.

example.
u = User.new
u.birth_date = ‘31/31/2011’
u.birth_date #=> nil
u.valid? #=> true

u.birth_date = ‘1/1/2011’
u.birth_date #=> Sat, 01 Jan 2011

On Wed, Feb 1, 2012 at 2:21 PM, Neethu S.
[email protected]wrote:

super


sαη∂ιρ Rαηѕιηg


twitter, github @sandipransing
skype sandip.ransing

On Wed, Feb 1, 2012 at 9:22 AM, sandip ransing
[email protected]wrote:

You are probably tripped up by the same unexpected behavior I recently
discovered.

At least with Postgresql, if you assign a completely invalid string to a
date (or a datetime)
field, it does not throw an exception but simply sets the value to nil:

c.birth_date = “2012-01-15”
=> “2012-01-15”
1.9.3-p0 :011 > c.save!
(0.2ms) BEGIN
(0.4ms) UPDATE “children” SET “birth_date” = ‘2012-01-15’,
“updated_at”
= ‘2012-02-01 08:52:13.063755’ WHERE “children”.“id” = 1
(8.5ms) COMMIT
=> true
1.9.3-p0 :012 > c.birth_date = “2012-01-45”
=> “2012-01-45”
1.9.3-p0 :013 > c.save!
(0.2ms) BEGIN
(0.4ms) UPDATE “children” SET “birth_date” = NULL, “updated_at” =
‘2012-02-01 08:54:46.965404’ WHERE “children”.“id” = 1
(18.8ms) COMMIT
=> true

I would have expected an exception here as the POLS.

But on ‘true’ it does raise an exception (I discovered this
because Rails.logger.warning now returns true).

c.birth_date = true
=> true
1.9.3-p0 :015 > c.save!
(0.2ms) BEGIN
(0.4ms) UPDATE “children” SET “birth_date” = ‘t’, “updated_at” =
‘2012-02-01 08:56:00.771141’ WHERE “children”.“id” = 1
PGError: ERROR: invalid input syntax for type date: “t”
LINE 1: UPDATE “children” SET “birth_date” = ‘t’, “updated_at” = '20…
^
: UPDATE “children” SET “birth_date” = ‘t’, “updated_at” = ‘2012-02-01
08:56:00.771141’ WHERE “children”.“id” = 1
(0.2ms) ROLLBACK
ActiveRecord::StatementInvalid: PGError: ERROR: invalid input syntax
for
type date: “t”

After debugging found that while assigning attributes birth_date value
it

end
end

Any clue how to get validation working properly for not mandatory date fields ??

As for the validation. You could use a regex, as suggested by Neethu,
but
that has the limitation
that it is hard do calculate that 2012-01-29 is OK en 2013-01-29 is not
OK.

Better may be to use a combination of strptime and rescue ArgumentError

1.9.3-p0 :051 > begin
1.9.3-p0 :052 > Date.strptime(“29/02/2012”, “%d/%m/%Y”)
1.9.3-p0 :053?> rescue ArgumentError
1.9.3-p0 :054?> puts “This date is invalid”
1.9.3-p0 :055?> end
=> Wed, 29 Feb 2012

1.9.3-p0 :056 > begin
1.9.3-p0 :057 > Date.strptime(“2012-31-31”, “%Y-%m-%d”)
1.9.3-p0 :058?> rescue ArgumentError
1.9.3-p0 :059?> puts “This date is invalid”
1.9.3-p0 :060?> end
This date is invalid
=> nil #This comes from the last line in the rescue block, which is
puts
here

1.9.3-p0 :061 > begin
1.9.3-p0 :062 > Date.strptime(“10/01/2012”, “%d/%m/%Y”)
1.9.3-p0 :063?> rescue ArgumentError
1.9.3-p0 :064?> puts “This date is invalid”
1.9.3-p0 :065?> end
=> Tue, 10 Jan 2012 # Correct EU dd/mm/yyyy intepretation

1.9.3-p0 :066 > begin
1.9.3-p0 :067 > Date.strptime(“10/01/2012”, “%m/%d/%Y”)
1.9.3-p0 :068?> rescue ArgumentError
1.9.3-p0 :069?> puts “This date is invalid”
1.9.3-p0 :070?> end
=> Mon, 01 Oct 2012 # Correct US mm/dd/yyyy interpretation

HTH,

Peter

Hi Peter,

validating date against Date.strptime(‘DATE STRING’) will surely work.
At controller level, i can able to do validation on date but at model
level
it goes for toss bcz value gets nil before validation(at assignment
level)

Would you please go through my previous reply.

On Wed, Feb 1, 2012 at 2:43 PM, Peter V.
[email protected]wrote:

birth_date field but that’s not happening and user object gets saved with
c.birth_date = “2012-01-15”
(0.2ms) BEGIN
c.birth_date = true
(0.2ms) ROLLBACK
def initialize(args={})

1.9.3-p0 :053?> rescue ArgumentError
=> nil #This comes from the last line in the rescue block, which is puts
1.9.3-p0 :067 > Date.strptime(“10/01/2012”, “%m/%d/%Y”)
You received this message because you are subscribed to the Google G.
“Ruby on Rails: Talk” group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/rubyonrails-talk?hl=en.


sαη∂ιρ Rαηѕιηg


twitter, github @sandipransing
skype sandip.ransing

On Wed, Feb 1, 2012 at 10:24 AM, sandip ransing
[email protected]wrote:

Hi Peter,

validating date against Date.strptime(‘DATE STRING’) will surely work.
At controller level, i can able to do validation on date but at model
level it goes for toss bcz value gets nil before validation(at assignment
level)

Would you please go through my previous reply.

OK, get it. I continue below.

mandatory field)

(8.5ms) COMMIT
I would have expected an exception here as the POLS.
PGError: ERROR: invalid input syntax for type date: “t”

gets assigned as blank and as birth_date is optional object gets saved.

OK.

1.9.3-p0 :062 > Date.strptime(“10/01/2012”, “%d/%m/%Y”)
=> Mon, 01 Oct 2012 # Correct US mm/dd/yyyy interpretation

With setting an error then:

u = User.new
u.birth_date = ‘31/31/2011’
u.birth_date #=> nil
u.valid? #=> true

u.birth_date = ‘1/1/2011’
u.birth_date #=> Sat, 01 Jan 2011

UNTESTED:
ONLY WORKs in Rails 3.2.x

birth_date = is overriden and call the original birth_date = with a
properly parsed Date object

my_parsed_date will set an error if the parsing failed,
so you object is not longer valid.

NOT TESTED, but indicative of a style.

class User

def birth_date=(bd)
super(my_parsed_date(bd, :birth_date))
end

end

this function could go in a library somewhere

def my_parsed_date(bd, attr)
begin
Date.strptime(bd, “%m/%d/%Y”)
rescue ArgumentError
self.errors.add(attr, “date is invalid”)
nil # this will be returned upon rescue
end
end

HTH,

Peter

On 1 February 2012 09:24, sandip ransing [email protected] wrote:

Hi Peter,

validating date against Date.strptime(‘DATE STRING’) will surely work.
At controller level, i can able to do validation on date but at model level
it goes for toss bcz value gets nil before validation(at assignment level)

The best thing is not to let the user enter a simple string for the
date, but give him different fields for day, month and year and then
combine them into a date. Allowing him to enter it as a string will
cause problems as different users will expect different ordering of
the date. I generally use date_select for dates as Rails will
automatically combine the form fields for you.

Colin

On Wed, Feb 1, 2012 at 10:35 AM, Colin L. [email protected]
wrote:

date, but give him different fields for day, month and year and then
combine them into a date. Allowing him to enter it as a string will
cause problems as different users will expect different ordering of
the date. I generally use date_select for dates as Rails will
automatically combine the form fields for you.

I had experience that the typical administrative user, is very fast at
typing
dates on her numeric keypad. It is really a lot easier for her to type a
requested
delivery_date as 300412 (that is delivery on 30 April 2012) even if we
as
developers frown upon it. Maybe a series of 3 boxes as you suggest
may also be a good compromise.

The protection against the problem of misinterpretation, is then to show
the
fully expanded date (preferably with a local fast javascript), to verify
that the
computer interpreted the data correctly. And use localisation, where
users
in
EU get dd/mm/yyyy as default setting and users in US get mm/dd/yyyy as
default input setting.

HTH,

Peter

In Ruby 1.9.2, Date.parse is now default to parse dd/mm/yyyy (European
format) instead of mm/dd/yyyy. So the format in mm/dd/yyyy is an invalid
date. home_run gem will help you to overcome this problem, but comes
with lot of incompatibilities. In an application I’m using I had to
revert this gem because it was causing trouble while running tests. Then
I used Date.strptime for this.

@peter make sense!
let me try with overridden birth_date= writer method in rails 3

On Wed, Feb 1, 2012 at 3:28 PM, Peter V.
[email protected]wrote:

dates on her numeric keypad. It is really a lot easier for her to type a
in
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/rubyonrails-talk?hl=en.


sαη∂ιρ Rαηѕιηg


twitter, github @sandipransing
skype sandip.ransing

On Wed, Feb 1, 2012 at 11:53 AM, Colin L. [email protected]
wrote:

I agree that for what one might call an ‘internal’ app where users
know what is expected that a single box may be ok.

Indeed ! That was an internal application. On the external side, we had
a Datepicker (slower to use, but no ambiguity).

In the case of
three boxes then provided tab can be used between them this can still
be quick to type.

Or even jump to next box automatically (my banking app does that for the
account number that has 3 blocks in Belgium). But the inconvenience is
that I cannot copy paste a full account number to it then :-/

The most safe way is possibly to provide a drop down select box for
the month then there is no confusion, but it is slower to enter. I
think date_select will provide this.
Of course there is no “correct” answer to this, different applications
require different solutions.

+1

Peter

On 1 February 2012 09:58, Peter V. [email protected]
wrote:

dates on her numeric keypad. It is really a lot easier for her to type a
requested
delivery_date as 300412 (that is delivery on 30 April 2012) even if we as
developers frown upon it. Maybe a series of 3 boxes as you suggest
may also be a good compromise.

I agree that for what one might call an ‘internal’ app where users
know what is expected that a single box may be ok. In the case of
three boxes then provided tab can be used between them this can still
be quick to type.

The protection against the problem of misinterpretation, is then to show the
fully expanded date (preferably with a local fast javascript), to verify
that the
computer interpreted the data correctly. And use localisation, where users
in
EU get dd/mm/yyyy as default setting and users in US get mm/dd/yyyy as
default input setting.

The most safe way is possibly to provide a drop down select box for
the month then there is no confusion, but it is slower to enter. I
think date_select will provide this.
Of course there is no “correct” answer to this, different applications
require different solutions.

Colin

This would be my solution:

lib/formatted_date_time.rb:

class ActiveRecord::Base

def self.formatted_date_accessor(*names)
names.each do |name|
define_method("#{name}=") do |value|
super(value)
if value.present? && self[name].nil?
class_eval do
define_method name.to_sym do
value
end
end
self.class.validate do
errors.add(name.to_sym, “can’t be formatted”)
end
end
end
end
end

end

class User < ActiveRecord::Base
formatted_date_accessor :birth_date

end

If you passing invalid date, it raises an validation error, but returns
the invalid date to a form at_request.