On 11/15/06, James Edward Gray II <james / grayproductions.net> wrote: > On Nov 15, 2006, at 12:35 PM, Jamie Macey wrote: > > That being said, on a lark I did a bit of refactoring and tried to > > move as much logic out of the ProgramManager into an actual Program > > class - the results are below. > > > > I definitely prefer the second version. It's got 10 more lines of > > actual code, but there's less random logic strewn about (and the logic > > is simpler). There's a definite separation of responsibilities - > > Program just does querying, ProgramManager handles priorities. > > You just learned what I also learned today, writing the summary. ;) > > James Edward Gray II > So it seems I've already missed the summary, but here's my solution. I took a different approach - I stored the programs to record in a linked list sorted by start time. As I was thinking about the solution. I realized this is the first time I even considered a linked list since I started using Ruby. For most places I would have used them in other languages, I can use Arrays in Ruby. (For a moment I thought - oh, can't do lists, no pointers - but then I realized that references worked just fine) The solution needs serious refactoring. I have 2 classes where I should have 4 or 5 to separate the list logic from the VCR logic. But I don't have any more time this week. -Adam ---------------- # A Program node. Contains the program recording information, # the linked list handle, and a few functions for list maintainance class Program attr_reader :start,:end_t,:channel, :repeat, :nxt def initialize start,finish,channel,repeat=false @start,@end_t,@channel = start,finish,channel @repeat = repeat end def advance @start+= (7*24).hours @end_t+= (7*24).hours self end def not_after? time @end_t < time || (@end_t == time && (@nxt && @nxt.start <=time)) end def split_and_insert node tail = Program.new(node.start,@end_t,@channel,@repeat) @end_t = node.start insert_after_self node node.insert_after_self tail end def insert_after_self node node.set_next @nxt @nxt = node end def set_next node @nxt = node end end class Time #returns a Time object for the begining of the day def daystart arr = self.to_a arr[0,3]=[0,0,0] Time.local(*arr) end end def hours_between daynum, daystr days = %w{sun mon tue wed thu fri sat}.map{|d|Regexp.new(d)} days.each_with_index{|dayname,idx| if dayname =~ daystr idx+=7 until idx-daynum >= 0 return (idx-daynum)*24.hours end } end class ProgramManager def initialize @head = nil end #adds programs to a linked list sorted by start time def add args start = args[:start] finish = args[:end] channel = args[:channel] if !daylist = args[:days] insert Program.new(start,finish,channel) repeat = nil else t = Time.now #This is only going to future recurring dates on the list #t = Time.local(2006,10,30) #so in order to pass the unit tests, #force back to Oct 30th today = t.wday daystart = t.daystart daylist.each{|day| offset = hours_between(today,day) p_start = daystart+offset+start p_finish = daystart+offset+finish insert Program.new(p_start,p_finish,channel, true) } end end #inserts node in list, sorted by time. #newer entries are inserted before older entries for the same period. #we don't do anything special if the end of the new entry overlaps #the beginning of the older one - when the new entry is done, the old one will be #next on the list, so it will become active, wherever it is in it's range. #the only tricky case is if a new program starts in the middle of an older one #then we have to split the old node, and insert the new one in the split. def insert new_node prevNode,curNode=nil,@head while curNode and curNode.end_t <= new_node.start prevNode,curNode=curNode,curNode.nxt end if curNode and curNode.start < new_node.start curNode.split_and_insert new_node elsif prevNode prevNode.insert_after_self new_node else new_node.set_next @head @head = new_node end end #pops all the nodes that end before the current time. #if they are repeating, advance their time one week, and reinsert them. def find_current_node time return false unless @head while @head && @head.not_after?(time) old_node = @head @head = @head.nxt insert old_node.advance if old_node.repeat end @head if @head && @head.start <= time end def record? time node = find_current_node time node && node.channel end end