且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

在rails模型中验证日期和时间字段

更新时间:2023-12-01 13:12:04

这是最后指出我正确的方向: https://github.com/codegram/date_validator/issues/25#issuecomment-6126879


EDIT: In case the question below looks a bit 'extensive', the summary is that I just want to combine a date field and an optional time field in my model purely for the purpose of validating it using a date validator but I can't get my test to fail correctly when I pass in a string as the time.


EDIT 2: Since I'm still struggling to find a way to join a date object with a time object for the purpose of validation then I wondered if I could just validate that the time value was a Time object using is_a?(Time) (rather than an invalid string) but that didn't seem to work either and I wondered if it was because of the Time mention with this method (but I don't quite understand it enough to know whether that causes an issue or not.

I also wondered whether the to_time method could help me check whether something is a Time object but I'm not sure how.

Hopefully someone can either advise on how to join a Date object with a Time object for the purpose of validation or at least tell me how I can check that the time value being submitted is a Time object and not an invalid string or something.


EDIT 3: I've just tried the following based on another answer I found but it still doesn't complain about the "invalid" string in the test:

validate :start_time_is_time?, :if => "start_time.present?"

def start_time_is_time?
  unless start_time.is_a?(Time) || ((Time.parse(start_time) rescue ArgumentError) == ArgumentError)
    errors.add(:start_time, 'must be a valid time') 
  end
end

Is there any reason why the string may be considered as a valid Time?


I have a view similar to Trying to set a variable in before_validation but it isn't working with a view that uses a calendar to select a day and drop downs to select time. I tried to apply the same to my model but couldn't get it to work since I'm a little new to ruby and was having trouble filling in the blanks left by the answer.

My model has start_date and start_time fields.

I need to combine these 2 fields just so that I can validate them together as a datetime (using the date_validator gem) rather than having to validate the date and time separately, although they will go into the database as separate fields, unless someone can persuade me otherwise (although I've done a lot with them separately so don't really want to change this).

A date has to be submitted but the time field is optional.

From the other question it looks like I need a before_validation method that can combine the 2 fields as required into a virtual field that I can then validate using the date_validator gem.

EDIT: original error has been corrected thanks to RyanJM but I still can't get the test to pass correctly. Below is the current state of play.

Here's the basics of my Showtime model now:

class Showtime < ActiveRecord::Base

  attr_accessible   :start_date, :start_time

  attr_accessor   :starting_at, :starting_time

  belongs_to :performance

  before_validation :construct_starting_at

  validates :start_date,  :presence => true,  :date => { :after_or_equal_to => Proc.new { Date.today } }
  validates :starting_at, :date => { :after_or_equal_to => Proc.new { Time.now } }, :if => "start_date.present? && start_time.present?"

  def construct_starting_at
    if start_date.present? && start_time.present?
      self.starting_time = self.start_time.strftime("%T")
      self.starting_at = DateTime.strptime("#{start_date} #{starting_time}", "%F %T")
    end
  end

end

In case it is required, here's the basics/relevant parts of the Performance model:

class Performance < ActiveRecord::Base

  has_many :showtimes, :dependent => :delete_all
  accepts_nested_attributes_for :showtimes, :allow_destroy => true, :reject_if => lambda { |a| a[:start_date].blank? }

end

Here's the failing test:

require 'spec_helper'

describe Showtime do

  before(:each) do
    @attr = {
        :name => "Performance 1",
        :showtimes_attributes => {
              "0" => {
                  :start_date => Date.today+15.days,
                  :start_time => Time.now
              }
        }
    }
  end

  it "should test that the start time is a valid time if it exists" do
    @attr_invalid = {
        "0" => {
            :start_date => Date.today+15.days,
            :start_time => "invalid"
        }
    }
    invalid = Performance.create(@attr.merge(:showtimes_attributes => @attr_invalid))
    invalid.should_not be_valid
  end

end

Hopefully I've not broken anything in the above code in my attempt to just show the necessary parts.

Here's what finally pointed me in the right direction: https://github.com/codegram/date_validator/issues/25#issuecomment-6126879