codegram / date_validator

A simple, ORM agnostic, Ruby >=2.2 compatible date validator for Rails, based on ActiveModel.
http://thoughts.codegram.com/date-validation-with-rails-3
MIT License
496 stars 82 forks source link

add :allow_blank? option #25

Closed enrico closed 11 years ago

enrico commented 12 years ago

I'd like to use it like this:

validates :date_of_birth, :date => {:after => ..., :before => ..., :message => 'is invalid' }, :allow_blank? => true , :if => :check_date?

nickpoorman commented 12 years ago

+1

amnesia7 commented 12 years ago

I have something similar and the following code seems to work for me:

  validates_each( :date_field ) do |record, attribute|
    raw_value = record.send("#{attribute}_before_type_cast".to_sym) || record.read_attribute_for_validation(attribute)
    # Rails converts invalid datetimes to nil and if date_field is an optional field (allow_blank) it
    # does not get validated and so rails saves the empty value rather than throw an error. 
    # Therefore, we need to validate against the raw (_before_type_cast) version which does
    # not get set to nil
    unless raw_value.nil?
      Time.zone.parse(raw_value) rescue record.errors.add(attribute, :not_a_date)
    end
  end

It would be good if @codegram could introduce this behaviour into date_validator itself so that it doesn't need to be called separately.

This code corrects my issue where I had a date_field. Rails converts invalid dates to nil and then because I was allowing blank it submitted the form rather than show an error message to the user.

Hope that helps.

If this behaviour is already in date_validator I'd love to know how to call it, if I've missed something.

lfv89 commented 12 years ago

+1

txus commented 12 years ago

@amnesia7 do you want to give a try at implementing the allow_blank option?

amnesia7 commented 11 years ago

I've not done the whole fork-change-pull before but I'll give it a go as soon as I can. If there's any guidance notes anywhere on how best/easiest to go about it then feel free to post a link so I know.

Thanks

amnesia7 commented 11 years ago

It was a bit difficult to get to stage 1 to begin making any changes - I think your gemfile has some gems missing in it that it requires (other gems seem to have the gems listed in the gemfile that the gem requires to run eg rails, minitest.

I got to the point of running your tests using rake test and they all passed so I started to add my tests to the test/date_validator.rb file and the new code to the lib/datevalidator.rb file.

My tests fail correctly but the code I've added throws up the following error: NoMethodError: undefined methodexpiration_date_before_type_cast'`.

Unless you can tell me any different then I assume I need something like def method_missing : http://made-of-stone.blogspot.co.uk/2007/01/stupid-mistakes-and-helpful-hints.html. I don't really know where to put this in your code if it is required.

@codegram, @txus : can you offer any help/advice?

amnesia7 commented 11 years ago

After looking at the issue again I think the method missing idea was a pointless idea because I was the one setting _before_type_cast anyway so I thought it best to remove that.

Here's what I changed in my /lib/active_model/validations/date_validator.rb (https://github.com/codegram/date_validator/blob/master/lib/active_model/validations/date_validator.rb#L38):

def validate_each(record, attr_name, value)
  return if (options[:allow_nil] && value.nil?) || (options[:allow_blank] && value.blank?)
  unless value.nil?
    begin
      Time.zone.parse(value)
    rescue
      record.errors.add(attr_name, :not_a_date)
      return
    end
  end

  unless value
    record.errors.add(attr_name, :not_a_date, options)
    return
  end

Here's the tests that I added to my /test/date_validator_test.rb:

describe "allow_blank set to true" do
  it "complains when an invalid date is provided" do
    I18n.backend.reload!
    TestRecord.validates :expiration_date,
                                :allow_blank => true,
            :date => {:before => Time.now}

    model = TestRecord.new("invalid")
    model.valid?.must_equal false
    model.errors[:expiration_date].must_equal(["is not a date"])
  end
  it "complains when a bogus date is provided" do
    I18n.backend.reload!
    TestRecord.validates :expiration_date,
                               :allow_blank => true,
           :date => {:before => Time.now}

    model = TestRecord.new("2012-02-99")
    model.valid?.must_equal false
    model.errors[:expiration_date].must_equal(["is not a date"])
  end
  it "is happy when a blank date is provided" do
    I18n.backend.reload!
    TestRecord.validates :expiration_date,
                              :allow_blank => true,
          :date => {:before => Time.now}

    model = TestRecord.new("  ")
    model.valid?.must_equal true
  end
  it "is happy when no date is provided" do
    I18n.backend.reload!
    TestRecord.validates :expiration_date,
                              :allow_blank => true,
          :date => {:before => Time.now}

    model = TestRecord.new("")
    model.valid?.must_equal true
  end
end

I think I'm going to leave it for someone else to takeover, I think I'm a bit out of my depth - I'm still learning rails yet and although I'm getting there with my own app development, I don't think I'm good enough to figure out gem changes yet.

Hope that helps, rather than hinders.

oriolgual commented 11 years ago

Closed by #37