1

I am wanting to format data entered into a string column (confusingly named time) of my database in a certain way. Because of legacy data issues on this project I can't simply change the column type from string to time because I would lose the old entries.

I simply want to require that the format of the information entered into the form and then submitted to the database is as follows:

00:00 where a 0 can by any integer 0-9.

How would I do that within a form_for.

I am using Rails 2.3.8.

bgadoci
  • 6,363
  • 17
  • 64
  • 91
  • You can change the field type, but it takes multiple steps. Whether it's worth it is the next question. – the Tin Man Dec 14 '10 at 03:32
  • Yea...I don't really want to change the field type. Just looking for a way to format data going in. – bgadoci Dec 14 '10 at 03:33
  • If I was to change the field type...could the existing data be mapped? – bgadoci Dec 14 '10 at 04:29
  • Or, what about just validating such that the field contains numbers in the format of 00:00. Can that be done through a validation in the model? – bgadoci Dec 14 '10 at 04:43
  • If they are valid dates they should be mappable. You could write a bit of code to walk through the table, reading the time values and trying to parse and validate the ranges as a pre-flight test. – the Tin Man Dec 14 '10 at 04:54

3 Answers3

1

A work around that might help:

First create a migration doing:

rename_column :the_models, :time, :old_time
add_column :the_models, :time, :time

So now, the model will have a field called old_time containing the original time data, and a new time field having the correct field type.

As a result, the existing forms would seems like lost all the time data, but at least they are calling the_model.time to get the result.

The real work around part:

class TheModel < ActiveRecord::Base
  def time
    read_attribute(:time) || Time.parse(read_attribute(:old_time))
  end
end

So now the time value will be saved into the time filed.

When time is nil, it will try to get the time value from the old_time and convert it to a time object.

There are two drawbacks:

  1. Time.parse("12:34") would give you something like: 2010-12-14 12:34:00 +0800 Because you gave it only the hr and min, other fields are not reliable.
  2. You have a nearly deprecated field called old_time in the database. So in the future, you may want to completely remove that field, after you have confidence that all old_time values has been converted to the time field.

One more note:

You should convert existing forms, for :time field which probably using text_field. The text_field may become showing something like 2010-12-14 12:34:00 +0800.

If you really still want to use text_field, you could:

def time
  t = read_attribute(:time)
  t.nil? ? read_attribute(:old_time) : t.strftime("%H:%M")
end

def time=(val)
  write_attribute(:time, Time.parse(val))
end
PeterWong
  • 15,951
  • 9
  • 59
  • 68
0

Your regex would be /\d\d:\d\d/, but I'm not sure how to use regular expressions in Ruby.

Christian Mann
  • 8,030
  • 5
  • 41
  • 51
0

Your regex would be:

/[0-9]{2}:[0-9]{2}/

or

/\d\d:\d\d/

Here are some tests:

'00:00'[/[0-9]{2}:[0-9]{2}/] # => "00:00"
'12:00'[/\d\d:\d\d/]         # => "12:00"
'11:59'[/[0-9]{2}:[0-9]{2}/] # => "11:59"
'23:59'[/\d\d:\d\d/]         # => "23:59"

':'[/[0-9]{2}:[0-9]{2}/] # => nil
':0'[/[0-9]{2}:[0-9]{2}/] # => nil
'0:0'[/[0-9]{2}:[0-9]{2}/] # => nil
'000:0'[/[0-9]{2}:[0-9]{2}/] # => nil
'0:000'[/[0-9]{2}:[0-9]{2}/] # => nil

Up to this point I was working with your specified solution, but here's where it breaks down:

# BUG?
'1:00'[/[0-9]{2}:[0-9]{2}/] # => nil

# Fix?
'1:00'[/[0-9]{1,2}:[0-9]{2}/] # => "1:00"

# *** BUG ***
'99:99'[/[0-9]{2}:[0-9]{2}/] # => "99:99"

Note that '99:99' is accepted.

Trying to use a regex to check the range is not really very workable when dealing with something like a time value. It can be done, but the regex becomes ugly.

Here's the start of a solution. Finishing the tests for appropriate range is left as an exercise for the reader:

timeval = '00:00'[/[0-9]{2}:[0-9]{2}/]
(timeval.split(':').map(&:to_i) <=> [11,59]) <= 0 # => true

timeval = '1:00'[/[0-9]{1,2}:[0-9]{2}/]
(timeval.split(':').map(&:to_i) <=> [11,59]) <= 0 # => true

timeval = '1:00'[/[0-9]{1,2}:[0-9]{2}/]
(timeval.split(':').map(&:to_i) <=> [11,59]) <= 0 # => true

timeval = '0:99'[/[0-9]{1,2}:[0-9]{2}/]
(timeval.split(':').map(&:to_i) <=> [11,59]) <= 0 # => true

timeval = '99:99'[/[0-9]{1,2}:[0-9]{2}/]
(timeval.split(':').map(&:to_i) <=> [11,59]) <= 0 # => false

Alternately, you might want to look into using some <select> popups for the values, or a jQuery plugin that is designed for time input, or something like validates_timeliness or validates_date_time. I also found time_select with 12 hour time and Time Zone in Ruby on Rails. You can search Stack Overflow too.

Community
  • 1
  • 1
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
  • Ok, so how would something like this be implemented? Would there be an addition to the Run model? Could you give me some specifics about what would go where (model, view). I am about 1.5 years into rails and so both reg expressions and testing aren't things I have mastered yet. – bgadoci Dec 14 '10 at 04:30