#32
May 16, 2007

Time in Text Field

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.
Download (15.1 MB, 5:22)
alternative download for iPod & Apple TV (8 MB, 5:22)
<!-- tasks/_form.rhtml -->
<%= f.text_field :due_at_string %>
# models/task.rb
def due_at_string
  due_at.to_s(:db)
end

def due_at_string=(due_at_str)
  self.due_at = Time.parse(due_at_str)
rescue ArgumentError
  @due_at_invalid = true
end

def validate
  errors.add(:due_at, "is invalid") if @due_at_invalid
end

RSS Feed for Episode Comments 22 comments

1. chineseGuy May 16, 2007 at 00:13

Sofa!
沙发!


2. weskycn May 16, 2007 at 01:11

不要站在沙发上老是灌水啊^_^。
Another great tutorial Ryan!
great tutorial !!!!
thanks


3. Trueke May 16, 2007 at 03:22

This one is one of the best Railscasts.

Congratulations!


4. Rob May 16, 2007 at 07:04

Thanks again. I love these screencasts because they're short (so I don't fall asleep) and tackle 'real-life' problems.


5. Simon Russell May 16, 2007 at 07:05

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.


6. Ryan Bates May 16, 2007 at 07:27

@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.


7. Bryce May 16, 2007 at 08:21

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.


8. Ryan Bates May 16, 2007 at 09:15

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.


9. Joe P Jun 29, 2007 at 14:32

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?


10. Ryan Bates Jun 29, 2007 at 16:07

@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


11. zzeroo Aug 01, 2007 at 23:17

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?


12. Ryan Bates Aug 02, 2007 at 07:49

@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.


13. August Lilleaas Aug 11, 2007 at 10:31

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/


14. Cupertino Miranda Sep 04, 2007 at 09:36

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.


15. Cupertino Miranda Sep 04, 2007 at 09:51

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


16. Power to The Peaceful May 14, 2008 at 17:13

what does the :db in to_s(:db) do?


17. Lawrence Pit May 16, 2008 at 16:25

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")


18. Lawrence Pit May 16, 2008 at 16:38

So the due_at_string that I would have is:

http://pastie.caboo.se/198554


19. Lawrence Pit May 18, 2008 at 17:37

I've turned this into a rails plugin which also support Chronic. See http://lawrencepit.com/2008/05/19/rails-plugin-validates_as_time/


20. kino May 23, 2008 at 01:52

The reader should be careful to observe that, so regarded, the transcendental unity of apperception can thereby determine in its totality the Transcendental Deduction, but the objects in space and time can never, as a whole, furnish a true and demonstrated science, because, like the pure employment of reason, they exclude the possibility of disjunctive principles.


21. Rick Sep 05, 2008 at 11:04

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.


22. rick Sep 05, 2008 at 11:06

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.

Add your comment:

(SKIP THIS ONE)

(required)

(not shown)


(use pastie or gist for code)

sponsored by:
if you want to help:
required:
Get Quicktime Player