Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails invalid datetime on model results in nil

I have a model with a datetime attribute. I am trying to validate incoming JSON which would update the model. But ActiveRecord seems to be setting the value of the attribute to nil if it is an invalid datetime. I can't respond with the appropriate error because the attribute is allowed to be nil. What am I doing wrong?

Code:

class DatetimeValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    value.to_datetime rescue record.errors[attribute] << (options[:message] || "must be a date.")
  end
end

class Foo
   # column :my_date, :datetime
   validates :my_date, :allow_nil => true, :datetime => true
end

Console:

1.9.3p125 :001 > x = Foo.new
=> #<Foo id: nil, my_date: nil>
1.9.3p125 :001 > x.my_date = 'bad value'
=> 'bad value'
1.9.3p125 :001 > x.my_date
=> nil
1.9.3p125 :001 > x.valid?
=> true

As far as ActiveRecord is concerned, setting the datetime attribute to 'bad_value' is equivalent to setting it to nil, so I can't validate it, since my_date is not required. This seems like a bug to me. What is the best workaround?

Thanks!

like image 254
James Andrew Wheeler Avatar asked Oct 23 '12 18:10

James Andrew Wheeler


1 Answers

One workaround would be to move the allow_nil part into the date_time validation:

class DatetimeValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    return if value.blank?
    value.to_datetime rescue record.errors[attribute] << (options[:message] || "must be a date.")
  end
end

class Foo
  validates :my_date, :datetime => true
end

Edit: I think it's an issue with typecasting - try this (adapted from the code for validates :numericality):

def validate_each(record, attribute, value)
  before_type_cast = "#{attribute}_before_type_cast"

  raw_value = record.send(before_type_cast) if record.respond_to?(before_type_cast.to_sym)
  raw_value ||= value

  return if raw_value.blank?
  raw_value.to_datetime rescue record.errors[attribute] << (options[:message] || "must be a date.")
end
like image 73
Alex Peattie Avatar answered Oct 28 '22 01:10

Alex Peattie