#32 Time in Text Field
May 16, 2007 | 5 minutes | Active Record, Forms
Although Rails does allow you to edit time attributes with text fields, it's not very flexible. In this episode you will learn how to use a virtual attribute to format the time to your liking.
This one is one of the best Railscasts.
Congratulations!
Thanks again. I love these screencasts because they're short (so I don't fall asleep) and tackle 'real-life' problems.
Thanks for these RailsCasts, they're very well done. Just a couple of points/questions on this one:
- should you be redefining the "validate" method, or just calling it? It was my understanding that if you redefine it, that basically cuts out all other validation.
- you can actually get the same effect just by using a text field (without doing the virtual attributes) if you just want the basic parsing behaviour.
@Simon, good comments!
Overriding "validate" still allows all other validations to get through, think of it as just a place to define your own custom validations. The main thing you need to watch out for is if you are subclassing another model (STI) and overriding it there.
True about using the text field without the virtual attribute pretty much works. I realized this after I recorded the episode, otherwise I would have mentioned it. But the technique is still useful if you want to customize the behavior (add Chronic for example).
Also, I should have added a "nil?" check on the getter method so it doesn't cause any errors while creating a new task.
Another thing I should probably have done is to store the entered string in an instance variable so, if there is a validation error, the time doesn't revert back to the original one.
YAGR (Yet another great Railscast) Ryan. Thanks a lot. It's improved my coding quite a bit.
Is there another episode where you cover getter and setter methods? I'm a little confused on why you would use them in the first place and how rails knows how to access them. I've got most of the Rails books so if anyone knows references from those, that could help too.
Virtual attributes are also covered on episode 16:
http://railscasts.com/episodes/16
It just so happens that Rails uses getter and setter methods for setting and retreiving attributes (columns) so you can create your own attributes by making your own getter and setter methods.
Ryan, When entering an invalid date and the form is redisplayed with the error, the invalid date string is lost and replaced with the old date value. What would be the best way to keep the text value that the user entered so they can more easily see and correct their mistake?
@Joe, good question. If you want the user submitted date to stick around for an invalid entry you should store it in an instance variable in the setter method. Then read from that instance variable in the getter method if it's set. You can see the code here:
http://pastie.caboo.se/74917
Hi@all,
I think theres an error in the code snippets above.
<!-- tasks/_form.rhtml -->
<%= f.text_field :due_at %>
must bee
<!-- tasks/_form.rhtml -->
<%= f.text_field :due_at_string %>
or isn't it.
Secondly ever time when I use
# models/task.rb
def due_at_string
due_at.to_s(:db)
end
I run in an ugly :
ArgumentError in Overtimes#new
wrong number of arguments (1 for 0)
with...
# models/task.rb
def due_at_string
due_at
end
... in my Models this happens not. Can Somebody explain me that?
@zzeroo, thanks, fixed the error. As for your problem, I'm not sure what it could be. Is the due_at column a datetime type? Is it considered a Time object by Ruby? Try playing with it in script/console and repeating the error.
I wrote a short plugin for something kind of similar. It's parses date strings, such as "2208", "22.08/2009", "15-07" and so on.
It's pretty rough, and it does some silly things. For instance, it doesn't use Active Records validation system, it just raises stuff when things go bad.
Anyway, it sorta works =P
http://lilleaas.net/svn/plugins/date_parser/
Ryan, thanks for this great "casts" and all the effort spent in teaching us cool tricks from Rails.
I am having the same kind of problem as zzeroo. As I am still new to Rails and only develop on it in my part-times, I would wonder if the problem (at least in my case) is because I am using class Date instead of DateTime. Please, tell me what you think.
Solved my problem, as it seems method to_s from Date does not accept any parameters and for this reason it was giving me this strange problem. Now I wonder how to change date format. Something to be solved with a quick look at the library reference. Thanks again
what does the :db in to_s(:db) do?
Rails has extended the Time class so you can do to_s(:db). See also http://api.rubyonrails.com/classes/ActiveSupport/CoreExtensions/Time/Conversions.html
Instead of using :db which also includes seconds (which seems a bit too much) you could also try :long. Or make up your own string like to_s("%Y-%m-%d %H:%M")
So the due_at_string that I would have is:
http://pastie.caboo.se/198554
I've turned this into a rails plugin which also support Chronic. See http://lawrencepit.com/2008/05/19/rails-plugin-validates_as_time/
Can someone post their working validation example? Actually, I have it all working, EXCEPT for the extra parent level messages of "Tasks is invalid" (in my case not Tasks, but you get the idea.)
I know some others earlier in this thread have mentioned the same thing, but I'm looking for what people have done about it. Does someone have a snippet of code that shows the best way to handle this? I would think it would just involve iterating over the errors and removing the messages I don't want? I'd like to see the best way to handle this, though.
Oooops! I'm embarrassed.
I had two different railscasts tabs open and posted my previous comment in the wrong one (it was meant for http://railscasts.com/episodes/75-complex-forms-part-3). Please delete it if possible. Once again, sorry.
If anybody gets "wrong number of arguments 1 for 0" like zzeroo's case, that should only happen when you try to create new object and that is because Rails is trying to parse nil.to_s(:db) and raises that error. As Rayn mentioned if you add nil? check on the getter method it should wotk fine!
Cheers
"As Rayn mentioned if you add nil? check on the getter method it should wotk fine!"
No. The code is outdated for use today. There are more efficient ways to do this now.
Hello!, I have a question: Why you create a new method called "validate" instead put the "Error.add" code directly into rescue block?
Thanks a lot for this excellent podcasts.
In this episode you will learn how to use a virtual attribute to format the time to your liking.
Although Rails does allow you to edit time attributes with text fields, it's not very flexible.
Why did you have to use self.due_at in the setter method, but not in the getter method?
This episode has been updated for Rails 5 as a blog post. Time in Text Field in Rails 5