views:

216

answers:

2

I'm writing an Ruby on Rails app using a legacy database. The problem I'm having is I have a unix timestamp column in the database. I'm trying to make it so that I can write a string like "2010-10-10" and get a Date when I read it.

def birthdate
  bdate = self[:birthdate]

  if bdate == 0
    ''
  else
    if bdate =~ $CetConfig.regexp.date_format.regexp
      bdate
    else
      Date.parse(Time.at(bdate).to_s)
    end
  end
end

def birthdate=(bdate)
  self[:birthdate]= Time.parse(bdate.to_s).to_i
end

These are the accessors I have to try to do this, however when the data gets repopulated into a form because of an error or something, I end up with a timestamp in the text field instead of the date string.

Here is the form. (element_block just wraps the element and label in the right tags for a dl)

<% form_for @pc, :url => { :controller => 'core', :action => 'create'}  do |f| %>
  <%= f.error_messages  %>
  <dl>
    <%= element_block  f.label(:contract, 'Contract Year'), f.contract_year_select(:contract) %>
    <% f.fields_for :profile_program do |pp| %>
      <%= element_block pp.label(:program, 'Program'), pp.hsp_program_select(:program) %>
    <% end %>

    <%= element_block f.label(:passport_number, 'Passport Number'), f.text_field(:passport_number) %>
    <%= element_block f.label(:passport_country, "Country that issued the student's passport"), f.countries_select(:passport_country) %>
    <%= element_block f.label(:passport_expires, 'Passport Expiration Date'), f.text_field(:passport_expires, :class => 'datepicker') %>
    <%= element_block f.label(:last_name, 'Last Name (as on passport)'), f.text_field(:last_name) %>
    <%= element_block f.label(:first_name, 'First Name (as on passport)'), f.text_field(:first_name) %>
    <%= element_block f.label(:middle_name, 'Middle Name (as on passport)'), f.text_field(:middle_name) %>
    <%= element_block f.label(:other_names, 'Other Names'), f.text_field(:other_names) %>
    <%= element_block f.label(:residence_street_address, 'Street Address'), f.text_field(:residence_street_address) %>
    <%= element_block f.label(:residence_city, 'City'), f.text_field(:residence_city) %>
    <%= element_block f.label(:residence_province, 'Province'), f.text_field(:residence_province) %>
    <%= element_block f.label(:residence, 'Country'), f.countries_select(:residence) %>
    <%= element_block f.label(:residence_postal_code, 'Postal Code'), f.text_field(:residence_postal_code) %>
    <%= element_block f.label(:birthdate, 'Date of Birth'), f.text_field(:birthdate) %>
    <%= element_block f.label(:citizenship, 'Country of Citizenship'), f.countries_select(:citizenship) %>
    <%= element_block f.label(:birth_city, 'Birth City'), f.text_field(:birth_city) %>
    <%= element_block f.label(:nationality, 'Nationality'), f.countries_select(:nationality) %>
    <%= element_block f.label(:gender, 'Gender'), f.gender_select(:gender) %>
    <%= element_block f.label(:email, 'Email'), f.text_field(:email) %>
    <%= element_block f.label(:desires_esl, 'Does the student wish to participate in CLEP?'), f.bool_yes_no_select(:desires_esl) %>
    <%= element_block f.label(:may_pay_tuiton, 'Willing to pay tuition'), f.yes_no_select(:may_pay_tuition) %>
  </dl>
  <div class="submit"><%= submit_tag("Proceed to Step Two") %></div>

<% end %>

Here's my helper

module ApplicationHelper
  def element_block label, element, example = ''
    example = '<br>' + example unless example.empty?
    "<dt>#{label}</dt><dd>#{element}#{example}</dd>"
  end
end

Update I've also tried the following with the same result.

def birthdate
  Time.at(read_attribute(:birthdate)).to_date
end

def birthdate=(bdate)
  write_attribute(:birthdate, bdate.to_time.to_i)
end

I'm using Rails 2.3.5

A: 

First of all, you should not be returning a blank string when you get zero as a value from the database:

>> Time.at(0)
=> Thu Jan 01 01:00:00 +0100 1970

Second, you should be reading/writing using ActiveRecord-supplied methods:

def birthdate
  Time.at(read_attribute(:birthdate)).to_date
end

def birthdate=(date)
  write_attribute(:birthdate, date.to_time.to_i)
end
Josh
I was using read_attribute and write_attribute but someone in #RubyOnRails on freenode was saying I shouldn't. Seemed stupid to me though.
docgnome
I'll try this variation in the morning when I have access to the db.
docgnome
Nope. Same result. It's like the FormHandler or whatever it is that fills back in the form, isn't using the accessor.
docgnome
Can you paste the form code in the original question, and I'll let you know what is going on. Also, what version of Rails are you using?
Josh
Done! Using rails 2.3.5
docgnome
Are you using a custom form builder/helper? Could you pastie that, as well? Thanks!
Josh
I added the only helper I have. I have duck punched a few extra menu selects but I'm not using them with the birthdate field which is the problem child. No pun intended.
docgnome
Have you tried making this into a `date_select` field? I think the problem is that there is movement from `Date` to `String` somewhere, and its just not helping that its a `text_field` tag: f.text_field(:birthdate)
Josh
I could try but I was using jquery to give it a selector
docgnome
Okay, well there is clearly something wrong with the way you're writing the timestamp back. In other words, do things with convention, and you're good to go ;)
Josh
Yeah... `date_select` didn't help. Just chokes when trying to assign the value back to it because it isn't designed to deal with timestamps. If I wasn't using a legacy db, believe me, I wouldn't be using a unix timestamp :-(
docgnome
You can also try using `time_select`
Josh
A: 

What I ended up doing is creating a false field in the model that is a Date and using a after_validation filter to convert it on the real field. In the model

after_validation :write_birthdate

def write_birthdate
  bd = self.birthday
  unless bd.nil?
    write_attribute(:birthdate, Time.parse(bd).to_i)
  end
end

...

def birthday
  bd = read_attribute :birthday
  if bd.nil?
    bd = read_attribute :birthdate
    return Date.parse(Time.at(bd).to_s).to_s unless bd.nil? or bd === 0
    return nil
  end
  return bd
end

def birthday=(bdate)
  write_attribute(:birthday, bdate)
end

alias :birthdate :birthday
alias :birthdate= :birthday=

This way I write to birthday as a Date but birthdate remains a timestamp.

docgnome