3

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.

Community
  • 1
  • 1
user1116573
  • 2,817
  • 4
  • 17
  • 27
  • How it the start_time actually defined in the database? Is start_date defined as a datetime in your database? – RadBrad Mar 31 '12 at 23:06
  • `start_date` is defined as a `date` and `start_time` is defined as a `time`. I kept them as separate fields so that `start_time` could be optional. – user1116573 Apr 03 '12 at 12:41
  • If start_time is optional then what check for it when you are about to set starting_at? You'll probably need two cases right? One for when it is there and one when it isn't? In my case, starting_at was the object I was saving to the db. How are you declaring the variable? – RyanJM Apr 05 '12 at 21:03
  • In my view I use `f.text_field :start_date` and `f.time_select :start_time`. I am then, currently, doing all my validation independent of each other but it looks laboured and would make sense if the 2 were patched together for the sake of validation to make more sense, also because I was struggling to find a way to check that `start_time` was a time whilst still allowing nil. Yes, I will need 2 cases, `if start_date.present? && start_time.present? xxxxxxxx elsif start_date.present? && start_time.nil? xxxxxxxxx end`. @RyanJM It would be good to see what the bits of your model & view look like. – user1116573 Apr 06 '12 at 09:21
  • Do you have `attr_accessor`s setup properly? Open up the console and verify your construct_stating_at works properly. https://gist.github.com/2320468 – RyanJM Apr 06 '12 at 14:44
  • Hi @RyanJM. Thanks for the gist. I've updated my model based on what I've learnt from it and I no longer have an error. I've added a comment to the gist showing the basics of what my model now looks like. I still have a failing test though and I'll add that to the gist comment as well to give a better idea of what's failing. – user1116573 Apr 07 '12 at 13:34
  • I've updated the question with the code from my gist comment so others can see it easier. – user1116573 Apr 09 '12 at 10:56

2 Answers2

0

I'm haven't used the date_validator gem, but validates_timeliness does this sort of thing pretty well and also seems to be more actively maintained.

Thilo
  • 17,565
  • 5
  • 68
  • 84
  • Thanks for the reply Thilo. I tried using that gem before moving to date_validator gem. I need to allow blank and there seems to be a bug with validates_timeliness allowing blank with fields that it is applying validation to. The date_validator gem also seemed to be a lot simpler. Date_validator doesn't seem to validate time so I'm trying to merge my date with my time to get a datetime that I can validate. – user1116573 Apr 14 '12 at 15:35
0

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

user1116573
  • 2,817
  • 4
  • 17
  • 27