views:

50

answers:

3

I have an Attendance model that allows the user to enter a starting, ending and break time, each as a ruby Time object. Each attendance also has a day (ruby Date object). I want the 'Date' elements of the times to be the same, so I override the assignment operators like this:

def startTime= (t)
  self[:startTime] = Time.mktime(day.year, day.month, day.day, t.hour, t.min)
end
def endTime= (t)
  self[:endTime] = Time.mktime(day.year, day.month, day.day, t.hour, t.min)
end
def breakTime= (t)
  self[:breakTime] = Time.mktime(day.year, day.month, day.day, t.hour, t.min)
end

My problem is that my tests fail only when I override the breakTime= function. They all fail on calls to new, i.e. att = Attendance.new @valid_attributes, specifically at breakTime=:

undefined method `year' for nil:NilClass

Apparently, breakTime= is getting called before the day is defined on the object, even though, startTime= and endTime= are not getting called so early. I realize this overriding is probably inelegant, but I'm pretty new to rails, so I imagine someone has made this mistake before. How should I be doing this differently?

+1  A: 

Why don't you use active record callbacks . before_save can help you out here .

NM
+4  A: 

Instead of overriding the attribute setters, use a before_save callback to change the attributes just before saving the model:

class Attendance < ActiveRecord::Base
  before_save :update_timestamps

  protected
  def update_timestamps
    self.start_time = Time.mktime(day.year, day.month, day.day, start_time.hour, start_time.min)
    # And similarly for the other columns
  end
end

For an overview on how callbacks work, have a look at the Active Record Validations and Callbacks guide over at guides.rubyonrails.org.

Pär Wieslander
+1  A: 

If you wish to stick with the override approach (which has the advantage that even unsaved models also conform with common time formatted attributes) you need to call the super class's implementation in each of yours:

def startTime= (t)
  t = Time.mktime(day.year, day.month, day.day, t.hour, t.min)
  super t
end
def endTime= (t)
  t = Time.mktime(day.year, day.month, day.day, t.hour, t.min)
  super t
end
def breakTime= (t)
  t = Time.mktime(day.year, day.month, day.day, t.hour, t.min)
  super t
end
bjg
Isn't `super` is getting called *after* the line that's throwing the error?
daniel
@daniel Yeah, I'm not sure if my answer, in of itself, would have solved your problem but I wanted to point out that the overrides as you proposed were missing the super calls
bjg