[QUIZ] DayRange (#92)

On the opposite end, I've been thinking about codegolf too much.
Here's an update to my under-engineered solution, which meets all the
requirements in 3 statements. It could be even shorter, but I wanted
to keep some possibility of someone else reading it.

class DayRange
  Dnames = [nil]+(1..7).map{|n|Time.gm(1,1,n).strftime("%a")}
  def initialize *l
    @range=l.map{|v|v.respond_to?(:split) ? v.split(',') :
v}.flatten.map{|v|(n=v.to_i)>0 ? n :
(1..7).find{|i|/#{Dnames[i]}/i=~v.to_s}||raise("ArgumentError:
#{v}")}.sort
  end
  def to_s
    @range.map{|e|"#{"-" if e-1==@l}#{Dnames[@l=e]}"}.join(',
').gsub(/(, -\w+)+, -/,'-').gsub(/ -/,' ')
  end
end

p DayRange.new( 1,3,4,5,6).to_s
p DayRange.new("Tuesday,Wednesday,Sunday").to_s
p DayRange.new(1,"tuesday,wed,5","6,7").to_s

···

tOn 8/27/06, Boris Prinz <boris.prinz@gmail.com> wrote:

my solution is definitely over-engineered :slight_smile:

Well, if you allow the custom day names and control that with an argument passed to to_s() calling it multiple times seems reasonable. Here's my own offering allowing that:

class DayRange
   SHORT_NAMES = %w[Mon Tue Wed Thu Fri Sat Sun].freeze
   LONG_NAMES = %w[ Monday
                     Tuesday
                     Wednesday
                     Thursday
                     Friday
                     Saturday
                     Sunday ].freeze

   def initialize(*days)
     @days = days.map do |d|
       ds = d.to_s.downcase.capitalize
       SHORT_NAMES.index(ds) || LONG_NAMES.index(ds) || d - 1
     end rescue raise(ArgumentError, "Unrecognized number format.")
     unless @days.all? { |d| d.between?(0, 6) }
       raise ArgumentError, "Days must be between 1 and 7."
     end
     raise ArgumentError, "Duplicate days given." unless @days == @days.uniq
   end

   def to_s(names = SHORT_NAMES)
     raise ArgumentError, "Please pass seven day names." unless names.size == 7

     @days.inject(Array.new) do |groups, day|
       if groups.empty? or groups.last.last.succ != day
         groups << [day]
       else
         groups.last << day
         groups
       end
     end.map { |g| g.size > 2 ? "#{g.first}-#{g.last}" : g.join(", ") }.
         join(", ").gsub(/\d/) { |i| names[i.to_i] }
   end
end

__END__

James Edward Gray II

···

On Aug 27, 2006, at 3:25 PM, Morus Walter wrote:

one remark:
I don't like the suggested interface (though I used and didn't work out an
alternative). It does not seem reasonable to instanciate an object providing
all the information in the constructor and the only thing one can do with
that object is call one method once (multiple calls to to_s should be avoided
for performance).

My second solution builds off of seeing one line of Morus' solution:

   self.inject( [ ] ) do | list, item |

(which helped, because I rarely remember inject)

and a new solution was born:

class DayRange
   NAMEMAP={"Mon"=>1, "Tue"=>2, "Wed"=>3, "Thu"=>4, "Fri"=>5, "Sat"=>6,
   "Sun"=>7, "Thurs"=>4, "Monday"=>1, "Tuesday"=>2, "Wednesday"=>3,
   "Thursday"=>4, "Friday"=>5, "Saturday"=>6, "Sunday"=>7}

   REVERSENAMEMAP=[nil, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]

   def initialize(*args)
      #parse arguments into Integers from 1 to 7
      args.collect!{|x| NAMEMAP || x}
      args.sort!.uniq!
      raise ArgumentError if args.any? do |x|
   not x.is_a?(Fixnum) or not (1..7).include? x
      end

      #turn everything into ranges
      @ranges=args.inject() do |a,v|
  if a[-1]==nil or a[-1].last != v-1
    a << (v..v)
  else
          #extend the existing range to include the new element
    a[-1]=((a[-1].first)..v)
  end
  a
      end
      
      #this code can be included if you would like wrap-around ranges
      #note that it constructs an ranges (with last<first) which doesn't
      #actually work with some ruby features. Hence, I don't use those
      #features which it breaks.

      #if @ranges[-1].last==7 and @ranges[0].first==1
      # v=((@ranges[-1].first)..(@ranges[0].last))
      # @ranges.delete_at(-1)
      # @ranges.delete_at(0)
      # @ranges << v
      #end
   end

   def to_s
      #determine how to print each range based on the length of the range
      @ranges.collect do |r|
   if r.first==r.last
      REVERSENAMEMAP[r]
   elsif r.first==r.last-1
      "#{REVERSENAMEMAP[r.first]}, #{REVERSENAMEMAP[r.last]}"
   else
      "#{REVERSENAMEMAP[r.first]}-#{REVERSENAMEMAP[r.last]}"
   end
      end.join(", ")
   end
end

puts DayRange.new(1,2,3,4,5,6,7).to_s
puts DayRange.new(1,2,3,6,7).to_s
puts DayRange.new(1,3,4,5,6).to_s
puts DayRange.new(2,3,4,6,7).to_s
puts DayRange.new(1,3,4,6,7).to_s
puts DayRange.new(7).to_s
puts DayRange.new(1,7).to_s
puts DayRange.new(1,8).to_s rescue puts "ArgumentError"

···

Morus Walter <ml.morus.walter@googlemail.com> wrote:

--
Ken Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/

Does anybody have some idea why my posts are getting garbage chars ('=20'
and '=3D') added on the archive site:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/211029

It appears to affect only me, and other archives of ruby-talk such as this
one:

http://aspn.activestate.com/ASPN/Mail/Message/ruby-talk/3250521

do not have the same issue. I would really like to fix this, but I have no
clue what may be causing it. Any suggestions are welcome...

Thanks,
-d

···

--
darren kirby :: Part of the problem since 1976 :: http://badcomputer.org
"...the number of UNIX installations has grown to 10, with more expected..."
- Dennis Ritchie and Ken Thompson, June 1972

Hi,

Here is my solution. I'm a RubyNuby but I thought this one was easy so
I gave it a shot.

require 'test/unit'

class DaysToString
  Table= {'mon' => 1,'tue' => 2,'wed' => 3,'thu' => 4,
          'fri' => 5,'sat' => 6,'sun' => 7,
          'monday'=>1,'tuesday'=>2,'wednesday'=>3,'thursday'=>4,
          'friday'=>5,'saturaday'=>6,'sunday'=> 7,
          1 => 1, 2 => 2, 3 => 3,4 => 4, 5 => 5,6 => 6, 7 => 7}
  Days=['Mon','Tue','Wed','Thu','Fri','Sat','Sun',]
  def initialize *arg
    arg.map! do |a|
      a.downcase! if a.respond_to?(:downcase!)
      raise(ArgumentError,"Wrong Format.") if !Table.include?(a)
      a = Table[a]
    end
    arg.sort!.uniq!
    @data = arg
  end
  
  def to_s
    temp =[]
    txt = ""
    loop do
     x=@data.shift
     if ((x.is_a?(NilClass) && !temp.empty?) || (!temp.empty? &&
temp.last!=x-1))
       txt+= "#{Days[temp.first-1]}"
       txt+= ",#{Days[temp[1]-1]}" if temp.length==2
       txt+= "-#{Days[temp.last-1]}" if temp.length>2
       txt+= ','
       temp.clear
       break if x.is_a?(NilClass)
     end
      temp.push(x)
    end
    txt
  end
end

class TestDaysToString < Test::Unit::TestCase
  def test_all
    assert_raise(ArgumentError)
{DaysToString.new(1,2,'tester',4,5,7).to_s}
    assert_raise(ArgumentError) {DaysToString.new(8).to_s}
    assert_raise(ArgumentError) {DaysToString.new('wacky_day').to_s}
assert_equal("Mon-Wed,Fri-Sun,",DaysToString.new(1,2,3,5,6,7).to_s)
assert_equal("Mon-Sun,",DaysToString.new(1,2,3,4,4,5,4,6,7,1).to_s)
    assert_equal("Tue-Thu,",DaysToString.new(2,3,4).to_s)
    assert_equal("Fri,",DaysToString.new(5).to_s)
assert_equal("Mon,Sat,Sun,",DaysToString.new('sun','mOnDaY',6).to_s)
    assert_equal("Tue,Thu-Sun,",DaysToString.new(4,5,6,7,2).to_s)
    assert_equal("Tue,Thu-Sun,",DaysToString.new(4,5,6,7,2).to_s)
  end
end

···

--
Adam Skobi

The fix is obvious and I have already applied it: rename
test_valid_args to test_args so it runs first.

Test::Unit provides the functionality you need in the "setup" and "teardown"
methods, which are run before and after each test_* method in your class.

tim

Request for clarification.

Are you asking us to define a class or a top-level conversion method?

The quiz asks for a class, yes.

Thanks for the clarification. Rereading the OP more carefully, I see this is explicitly specified in the bulleted rules. I got thrown off course by following sentences, which precede the rules:

For example, suppose a musician plays at a certain venue on Monday, Tuesday,
Wednesday, and Saturday. You could pass a list of associated day numbers to your
object or method, which might return "Mon-Wed, Sat".

To me, the second sentence doesn't make a lot of sense in the context of a DayRange class. Something that takes a list of numbers and returns a string, suggests a top-level conversion function.

Regards, Morton

···

On Aug 25, 2006, at 8:49 PM, James Edward Gray II wrote:

On Aug 25, 2006, at 5:47 PM, Morton Goldberg wrote:

There's no right or wrong answers here. I see you introduces a feature not called for in the quiz and I like it. Clever.

James Edward Gray II

···

On Aug 26, 2006, at 10:02 AM, Robert Retzbach wrote:

Thanks I was about to ask too.

For the time being I made it this way:

  dayrange = DayRange.new [1,3,4,5,6,7], %w{Mo Di Mi Do Fr Sa So}
  p dayrange.to_s #=> "Mo, Mi-So"

I think that's the way it should work. If not, please tell me.

It certainly looks good to me. I believe the quiz master has indicated we have considerable latitude in what we admit as a valid argument sequence for DayRange#initialize.

Allowing a set of day names to be specified when an instance of DayRange is created is a neat idea. I didn't think of that. Also, I decided not to allow lists such as [1,3,4,5,6,7] although my DayRange#initialize is pretty permissive in other ways. The closest I can get to what you show is:

<code>
    days = DayRange.new(1,3,4,5,6,7)
    puts days
    puts days.to_s(true)
</code>

<result>
Mon, Wed-Sun
Monday, Wednesday-Sunday
</result>

As you can see, I do allow optional long-form English names for days to be returned from to_s. But your idea of allowing the programmer to specify the names at instance creation time is better.

Regards, Morton

···

On Aug 26, 2006, at 11:02 AM, Robert Retzbach wrote:

Thanks I was about to ask too.

For the time being I made it this way:

  dayrange = DayRange.new [1,3,4,5,6,7], %w{Mo Di Mi Do Fr Sa So}
  p dayrange.to_s #=> "Mo, Mi-So"

I think that's the way it should work. If not, please tell me.

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

Someone smarter than me will have to fill in the blanks, but I'm
pretty sure it has to do with sending e-mails to the list as HTML
instead of plain text e-mails.

···

On 8/28/06, darren kirby <bulliver@badcomputer.org> wrote:

Does anybody have some idea why my posts are getting garbage chars ('=20'
and '=3D') added on the archive site:

Apparently, Kmail sends messages encoded (inappropriately, it seems)
as quoted-printable. This means that certain characters (unprintable
ones, and ones that have the high bit set to 1) are encoded by an
equals sign (=) followed by two hexadecimal digits. I'm not a Kmail
user, so I can't give you a sure way to fix it, but a quick google
search tells me that it may help to tell Kmail to allow 8-bit encoding
for your messages. This may cause problems of its own as some mail
software may have problems with 8-bit characters, but that should be
OK as long as you stick to ASCII which is 7-bit. It may also help if
you could set your char-set to US-ASCII instead of iso-8859-6.

HTH

Peter

···

On 8/28/06, darren kirby <bulliver@badcomputer.org> wrote:

Does anybody have some idea why my posts are getting garbage chars ('=20'
and '=3D') added on the archive site:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/211029

It appears to affect only me, and other archives of ruby-talk such as this
one:

ActiveState Community - Boosting coder and team productivity with ready-to-use open source languages and tools.

do not have the same issue. I would really like to fix this, but I have no
clue what may be causing it. Any suggestions are welcome...

I can't see how setup and teardown would help in this case. They are run as before and after methods for each test, not as initializer and finalizer for the whole test case. Also, recall that I don't want to create any test objects outside of test_valid_args. I only want to use objects that have already been validated by this test in the other tests. To me, that means test_valid_args MUST run first.

However, being new to Test::Unit, I may well be missing something. Could you post an example showing how setup and teardown could be used to avoid the trap I fell into?

Regards, Morton

···

On Aug 30, 2006, at 6:55 AM, Tim Kuntz wrote:

The fix is obvious and I have already applied it: rename
test_valid_args to test_args so it runs first.

Test::Unit provides the functionality you need in the "setup" and "teardown"
methods, which are run before and after each test_* method in your class.

Gordon Thiesfeld wrote:

Here's my solution with unit tests (I'm trying to get into the habit).

I forgot to mention that I got the Array#collapse_ranges method here
(though I tweaked it a bit):

Just wanted to give credit where it was due.

quoth the Lyle Johnson:

···

On 8/28/06, darren kirby <bulliver@badcomputer.org> wrote:
> Does anybody have some idea why my posts are getting garbage chars ('=20'
> and '=3D') added on the archive site:

Someone smarter than me will have to fill in the blanks, but I'm
pretty sure it has to do with sending e-mails to the list as HTML
instead of plain text e-mails.

But I _never_ send HTML mails...ever...

-d
--
darren kirby :: Part of the problem since 1976 :: http://badcomputer.org
"...the number of UNIX installations has grown to 10, with more expected..."
- Dennis Ritchie and Ken Thompson, June 1972

Here's my modification to your tests to get them working with setup():

class TestDayRange < Test::Unit::TestCase

    # All these produce @days == [1..7].
    ONE_RANGE = [
       %w[mon tue wed thu fri sat sun],
       %w[monDay tuesday Wednesday Thursday friDAY saturday SUNDAY],
       %w[mon-fri sat-sun],
       %w[4-7 1-3],
       (1..7).to_a.reverse,
       [4, 7, 6, 5, 4, 1, 2, 1, 2, 3, 3, 7, 6, 5],
    ]

    # Both these produce @days == [1..3, 5..7].
    TWO_RANGES = [
       %w[mon-mon tue-tue wed-wed fri-sun],
       [1, 2, 'mon-wed', 'friday', 6, 7]
    ]

    INVALID_ARGS = [
       [1, 2, 'foo'],
       %w[foo-bar],
       %w[sat-mon],
       (0..7).to_a.reverse,
       (1..8).to_a
    ]

    def setup
      assert_nothing_raised(ArgumentError) do
        @one_range = ONE_RANGE.map { |args| DayRange.new(*args) }
      end
      assert_nothing_raised(ArgumentError) do
        @two_ranges = TWO_RANGES.map { |args| DayRange.new(*args) }
      end
    end

    def test_valid_args
       @one_range.each do |obj|
          assert_expected_days([1..7], obj)
       end
       @two_ranges.each do |obj|
          assert_expected_days([1..3, 5..7], obj)
       end
    end

    def test_bad_args
       INVALID_ARGS.each do |args|
          assert_raise(ArgumentError) { DayRange.new(*args) }
       end
    end

    def test_to_s
       @one_range.each do |obj|
          assert_equal('Mon-Sun', obj.to_s)
       end
       @two_ranges.each do |obj|
          assert_equal('Mon-Wed, Fri-Sun', obj.to_s)
       end
    end

    def test_to_a
       @one_range.each do |obj|
          assert_equal((1..7).to_a, obj.to_a)
       end
       @two_ranges.each do |obj|
          assert_equal([1, 2, 3, 5, 6, 7], obj.to_a)
       end
    end

    private

    def assert_expected_days(expected, day_range)
      assert_equal(expected, day_range.instance_variable_get(:@days))
    end

end

James Edward Gray II

···

On Aug 30, 2006, at 11:55 AM, Morton Goldberg wrote:

Could you post an example showing how setup and teardown could be used to avoid the trap I fell into?

quoth the darren kirby:

quoth the Lyle Johnson:
> > Does anybody have some idea why my posts are getting garbage chars
> > ('=20' and '=3D') added on the archive site:
>
> Someone smarter than me will have to fill in the blanks, but I'm
> pretty sure it has to do with sending e-mails to the list as HTML
> instead of plain text e-mails.

But I _never_ send HTML mails...ever...

-d

Sorry to reply to self.

I had a look at my own sent messages using 'view source' in Kmail. The extra
chars, whatever they are there too. It got me thinking...I turned off signing
my messages, but it seems to have done nothing. Still there. Nothing in my
Kmail settings seems relevant. Could it be the character encoding?

My messages aren't appearing as HTML to you all are they? My settings say
not...

Sorry again for OT chatter...

Thanks,
-d

···

> On 8/28/06, darren kirby <bulliver@badcomputer.org> wrote:

--
darren kirby :: Part of the problem since 1976 :: http://badcomputer.org
"...the number of UNIX installations has grown to 10, with more expected..."
- Dennis Ritchie and Ken Thompson, June 1972

Thanks for trying to help, but if I understand how setup functions, your modification defeats my requirement that the test objects be created once and once only. As I understand it, setup will run before EACH test and the test objects will be created over and over again, even for test_bad_args where they are not needed at all.

Renaming test_valid_args to test_args is not only simpler, it meets the requirement of parsimony of test objects.

Regards, Morton

···

On Aug 30, 2006, at 1:27 PM, James Edward Gray II wrote:

On Aug 30, 2006, at 11:55 AM, Morton Goldberg wrote:

Could you post an example showing how setup and teardown could be used to avoid the trap I fell into?

Here's my modification to your tests to get them working with setup():

class TestDayRange < Test::Unit::TestCase

   # All these produce @days == [1..7].
   ONE_RANGE = [
      %w[mon tue wed thu fri sat sun],
      %w[monDay tuesday Wednesday Thursday friDAY saturday SUNDAY],
      %w[mon-fri sat-sun],
      %w[4-7 1-3],
      (1..7).to_a.reverse,
      [4, 7, 6, 5, 4, 1, 2, 1, 2, 3, 3, 7, 6, 5],
   ]

   # Both these produce @days == [1..3, 5..7].
   TWO_RANGES = [
      %w[mon-mon tue-tue wed-wed fri-sun],
      [1, 2, 'mon-wed', 'friday', 6, 7]
   ]

   INVALID_ARGS = [
      [1, 2, 'foo'],
      %w[foo-bar],
      %w[sat-mon],
      (0..7).to_a.reverse,
      (1..8).to_a
   ]

   def setup
     assert_nothing_raised(ArgumentError) do
       @one_range = ONE_RANGE.map { |args| DayRange.new(*args) }
     end
     assert_nothing_raised(ArgumentError) do
       @two_ranges = TWO_RANGES.map { |args| DayRange.new(*args) }
     end
   end

   def test_valid_args
      @one_range.each do |obj|
         assert_expected_days([1..7], obj)
      end
      @two_ranges.each do |obj|
         assert_expected_days([1..3, 5..7], obj)
      end
   end

   def test_bad_args
      INVALID_ARGS.each do |args|
         assert_raise(ArgumentError) { DayRange.new(*args) }
      end
   end

   def test_to_s
      @one_range.each do |obj|
         assert_equal('Mon-Sun', obj.to_s)
      end
      @two_ranges.each do |obj|
         assert_equal('Mon-Wed, Fri-Sun', obj.to_s)
      end
   end

   def test_to_a
      @one_range.each do |obj|
         assert_equal((1..7).to_a, obj.to_a)
      end
      @two_ranges.each do |obj|
         assert_equal([1, 2, 3, 5, 6, 7], obj.to_a)
      end
   end

   private

   def assert_expected_days(expected, day_range)
     assert_equal(expected, day_range.instance_variable_get(:@days))
   end

end

James Edward Gray II

darren kirby wrote:

My messages aren't appearing as HTML to you all are they? My settings say not...

Nope. But they are encoded in iso-8859-6, which I haven't covered in Thunderbird font settings and the default Courier New ticks me off. What language set is that anyway? (I should send a hint to Mozilla people that font substitution is a Good Thing, and that some people just want to use one font for everything without clicking through all 20 language groups, thankyouverymuch, since they can't read one bit Chinese or Indian anyway.)

David Vallner

Thanks for trying to help, but if I understand how setup functions, your modification defeats my requirement that the test objects be created once and once only. As I understand it, setup will run before EACH test and the test objects will be created over and over again, even for test_bad_args where they are not needed at all.

My opinion is that this is a very good thing. Tests should work in isolation as much as possible.

You are testing one small part of the whole, to verify correctness. When you start sharing details between the tests you tie them right back into a complete system again and that's what unit testing is trying to avoid. Requiring that tests be run in a given order is just too fragile.

When I do require that some tests work sequentially, I do it like this:

   def test_me_first
     # ...
   end

   def test_me_second
     test_me_first

     # ...
   end

I only feel safe counting on the order when I get to say what the order is, using the above.

This does extra work, as you complained about with my implementation of setup(). Ruby doesn't mind the exercise though and as long as she can do it quickly, I don't either. Besides, it shoots that test counter right on up! (Makes me feel great, "These tests are just flying by...")

Renaming test_valid_args to test_args is not only simpler, it meets the requirement of parsimony of test objects.

I'm very convinced this is the wrong way to go. You are relying on an implementation detail of Test::Unit here, that could change at anytime. That could cause your tests to start magically failing at some point down the road.

I also don't believe you have the order correct. Your tests run as expected on my box with no modification to the method names. I haven't gone into the source of Test::Unit to determine why this is, but it could be that the methods are hashed in which case you can't count on any order at all.

James Edward Gray II

···

On Aug 30, 2006, at 2:40 PM, Morton Goldberg wrote: