Begin forwarded message:

> From: Eric Torreborre <etorreborre / yahoo.com>
> Date: August 31, 2006 4:20:10 PM CDT
> To: submission / rubyquiz.com
> Subject: Please Forward: Ruby Quiz Submission (92)
> Reply-To: Eric Torreborre <etorreborre / yahoo.com>
>
> Hi all,
>
> This is my first  submission to the quiz and I am very glad to do  
> it! I used this quiz to introduce Ruby to my fellow workers for  
> today's "Dojo" (see http://bossavit.com/dojo/and http:// 
> wiki.agilefinland.com/?CodingDojo).
>
> They found it a very gentle introduction to the language.
>
> As for myself, I found a great interest in writing properly things  
> down and then having a look at all other submissions, searching for  
> better ways to do things.
>
> Anyway, here is my submission, that goes around the following lines:
>
> -use of rspec (behavior driven development) to write acceptance tests
> -use of the Set class to know if days identifiers are correct
> -extension of the Range class to split an array into an array of  
> ranges for consecutive values
>
> Here's the code:
>
> ===========================
> #day_range_spec.rb
> ===========================
> require "spec"
> require "day_range"
>
> context "A new DayRange" do
>   specify "should accept a list of day numbers" do
>     DayRange.new([1, 2, 3])
>   end
>
>   specify "should accept a list of abbreviated day names" do
>     DayRange.new(%w{Mon Tue Fri})
>   end
>
>   specify "should accept a list of whole day names" do
>     DayRange.new(%w{Monday Tuesday Friday})
>   end
>
>   specify "should raise an argument error an invalid day number is  
> inputed in the list" do
>     lambda {DayRange.new([1, 8])}.should_raise(ArgumentError)
>   end
>
>   specify "should raise an argument error an invalid abbreviated  
> day is inputed in the list" do
>     lambda {DayRange.new(["bla"])}.should_raise(ArgumentError)
>   end
>
>   specify "should raise an argument error an invalid day name is  
> inputed in the list" do
>     lambda {DayRange.new(["lunedi"])}.should_raise(ArgumentError)
>   end
> end
>
> context "A week with a single day" do
>   specify "should return the day short name only" do
>     DayRange.new([7]).to_s.should_equal('Sun')
>   end
> end
>
> context "An initialized DayRange" do
>   specify "should sort days starting with Monday" do
>     DayRange.new([3, 1]).to_s.should_equal('Mon, Wed')
>   end
> end
>
> context "A week DayRange" do
>   specify "should represent three or more consecutive days by  
> listing the first day followed by a hyphen (-), followed by the  
> last day of the range" do
>     DayRange.new([1, 2, 3, 4, 5, 6, 7]).to_s.should_equal('Mon-Sun')
>   end
> end
> context "A week without Thursday and Friday" do
>   specify "should return 'Mon-Wed, Sat, Sun' (added hyphen)" do
>     DayRange.new([1, 2, 3, 6, 7]).to_s.should_equal('Mon-Wed, Sat,  
> Sun')
>   end
> end
>
> ===========================
> # day_range.rb
> ===========================
> require 'set'
> require 'range'
>
> class DayRange
>   def initialize(daylist)
>     raise ArgumentError, "#{daylist.to_s} is not valid" unless  
> Week.are_valid_identifiers?(daylist)
>     @days = Week.day_indexes(daylist)
>   end
>
>   def to_s
>     ranges = Range.split(@days)
>     ranges.collect{|r| Week.short_names_from_range(r)}.join(', ')
>   end
>
> end
>
> class Week
>   DAYS = [[1, 'Mon', 'Monday'],
>           [2, 'Tue', 'Tuesday'],
>           [3, 'Wed', 'Wednesday'],
>           [4, 'Thu', 'Thursday'],
>           [5, 'Fri', 'Friday'],
>           [6, 'Sat', 'Saturday'],
>           [7, 'Sun', 'Sunday']]
>
>   def self.are_valid_identifiers?(days)
>     return DAYS.flatten.to_set.superset?(days.to_set)
>   end
>
>   def self.day_indexes(daylist)
>     return DAYS.select{|d| !d.to_set.intersection 
> (daylist.to_set).empty?}.collect{|d| d[0]}.sort!
>   end
>
>   def self.short_names_from_range(range)
>     if range.to_a.size == 1
>       return DAYS[range.first - 1][1]
>     elsif range.to_a.size == 2
>       return DAYS[range.first - 1][1] + ', ' + DAYS[range.last - 1][1]
>     else
>       return DAYS[range.first - 1][1] + '-' + DAYS[range.last - 1][1]
>     end
>   end
> end
>
> ===========================
> #range_spec.rb
> ===========================
> require "spec"
> require "range"
>
> context "A Range" do
>   specify "should split a [1] array in a 1..1 range" do
>     Range.split([1]).should_equal([1..1])
>   end
>
>   specify "should split a [1, 2] array in a 1..2 range" do
>     Range.split([1, 2]).should_equal([1..2])
>   end
>
>   specify "should split a [1, 2, 3] array in a 1..3 range" do
>     Range.split([1, 2, 3]).should_equal([1..3])
>   end
>
>   specify "should split a [1, 3] array in 2 ranges: [1..1, 3..3]" do
>     Range.split([1, 3]).should_equal([1..1, 3..3])
>   end
>
>   specify "should split a [1, 2, 4] array in 2 ranges: [1..2,  
> 4..4]" do
>     Range.split([1, 2, 4]).should_equal([1..2, 4..4])
>   end
>
> end
> ===========================
> #range.rb
> ===========================
> class Range
>   def self.split(list)
>     list.inject([]) do |ranges, i|
>       last_range = ranges.last
>       if (last_range.nil? || i > last_range.last.next)
>         ranges << [i, i]
>       else
>         last_range[1] = i
>       end
>       ranges
>     end.collect{|r| r.first..r.last} # transform to Range objects
>   end
>
> end
>
> --------------------------------------------------
> Eric Torreborre
> LTG - Product Manager
> LEIRIOS
> tel: 33(0)6.61.48.57.65/33(0)3.81.88.62.02
> e-mail: etorreborre / yahoo.com
> blog: http://etorreborre.blogspot.com
> --------------------------------------------------
>
>