How do you test dates in ruby on rails 3 with rspec? - ruby-on-rails

Say for instance I have a datefield or 3 select fields for day month and yea..
How does the final selection of date get sent to the database? or in what format doesn't it get sent?
20110803 ?
2011-08-03 ?
What I want to do is:
Test for a valid date (a date that has been selected that exists)
Test for an invalid date (invalid would be a date that doesn't exist and also be when nothing has been selected)
I know an idea of how to write these tests but not sure what actually gets sent to the database
What actually appears before save and in what format. String? Integer?
Thanks in advance for responses.

The gets sent to the database as the database expects it. Each database and configuration will yield a different format for a date but this will not make any difference to you as the field is configured to be Date and it will also be a Date on Rails, so you should not care about the format.
While testing, there are many ways you can validate, but you will not be using neither integer or string, you're using Date objects. Here's an example of how you could write the first one:
it 'should find a model today' do
#date = Date.today
#model = Model.create!(:date => #date)
Model.first( :conditions => {:date => #date}).should == #model
end

Related

Rails convert datetimepicker date string to datetime data type

I am building a To Do List application with Ruby on Rails, and each item in my Task resource has a deadline attribute that is created with the gem 'datetimepicker'.
This 'datetimepicker' gem pulls in dates as a string data type with the format "12/13/2012", "%m/%d/%Y".
I am trying to group task elements by the day value in their deadline attribute. I've found one article on how to do this (although I think it contains one typo):
https://ariejan.net/2007/01/12/rails-group-results-by-week-using-group_by/
However, I'd rather do the work in the tasks_controller than the task model (maybe the model is the cleaner way to do it? I am a nuby and open to suggestions on this).
I've found another article on the group_by method that demonstrates an approximate process:
http://railscasts.com/episodes/29-group-by-month?view=asciicast
This article looks a little cleaner to me. It requires me to use the .beginng_of_day method to treat all task items with the same day value in their deadline equally.
I'm running into an issue with both methods, which is that they treat deadline like a datetime data type. I want to use the .to_datetime method to convert task.deadline, but the 'datetimepicker' gem formats the deadline string in precisely the wrong way for .to_datetime to convert it:
"12/13/2012".to_datetime # => ArgumentError: invalid date
see => apidock[dot]com[slash]rails[slash]String[slash]to_datetime
I've found a similar article that recommends re-formatting the deadline attribute with the .strptime and .strftime methods, but I don't see how to do this when deadline is a string and not a datetime:
see => stackoverflow[dot]com[slash]questions[slash]16296885[slash]how-to-convert-datetime-picker-value-to-datetime-in-ruby-on-rails
Can someone help me make this conversion, or suggest a different (preferably simpler!) strategy for grouping tasks by day?
Thanks for your help, and apologies for the broken links; I don't have sufficient reputation points to embed more than 2 yet.
Don't you actually want a date? not a datetime?
In any case you can use
Date.strptime("12/13/2012", "%m/%d/%Y")
=> #<Date: 2012-12-13 ((2456275j,0s,0n),+0s,2299161j)>
DateTime.strptime("12/13/2012", "%m/%d/%Y")
=> #<DateTime: 2012-12-13T00:00:00+00:00 ((2456275j,0s,0n),+0s,2299161j)>

Ruby check if datetime is a iso8601 and saving

I need to check if a DateTime is in a valid ISO8601 format.
Like: #iso8601?
I checked if ruby has a specific method but I haven't found any.
Currently I'm using date.iso8601 == date to check this.
Is there a good way to do this?
EDIT
Explaining my environment, and changing the scope of the question.
So, my project will use the js api FullCalendar, that's why i need a iso8601 string format. And I wondered what it's better or the correct way, save the date in the database in the correct format, or let the ActiveRecord do their job and manipulate it on just when I require the time information.
I dont' quite understand your question. I am assuming that you want to check a DateTime string if it's a valid ISO8601 format date string or not. Correct me if I am wrong.
You can use the class methods Time.iso8601 and Date.iso8601. In order to use them, you need to require the 'date' and 'time' library from standard library. One caveat is, as you can see from the name, they are not predicate method (without ?), instead they raise an ArgumentError with "invalid date" message if the wrong string is being input. So, you need to add a begin rescue block. eg.
require 'time'
t = Time.now
time_string = t.rfc2822 # intentionally set to wrong format string
begin
Time.iso8601(time_string)
puts "Yeah, correct string"
rescue ArgumentError => e
puts e
puts "Nah, wrong string"
end
This is more verbose than your "date.iso8601 == date". But I am posting it because don't understand how your method works. To me date.iso8601 == date would always be false. Am I wrong?
UPDATE
As an answer for your updated question, it's best you can just store the DateTime normally in the database and call iso8601 method to get the ISO8601 string. For creating DateTime, you can just use Time.iso8601 to parse the input string into DateTime object.
Ruby 3.1:
begin
invalid_date = '1970-01-32'
Date.iso8601(invalid_date)
rescue Date::Error
puts 'The date is invalid'
end
Rails 6.1:
Given the attribute has the type of date in the underlying database, you will not be able to set it to the invalid value. The following test (written in RSpec) passes:
specify 'Rails ignores invalid date' do
invalid_date = '1970-01-32'
invoice = Invoice.new
invoice.date = invalid_date
expect(invoice.date).to be nil
end
P.S. It's not fully clear whether you needed to make it work with Rails or without so there are solutions for both cases.

Convert time zone of datetime returned by f.datetime_select

When I use f.datetime_select in a rails form, it returns a datetime to my controller in UTC, e.g.
2014-06-18T11:00:00+00:00
My local time zone is set to Melbourne (+10) as specified in my application.rb file:
config.time_zone = 'Melbourne'
So when I retrieve datatimes from my database they are automatically converted to Melbourne (+10) timezone, e.g.
2014-06-17 19:00:00 +1000
I want to compare the datetime returned by f.datetime_select with a field in my database. How can I do this?
i.e. how can i change the time zone of the datetime returned by f.datetime select to 'Melbourne' (+10) without changing the actual time? i.e. convert:
2014-06-18T11:00:00+00:00
to
2014-06-18T11:00:00+10:00
All dates stored in database are in UTC time. So when your app get new date from 'params' you basically have 2 options: to save it to the ActiveRecord model, and during that AR will perform all heavylifting of deciding of what timezone was meant.
If you don't want to save data to the model, you have to deal with it yourself. Date select control return just 3 specially formatted strings in params hash.
Let's say I named field in my form 'birthdate'. Controller will get something like:
"<your_model_name>" => {... , "birthdate(3i)" => "<day>", "birthdate(2i)" => "<month>", "birthdate(1i)" => "<year>", ...}
So you could deal with that info something like:
Time.zone.parse("#{ params[:model]['birthdate(3i)'] }-#{ params[:model]['birthdate(2i)'] }-#{ params[:model]['birthdate(1i)'] }")
And yeah I know that it looks ugly, and after some research I surprised that there is no any 'out of the box' solution )

Why does Ruby parse 'foo' into 01.01.1970 within a Date type

I do have a model within a Rails application with the following definition (I am using Mongoid):
field :date, type: Date
I included the field within the view as a text_field (f.text_field). Everything works fine, if I do enter a valid date. But when I enter some text in this field (e.g. 'foo'), then the date gets parsed as 01.01.1970. I am not parsing the date manually, rather I use the following code:
#offer = Offer.new(offer_params)
I also checked on the Ruby console:
>> offer = Offer.new
...
>> offer.date = 'foo'
"foo"
>> offer.date
Thu, 01 Jan 1970
>> offer.date.is_a? Date
true
How can I prevent this behavior so that the record can't be saved and a validation error gets shown to the user?
Thanks for your help!
You can modify the Offer initializer so that it will use the Date::parse method for parsing your date. This will throw an ArgumentError, when the parameter you supply to it is not a valid date format.
You may have to convert that to seconds since 1.1.1970 as that seems to be the format used for storing your dates in your database. Have a look at Date#strftime for that.

Evaluate whether date is not one of past dates in model -- Ruby on Rails

I have a Day model which has a date column. I have to implement the validation that the date column must not have a past date. If the date is a past date it must not get saved to the database and give the appropriate error message right on the. I know I can put this validation in the controller, but I think it violates the MVC rules i.e keeping the business logic away from the controllers.
Is there any way to put this validation in the model or anywhere else and if the date is a past date then it must redirect back to the new action with the message "Date cannot be a past date"
Please Help
Thanks in advance
In your model you need add a validation
validate :not_past_date
def not_past_date
if self.date < Date.today
errors.add(:date, 'not in past')
end
end
After in your controller, you just check if save return true or false. False is send when you don't validate your model. If false redirect to another controller.
Edit :
Like said by Simone Carletti in comment you can use #past?
validate :not_past_date
def not_past_date
if self.date.past?
errors.add(:date, 'not in past')
end
end
I know I can put this validation in
the controller, but I think it
violates the MVC rules i.e keeping the
business logic away from the
controllers.
Eeh, in Rails you should put validations in the model (not in the controller!). See:
http://guides.rubyonrails.org/active_record_validations_callbacks.html.
#Simone and #shingara using the #past? method will compare a date with the current DateTime in UTC. So you may face a problem in two occasions here:
If you want to get all elements with a DateTime set to a day, wether they began at the beginning of the day or not;
If you are working on another TimeZone.
There are workarounds to deal with it, but it's just more straightforward to do the first #shingara way: self.date < Date.today

Resources