Loading part of a has_many association, and sorting as I add more elements


I have what boils down to a timesheet app. Before I display the entry
form (eg one text input per day in the current schedule) I load the
shifts I have in the database for the given period, then create new,
blank shifts to fill in the blanks for the week before sorting the
shifts by date and assigning to the relation.

This works in that if I have 2 shifts stored in the DB and want to
display a week, the routine adds 5 more blank shifts and sorts.
However, once I assign this to the relationship, the sorting seems to
revert to created_by rather than the sort order of the array I
assigned (example below)

class Shift < ActiveRecord::Base
  belongs_to :employee

class Employee < ActiveRecord::Base
     has_many :shifts, :order => "date ASC"

     def load_schedule(start, numdays)

        raise "numdays must be >=1" if numdays < 1
        # find the shifts that are already stored that are between
start and start+numdays
        currentshifts = Shift.find(
           :conditions => ["employee_id = :eid AND date >= :start AND
date <= :finish",
           { :eid => self.id,
              :start => start.at_beginning_of_day,
              :finish => start.at_beginning_of_day
           } ] )

         newshifts = Array.new
         # iterate through the week and create new shifts for the days
that are missing
         # code omitted

         # debugging stuff
         puts "============================================"
         c = newshifts.to_a + currentshifts.to_a
         puts "unsorted"
         c.each { |s| puts "#{s.date} - #{s.hours}" }
         d = c.sort_by { |a| a.date }
         puts "sorted"
         d.each { |s| puts "#{s.date} - #{s.hours}" }
         self.shifts = d
         puts "in employee object"
         self.shifts.each { |s| puts "#{s.date} - #{s.hours}" }

The results of this are:
2009-02-25 -
2009-02-26 -
2009-02-28 -
2009-03-02 -
2009-03-03 -
2009-02-27 - 1.5
2009-03-01 - 1.5
2009-02-25 -
2009-02-26 -
2009-02-27 - 1.5
2009-02-28 -
2009-03-01 - 1.5
2009-03-02 -
2009-03-03 -
in employee object
2009-02-27 - 1.5
2009-03-01 - 1.5
2009-02-25 -
2009-02-26 -
2009-02-28 -
2009-03-02 -
2009-03-03 -

The reason I'm sorting and adding the blank shifts in the model is
because I'm passing the object back up to the view where I'm using the
FormHelpers to create the form. I'm loading a subset of the shifts at
a time because it seems wasteful to load a year's worth of timesheet
data if I'm only working with a week.

Why/how is the Array getting resorted when I assign it to the
association? How do I stop it?
Should I be working with a subset of the shift data this way, or is
there a better way?
Am I going about this completely wrong?



Why/how is the Array getting resorted when I assign it to the
association? How do I stop it?

The key bit is the replace method in

It's trying to minimise the number of delete or updates to the
database, so it first removes all the records that should no longer be
there, then appends all the records that need to be added to the
collection, which will mess with your order if the collection is non
empty to begin with

If you reload the association the records should come out in the right


self.shifts = (newshifts.to_a + currentshifts.to_a).sort_by { |a|
a.date }
   # Reload to get them in order

That seems to work (or at least passes my test cases :wink: ), thanks a