From: Gordon Thiesfeld Date: 2006-08-28T01:04:12+09:00 Subject: Re: [QUIZ][SOLUTION] DayRange (#92) Here's my solution with unit tests (I'm trying to get into the habit). I made Day a subclass of Date, but I ended up overriding most of the methods I needed, so I don't know if it bought me much. It also handles wrapping, but can be disabled by commenting out the Day#succ method. Thanks, Gordon # # day_range.rb # class Array def collapse_ranges(options = {}) range = [] return_array = [] self.each_with_index do |item, i| range.push(item) # if this is the last item # - or - # there is another item after this one if item == self.last || self[i + 1] # if this is the last item # - or - # the next item is not the item after the current one if item == self.last|| item.succ != self[i + 1] # if there is a range of 3 items or more if range.length >= 3 return_array.push(range.first..range.last) # else empty the range individually else return_array.concat range end # clear out the range range.clear end end end return return_array end def to_s self.map { |i| i.to_s }.join(', ') end end class Range def to_s "#{first}-#{last}" end end require 'date' class Day < Date Days = [nil, "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] Abbr = [nil, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] def self.commercial(int) day = send("from_#{int.class}".downcase.intern, int) super(1984,1,day) end def succ if cwday == 7 Day.commercial(1) else super end end def to_s Days[cwday] end def to_abbr Abbr[cwday] end alias_method :to_s, :to_abbr def self.from_string(string) # If string isn't in Days or Abbr, return string and let Date#commercial raise ArgumentError Days.index(string.capitalize) || Abbr.index(string.capitalize) || string.capitalize end def self.from_fixnum(int) # Date#commercial allows integers over 7, so raise ArgumentErrror here if (1..7).include? int int else raise ArgumentError end end end class DayRange def initialize(array) @array = array.map{|i| Day.commercial(i) }.collapse_ranges end def to_s @array.to_s end end # # test_day_range.rb # require 'test/unit' require 'lib/days.rb' class TestArray < Test::Unit::TestCase def test_collapse_ranges assert_equal( [(1..4),6], [1,2,3,4,6].collapse_ranges) end def test_to_s assert_equal([1,2,3].to_s, '1, 2, 3') end end class TestRange < Test::Unit::TestCase def test_to_s assert_equal((1..3).to_s, '1-3') end end class TestDay < Test::Unit::TestCase def setup @day = Day.commercial(6) @next_day = Day.commercial('Sun') end def test_error assert_raise(ArgumentError){ Day.commercial('not') } assert_raise(ArgumentError){ Day.commercial(8) } end def test_succ assert_equal(@day.succ.cwday,7) end def test_spaceship assert(@day < @next_day) end def test_to_s assert_equal('Sat', @day.to_s) end end class TestDayRange< Test::Unit::TestCase def test_to_s [ [[1,2,3,4,5,6,7],'Mon-Sun'], [[1,2,3,6,7], "Mon-Wed, Sat, Sun"], [[1,3,4,5,6], "Mon, Wed-Sat"], [[2,3,4,6,7], "Tue-Thu, Sat, Sun"], [[1,3,4,6,7], "Mon, Wed, Thu, Sat, Sun"], [[7], "Sun"], [[1,7], "Mon, Sun"] , [['Tue','Wed','Thu','Fri'],"Tue-Fri"], [['Wednesday','Thursday','Friday','Saturday'],"Wed-Sat"], [['tue','fri','sat','sun'], "Tue, Fri-Sun"], [[5,6,7,1],"Fri-Mon"], [[1,5,6,7],"Mon, Fri-Sun"] ].each do |arr, str| assert_equal(str, DayRange.new(arr).to_s) end assert_raise(ArgumentError){ DayRange.new([1,8]).to_s } end end