icu_tournament 1.4.3 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/icu_tournament/player.rb +9 -1
- data/lib/icu_tournament/result.rb +26 -11
- data/lib/icu_tournament/tournament.rb +49 -7
- data/lib/icu_tournament/version.rb +1 -1
- data/spec/player_spec.rb +16 -0
- data/spec/result_spec.rb +53 -50
- data/spec/tournament_spec.rb +32 -3
- metadata +4 -4
| @@ -181,11 +181,19 @@ module ICU | |
| 181 181 | 
             
                  end
         | 
| 182 182 | 
             
                end
         | 
| 183 183 |  | 
| 184 | 
            -
                # Lookup a result by round number.
         | 
| 184 | 
            +
                # Lookup a result by round number and return it (or nil if there is no such result).
         | 
| 185 185 | 
             
                def find_result(round)
         | 
| 186 186 | 
             
                  @results.find { |r| r.round == round }
         | 
| 187 187 | 
             
                end
         | 
| 188 188 |  | 
| 189 | 
            +
                # Lookup a result by round number, remove it and return it (or return nil if there is no such result).
         | 
| 190 | 
            +
                def remove_result(round)
         | 
| 191 | 
            +
                  result = find_result(round)
         | 
| 192 | 
            +
                  return unless result
         | 
| 193 | 
            +
                  @results.delete(result)
         | 
| 194 | 
            +
                  result
         | 
| 195 | 
            +
                end
         | 
| 196 | 
            +
             | 
| 189 197 | 
             
                # Return the player's total points.
         | 
| 190 198 | 
             
                def points
         | 
| 191 199 | 
             
                  @results.inject(0.0) { |t, r| t += r.points }
         | 
| @@ -66,6 +66,19 @@ module ICU | |
| 66 66 | 
             
              #
         | 
| 67 67 | 
             
              # The _points_ read-only accessor always returns a floating point number: either 0.0, 0.5 or 1.0.
         | 
| 68 68 | 
             
              #
         | 
| 69 | 
            +
              # Two results are <em>eql?</em> if all there attributes are the same, unless exceptions are specified.
         | 
| 70 | 
            +
              #
         | 
| 71 | 
            +
              #   r  = ICU::Result.new(1, 1, 'W', :opponent => 2)
         | 
| 72 | 
            +
              #   r1 = ICU::Result.new(1, 1, 'W', :opponent => 2)
         | 
| 73 | 
            +
              #   r2 = ICU::Result.new(1, 1, 'W', :opponent => 2, :rateable => false)
         | 
| 74 | 
            +
              #   r3 = ICU::Result.new(1, 1, 'L', :opponent => 2, :rateable => false)
         | 
| 75 | 
            +
              #
         | 
| 76 | 
            +
              #   r.eql?(r1)                                   # => true
         | 
| 77 | 
            +
              #   r.eql?(r2)                                   # => false
         | 
| 78 | 
            +
              #   r.eql?(r3)                                   # => false
         | 
| 79 | 
            +
              #   r.eql?(r2, :except => :rateable)             # => true
         | 
| 80 | 
            +
              #   r.eql?(r3, :except => [:rateable, :score])   # => true
         | 
| 81 | 
            +
              #
         | 
| 69 82 | 
             
              class Result
         | 
| 70 83 | 
             
                extend ICU::Accessor
         | 
| 71 84 | 
             
                attr_positive :round
         | 
| @@ -167,20 +180,22 @@ module ICU | |
| 167 180 | 
             
                  "R#{@round}P#{@player}O#{@opponent || '-'}#{@score}#{@colour || '-'}#{@rateable ? 'R' : 'U'}"
         | 
| 168 181 | 
             
                end
         | 
| 169 182 |  | 
| 170 | 
            -
                #  | 
| 171 | 
            -
                def  | 
| 183 | 
            +
                # Equality. True if all attributes equal, exceptions allowed.
         | 
| 184 | 
            +
                def eql?(other, opt={})
         | 
| 185 | 
            +
                  return true if equal?(other)
         | 
| 172 186 | 
             
                  return unless other.is_a? Result
         | 
| 173 | 
            -
                   | 
| 174 | 
            -
             | 
| 187 | 
            +
                  except = Hash.new
         | 
| 188 | 
            +
                  if opt[:except]
         | 
| 189 | 
            +
                    if opt[:except].is_a?(Array)
         | 
| 190 | 
            +
                      opt[:except].each { |x| except[x.to_sym] = true }
         | 
| 191 | 
            +
                    else
         | 
| 192 | 
            +
                      except[opt[:except].to_sym] = true
         | 
| 193 | 
            +
                    end
         | 
| 194 | 
            +
                  end
         | 
| 195 | 
            +
                  [:round, :player, :opponent, :colour, :score, :rateable].each do |m|
         | 
| 196 | 
            +
                    return false unless except[m] || self.send(m) == other.send(m)
         | 
| 175 197 | 
             
                  end
         | 
| 176 198 | 
             
                  true
         | 
| 177 199 | 
             
                end
         | 
| 178 | 
            -
             | 
| 179 | 
            -
                # Strict equality. True if the there's loose equality and also the rateablity is the same.
         | 
| 180 | 
            -
                def eql?(other)
         | 
| 181 | 
            -
                  return true if equal?(other)
         | 
| 182 | 
            -
                  return false unless self == other
         | 
| 183 | 
            -
                  self.rateable == other.rateable
         | 
| 184 | 
            -
                end
         | 
| 185 200 | 
             
              end
         | 
| 186 201 | 
             
            end
         | 
| @@ -55,6 +55,22 @@ module ICU | |
| 55 55 | 
             
              #   t.add_result(ICU::Result.new(3, 20, 'L', :opponent => 10, :colour => 'W'))
         | 
| 56 56 | 
             
              #   t.add_result(ICU::Result.new(3, 10, 'D', :opponent => 20, :colour => 'B'))  # would raise an exception
         | 
| 57 57 | 
             
              #
         | 
| 58 | 
            +
              # == Asymmetric Scores
         | 
| 59 | 
            +
              #
         | 
| 60 | 
            +
              # There is one exception to the rule that two corresponding results must be consistent:
         | 
| 61 | 
            +
              # if both results are unrateable then the two scores need not sum to 1. The commonest case
         | 
| 62 | 
            +
              # this caters for is probably that of a double default. To create such asymmetric results
         | 
| 63 | 
            +
              # you must add the result from both players' perspectives. For example:
         | 
| 64 | 
            +
              #
         | 
| 65 | 
            +
              #   t.add_result(ICU::Result.new(3, 20, 'L', :opponent => 10, :rateable => false))
         | 
| 66 | 
            +
              #   t.add_result(ICU::Result.new(3, 10, 'L', :opponent => 20, :rateable => false))
         | 
| 67 | 
            +
              #
         | 
| 68 | 
            +
              # After the first _add_result_ the two results are, as usual, consistent (in particular, the loss for player 20
         | 
| 69 | 
            +
              # is balanced by a win for player 10). However, the second _add_result_, which asserts player 10 lost, does not cause
         | 
| 70 | 
            +
              # an exception. It would have done if the results had been rateable but, because they are not, the scores are
         | 
| 71 | 
            +
              # allowed to add up to something other than 1.0 (in this case, zero) and the effect of the second call to _add_result_
         | 
| 72 | 
            +
              # is merely to adjust the score of player 10 from a win to a loss (while maintaining the loss for player 20).
         | 
| 73 | 
            +
              #
         | 
| 58 74 | 
             
              # See ICU::Player and ICU::Result for more details about players and results.
         | 
| 59 75 | 
             
              #
         | 
| 60 76 | 
             
              # == Validation
         | 
| @@ -67,11 +83,11 @@ module ICU | |
| 67 83 | 
             
              # Validations checks that:
         | 
| 68 84 | 
             
              #
         | 
| 69 85 | 
             
              # * there are at least two players
         | 
| 70 | 
            -
              # *  | 
| 71 | 
            -
              # *  | 
| 86 | 
            +
              # * result round numbers are consistent (no more than one game per player per round)
         | 
| 87 | 
            +
              # * corresponding results are consistent (although they may have asymmetric scores if unrateable, as previously desribed)
         | 
| 72 88 | 
             
              # * the tournament dates (start, finish, round dates), if there are any, are consistent
         | 
| 73 | 
            -
              # *  | 
| 74 | 
            -
              # * there are no players with duplicate ICU IDs or duplicate FIDE IDs
         | 
| 89 | 
            +
              # * player ranks are consistent with their scores
         | 
| 90 | 
            +
              # * there are no players with duplicate \ICU IDs or duplicate \FIDE IDs
         | 
| 75 91 | 
             
              #
         | 
| 76 92 | 
             
              # Side effects of calling <em>validate!</em> or _invalid_ include:
         | 
| 77 93 | 
             
              #
         | 
| @@ -87,7 +103,7 @@ module ICU | |
| 87 103 | 
             
              #
         | 
| 88 104 | 
             
              #   t.validate!(:type => 'ForeignCSV')
         | 
| 89 105 | 
             
              #
         | 
| 90 | 
            -
              # which, amongst other tests, checks that there is at least one player with an ICU number and
         | 
| 106 | 
            +
              # which, amongst other tests, checks that there is at least one player with an \ICU number and
         | 
| 91 107 | 
             
              # that all such players have a least one game against a FIDE rated opponent. This is an example
         | 
| 92 108 | 
             
              # of a specialized check that is only appropriate for a particular serializer. If it raises an
         | 
| 93 109 | 
             
              # exception then the tournament cannot be serialized that way.
         | 
| @@ -278,6 +294,7 @@ module ICU | |
| 278 294 | 
             
                  raise "invalid result" unless result.class == ICU::Result
         | 
| 279 295 | 
             
                  raise "result round number (#{result.round}) inconsistent with number of tournament rounds" if @rounds && result.round > @rounds
         | 
| 280 296 | 
             
                  raise "player number (#{result.player}) does not exist" unless @player[result.player]
         | 
| 297 | 
            +
                  return if add_asymmetric_result?(result)
         | 
| 281 298 | 
             
                  @player[result.player].add_result(result)
         | 
| 282 299 | 
             
                  if result.opponent
         | 
| 283 300 | 
             
                    raise "opponent number (#{result.opponent}) does not exist" unless @player[result.opponent]
         | 
| @@ -418,14 +435,15 @@ module ICU | |
| 418 435 | 
             
                      raise "duplicate FIDE IDs, players #{p.num} and #{fide_ids[p.fide_id]}" if fide_ids[p.fide_id]
         | 
| 419 436 | 
             
                      fide_ids[p.fide_id] = num
         | 
| 420 437 | 
             
                    end
         | 
| 421 | 
            -
                     | 
| 438 | 
            +
                    return if p.results.size == 0
         | 
| 422 439 | 
             
                    p.results.each do |r|
         | 
| 423 440 | 
             
                      next unless r.opponent
         | 
| 424 441 | 
             
                      opponent = @player[r.opponent]
         | 
| 425 442 | 
             
                      raise "opponent #{r.opponent} of player #{num} is not in the tournament" unless opponent
         | 
| 426 443 | 
             
                      o = opponent.find_result(r.round)
         | 
| 427 444 | 
             
                      raise "opponent #{r.opponent} of player #{num} has no result in round #{r.round}" unless o
         | 
| 428 | 
            -
                       | 
| 445 | 
            +
                      score = r.rateable || o.rateable ? [] : [:score]
         | 
| 446 | 
            +
                      raise "opponent's result (#{o.inspect}) is not reverse of player's (#{r.inspect})" unless o.reverse.eql?(r, :except => score)
         | 
| 429 447 | 
             
                    end
         | 
| 430 448 | 
             
                  end
         | 
| 431 449 | 
             
                end
         | 
| @@ -587,5 +605,29 @@ module ICU | |
| 587 605 | 
             
                    else player.name
         | 
| 588 606 | 
             
                  end
         | 
| 589 607 | 
             
                end
         | 
| 608 | 
            +
             | 
| 609 | 
            +
                # Detect when an asymmetric result is about to be added, make the appropriate adjustment and return true.
         | 
| 610 | 
            +
                # The conditions for an asymric result are: the player's result already exists, the opponent's result
         | 
| 611 | 
            +
                # already exists, both results are unrateable and the reverse of one result is equal to the other, apart
         | 
| 612 | 
            +
                # from score. In this case all we do update score of the player's result, thus allowing two results whose
         | 
| 613 | 
            +
                # total score does not add to 1.
         | 
| 614 | 
            +
                def add_asymmetric_result?(result)
         | 
| 615 | 
            +
                  return false if result.rateable
         | 
| 616 | 
            +
             | 
| 617 | 
            +
                  plr = @player[result.player]
         | 
| 618 | 
            +
                  opp = @player[result.opponent]
         | 
| 619 | 
            +
                  return false unless plr && opp
         | 
| 620 | 
            +
             | 
| 621 | 
            +
                  plr_result = plr.find_result(result.round)
         | 
| 622 | 
            +
                  opp_result = opp.find_result(result.round)
         | 
| 623 | 
            +
                  return false unless plr_result && opp_result
         | 
| 624 | 
            +
                  return false if plr_result.rateable || opp_result.rateable
         | 
| 625 | 
            +
                  
         | 
| 626 | 
            +
                  reversed = plr_result.reverse
         | 
| 627 | 
            +
                  return false unless reversed && reversed.eql?(opp_result, :except => :score)
         | 
| 628 | 
            +
             | 
| 629 | 
            +
                  plr_result.score = result.score
         | 
| 630 | 
            +
                  true
         | 
| 631 | 
            +
                end
         | 
| 590 632 | 
             
              end
         | 
| 591 633 | 
             
            end
         | 
    
        data/spec/player_spec.rb
    CHANGED
    
    | @@ -247,6 +247,22 @@ module ICU | |
| 247 247 | 
             
                  end
         | 
| 248 248 | 
             
                end
         | 
| 249 249 |  | 
| 250 | 
            +
                context "removing a result" do
         | 
| 251 | 
            +
                  before(:all) do
         | 
| 252 | 
            +
                    @p = Player.new('Mark', 'Orr', 1)
         | 
| 253 | 
            +
                    @p.add_result(Result.new(1, 1, 'W', :opponent => 37, :score => 'W', :colour => 'W'))
         | 
| 254 | 
            +
                    @p.add_result(Result.new(2, 1, 'W', :opponent => 13, :score => 'W', :colour => 'B'))
         | 
| 255 | 
            +
                    @p.add_result(Result.new(3, 1, 'W', :opponent => 7,  :score => 'D', :colour => 'W'))
         | 
| 256 | 
            +
                  end
         | 
| 257 | 
            +
             | 
| 258 | 
            +
                  it "should find and remove a result by round number" do
         | 
| 259 | 
            +
                    result = @p.remove_result(1)
         | 
| 260 | 
            +
                    result.inspect.should == "R1P1O37WWR"
         | 
| 261 | 
            +
                    @p.results.size.should == 2
         | 
| 262 | 
            +
                    @p.results.map(&:round).join("|").should == "2|3"
         | 
| 263 | 
            +
                  end
         | 
| 264 | 
            +
                end
         | 
| 265 | 
            +
             | 
| 250 266 | 
             
                context "merge" do
         | 
| 251 267 | 
             
                  before(:each) do
         | 
| 252 268 | 
             
                    @p1 = Player.new('Mark', 'Orr', 1, :id => 1350)
         | 
    
        data/spec/result_spec.rb
    CHANGED
    
    | @@ -8,7 +8,7 @@ module ICU | |
| 8 8 | 
             
                    lambda { Result.new(3, 5, 'W', :opponent => 11, :colour => 'W') }.should_not raise_error
         | 
| 9 9 | 
             
                  end
         | 
| 10 10 | 
             
                end
         | 
| 11 | 
            -
             | 
| 11 | 
            +
             | 
| 12 12 | 
             
                context "round number" do
         | 
| 13 13 | 
             
                  it "should be a positive integer" do
         | 
| 14 14 | 
             
                    lambda { Result.new(-2, 2, 0) }.should raise_error(/invalid positive integer/)
         | 
| @@ -17,7 +17,7 @@ module ICU | |
| 17 17 | 
             
                    Result.new(' 3 ', 2, 0).round.should == 3
         | 
| 18 18 | 
             
                  end
         | 
| 19 19 | 
             
                end
         | 
| 20 | 
            -
             | 
| 20 | 
            +
             | 
| 21 21 | 
             
                context "player number" do
         | 
| 22 22 | 
             
                  it "should be an integer" do
         | 
| 23 23 | 
             
                    lambda { Result.new(1, '  ', 0) }.should raise_error(/invalid integer/)
         | 
| @@ -27,7 +27,7 @@ module ICU | |
| 27 27 | 
             
                    Result.new(1, " 9 ", 0).player.should == 9
         | 
| 28 28 | 
             
                  end
         | 
| 29 29 | 
             
                end
         | 
| 30 | 
            -
             | 
| 30 | 
            +
             | 
| 31 31 | 
             
                context "score" do
         | 
| 32 32 | 
             
                  [1, 1.0, 'W', 'w', '+'].each do |score|
         | 
| 33 33 | 
             
                    it "should be 'W' for #{score}, #{score.class}" do
         | 
| @@ -55,7 +55,7 @@ module ICU | |
| 55 55 | 
             
                    Result.new(1, 2, 'D').points.should == 0.5
         | 
| 56 56 | 
             
                  end
         | 
| 57 57 | 
             
                end
         | 
| 58 | 
            -
             | 
| 58 | 
            +
             | 
| 59 59 | 
             
                context "colour" do
         | 
| 60 60 | 
             
                  it "should be 'W' or 'B' or nil (unknown)" do
         | 
| 61 61 | 
             
                    Result.new(4, 1, 0).colour.should be_nil
         | 
| @@ -66,7 +66,7 @@ module ICU | |
| 66 66 | 
             
                    lambda { Result.new(4, 1, 0, :colour => 'red') }.should raise_error(/invalid colour/)
         | 
| 67 67 | 
             
                  end
         | 
| 68 68 | 
             
                end
         | 
| 69 | 
            -
             | 
| 69 | 
            +
             | 
| 70 70 | 
             
                context "opponent number" do
         | 
| 71 71 | 
             
                  it "should be nil (the default) or an integer" do
         | 
| 72 72 | 
             
                    Result.new(4, 1, 0).opponent.should be_nil
         | 
| @@ -78,27 +78,27 @@ module ICU | |
| 78 78 | 
             
                    Result.new(4, 1, 0, :opponent => ' 10 ').opponent.should == 10
         | 
| 79 79 | 
             
                    lambda { Result.new(4, 1, 0, :opponent => 'X') }.should raise_error(/invalid opponent number/)
         | 
| 80 80 | 
             
                  end
         | 
| 81 | 
            -
             | 
| 81 | 
            +
             | 
| 82 82 | 
             
                  it "should be different to player number" do
         | 
| 83 83 | 
             
                    lambda { Result.new(4, 1, 0, :opponent => 1) }.should raise_error(/opponent .* player .* different/)
         | 
| 84 84 | 
             
                  end
         | 
| 85 85 | 
             
                end
         | 
| 86 | 
            -
             | 
| 86 | 
            +
             | 
| 87 87 | 
             
                context "rateable flag" do
         | 
| 88 88 | 
             
                  it "should default to false if there is no opponent" do
         | 
| 89 89 | 
             
                    Result.new(4, 1, 0).rateable.should be_false
         | 
| 90 90 | 
             
                  end
         | 
| 91 | 
            -
             | 
| 91 | 
            +
             | 
| 92 92 | 
             
                  it "should default to true if there is an opponent" do
         | 
| 93 93 | 
             
                    Result.new(4, 1, 0, :opponent => 10).rateable.should be_true
         | 
| 94 94 | 
             
                  end
         | 
| 95 | 
            -
             | 
| 95 | 
            +
             | 
| 96 96 | 
             
                  it "should change if an opponent is added" do
         | 
| 97 97 | 
             
                    r = Result.new(4, 1, 0)
         | 
| 98 98 | 
             
                    r.opponent = 5;
         | 
| 99 99 | 
             
                    r.rateable.should be_true
         | 
| 100 100 | 
             
                  end
         | 
| 101 | 
            -
             | 
| 101 | 
            +
             | 
| 102 102 | 
             
                  it "should be settable to false from the constructor" do
         | 
| 103 103 | 
             
                    Result.new(4, 1, 0, :opponent => 10, :rateable => false).rateable.should be_false
         | 
| 104 104 | 
             
                  end
         | 
| @@ -108,43 +108,43 @@ module ICU | |
| 108 108 | 
             
                    r.rateable= false
         | 
| 109 109 | 
             
                    r.rateable.should be_false
         | 
| 110 110 | 
             
                  end
         | 
| 111 | 
            -
             | 
| 111 | 
            +
             | 
| 112 112 | 
             
                  it "should not be settable to true if there is no opponent" do
         | 
| 113 113 | 
             
                    r = Result.new(4, 1, 0)
         | 
| 114 114 | 
             
                    r.rateable= true
         | 
| 115 115 | 
             
                    r.rateable.should be_false
         | 
| 116 116 | 
             
                  end
         | 
| 117 117 | 
             
                end
         | 
| 118 | 
            -
             | 
| 118 | 
            +
             | 
| 119 119 | 
             
                context "reversed result" do
         | 
| 120 120 | 
             
                  it "should be nil if there is no opponent" do
         | 
| 121 121 | 
             
                    Result.new(4, 1, 0).reverse.should be_nil
         | 
| 122 122 | 
             
                  end
         | 
| 123 | 
            -
             | 
| 123 | 
            +
             | 
| 124 124 | 
             
                  it "should have player and opponent swapped" do
         | 
| 125 125 | 
             
                    r = Result.new(4, 1, 0, :opponent => 2).reverse
         | 
| 126 126 | 
             
                    r.player.should == 2
         | 
| 127 127 | 
             
                    r.opponent.should == 1
         | 
| 128 128 | 
             
                  end
         | 
| 129 | 
            -
             | 
| 129 | 
            +
             | 
| 130 130 | 
             
                  it "should have reversed result" do
         | 
| 131 131 | 
             
                    Result.new(4, 1, 0, :opponent => 2).reverse.score.should == 'W'
         | 
| 132 132 | 
             
                    Result.new(4, 1, 1, :opponent => 2).reverse.score.should == 'L'
         | 
| 133 133 | 
             
                    Result.new(4, 1, '=', :opponent => 2).reverse.score.should == 'D'
         | 
| 134 134 | 
             
                  end
         | 
| 135 | 
            -
             | 
| 135 | 
            +
             | 
| 136 136 | 
             
                  it "should preserve rateability" do
         | 
| 137 137 | 
             
                    Result.new(4, 1, 0, :opponent => 2).reverse.rateable.should be_true
         | 
| 138 138 | 
             
                    Result.new(4, 1, 0, :opponent => 2, :rateable => false).reverse.rateable.should be_false
         | 
| 139 139 | 
             
                  end
         | 
| 140 140 | 
             
                end
         | 
| 141 | 
            -
             | 
| 141 | 
            +
             | 
| 142 142 | 
             
                context "renumber the player numbers in a result" do
         | 
| 143 143 | 
             
                  before(:each) do
         | 
| 144 144 | 
             
                    @r1 = Result.new(1, 4, 0)
         | 
| 145 145 | 
             
                    @r2 = Result.new(2, 3, 1, :opponent => 4, :color => 'B')
         | 
| 146 146 | 
             
                  end
         | 
| 147 | 
            -
             | 
| 147 | 
            +
             | 
| 148 148 | 
             
                  it "should renumber successfully if the map has the relevant player numbers" do
         | 
| 149 149 | 
             
                    map = { 4 => 1, 3 => 2 }
         | 
| 150 150 | 
             
                    @r1.renumber(map).player.should == 1
         | 
| @@ -152,50 +152,53 @@ module ICU | |
| 152 152 | 
             
                    @r1.opponent.should be_nil
         | 
| 153 153 | 
             
                    @r2.opponent.should == 1
         | 
| 154 154 | 
             
                  end
         | 
| 155 | 
            -
             | 
| 155 | 
            +
             | 
| 156 156 | 
             
                  it "should raise exception if a player number is not in the map" do
         | 
| 157 157 | 
             
                    lambda { @r1.renumber({ 5 => 1, 3 => 2 }) }.should raise_error(/player.*4.*not found/)
         | 
| 158 158 | 
             
                  end
         | 
| 159 159 | 
             
                end
         | 
| 160 | 
            -
             | 
| 161 | 
            -
                context " | 
| 160 | 
            +
             | 
| 161 | 
            +
                context "equality" do
         | 
| 162 162 | 
             
                  before(:each) do
         | 
| 163 | 
            +
                    @r  = Result.new(1, 1, 0, :opponent => 2, :colour => 'W')
         | 
| 163 164 | 
             
                    @r1 = Result.new(1, 1, 0, :opponent => 2, :colour => 'W')
         | 
| 164 | 
            -
                    @r2 = Result.new(1, 1, 0, :opponent => 2, :colour => 'W')
         | 
| 165 | 
            +
                    @r2 = Result.new(1, 1, 0, :opponent => 2, :colour => 'W', :rateable => false)
         | 
| 165 166 | 
             
                    @r3 = Result.new(2, 1, 0, :opponent => 2, :colour => 'W')
         | 
| 166 | 
            -
                    @r4 = Result.new(1,  | 
| 167 | 
            -
                    @r5 = Result.new( | 
| 168 | 
            -
             | 
| 169 | 
            -
             | 
| 170 | 
            -
                   | 
| 171 | 
            -
             | 
| 172 | 
            -
             | 
| 173 | 
            -
                    (@ | 
| 174 | 
            -
                    (@ | 
| 175 | 
            -
             | 
| 176 | 
            -
             | 
| 177 | 
            -
                   | 
| 178 | 
            -
             | 
| 179 | 
            -
             | 
| 180 | 
            -
                    (@ | 
| 181 | 
            -
                    (@ | 
| 182 | 
            -
                    (@ | 
| 167 | 
            +
                    @r4 = Result.new(1, 1, 0, :opponent => 2, :colour => 'B')
         | 
| 168 | 
            +
                    @r5 = Result.new(2, 1, 1, :opponent => 3, :colour => 'B')
         | 
| 169 | 
            +
                  end
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                  it "should only be equal if everything is the same" do
         | 
| 172 | 
            +
                    @r.eql?(@r).should be_true
         | 
| 173 | 
            +
                    @r.eql?(@r1).should be_true
         | 
| 174 | 
            +
                    @r.eql?(@r2).should be_false
         | 
| 175 | 
            +
                    @r.eql?(@r3).should be_false
         | 
| 176 | 
            +
                    @r.eql?(@r4).should be_false
         | 
| 177 | 
            +
                    @r.eql?(@r5).should be_false
         | 
| 178 | 
            +
                  end
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                  it "exceptions are allowed" do
         | 
| 181 | 
            +
                    @r.eql?(@r2, :except => :rateable).should be_true
         | 
| 182 | 
            +
                    @r.eql?(@r3, :except => "round").should be_true
         | 
| 183 | 
            +
                    @r.eql?(@r4, :except => :colour).should be_true
         | 
| 184 | 
            +
                    @r.eql?(@r5, :except => [:colour, :round, :score, "opponent"]).should be_true
         | 
| 183 185 | 
             
                  end
         | 
| 184 186 | 
             
                end
         | 
| 185 | 
            -
             | 
| 186 | 
            -
                context " | 
| 187 | 
            +
             | 
| 188 | 
            +
                context "equality documentation example" do
         | 
| 187 189 | 
             
                  before(:each) do
         | 
| 188 | 
            -
                    @ | 
| 189 | 
            -
                    @ | 
| 190 | 
            -
                    @ | 
| 191 | 
            -
                    @ | 
| 190 | 
            +
                    @r  = ICU::Result.new(1, 1, 'W', :opponent => 2)
         | 
| 191 | 
            +
                    @r1 = ICU::Result.new(1, 1, 'W', :opponent => 2)
         | 
| 192 | 
            +
                    @r2 = ICU::Result.new(1, 1, 'W', :opponent => 2, :rateable => false)
         | 
| 193 | 
            +
                    @r3 = ICU::Result.new(1, 1, 'L', :opponent => 2, :rateable => false)
         | 
| 192 194 | 
             
                  end
         | 
| 193 | 
            -
             | 
| 194 | 
            -
                  it "should  | 
| 195 | 
            -
                    @ | 
| 196 | 
            -
                    @ | 
| 197 | 
            -
                    @ | 
| 198 | 
            -
                    @ | 
| 195 | 
            +
             | 
| 196 | 
            +
                  it "should be correct" do
         | 
| 197 | 
            +
                    @r.eql?(@r1).should be_true
         | 
| 198 | 
            +
                    @r.eql?(@r2).should be_false
         | 
| 199 | 
            +
                    @r.eql?(@r3).should be_false
         | 
| 200 | 
            +
                    @r.eql?(@r2, :except => :rateable).should be_true
         | 
| 201 | 
            +
                    @r.eql?(@r3, :except => [:rateable, :score]).should be_true
         | 
| 199 202 | 
             
                  end
         | 
| 200 203 | 
             
                end
         | 
| 201 204 | 
             
              end
         | 
    
        data/spec/tournament_spec.rb
    CHANGED
    
    | @@ -32,8 +32,9 @@ module ICU | |
| 32 32 | 
             
            042 2009-11-09
         | 
| 33 33 | 
             
            001   10      Fischer,Bobby                                                      1.5    1    30 w =              20 b 1
         | 
| 34 34 | 
             
            001   20      Kasparov,Garry                                                     1.0    2              30 b 1    10 w 0
         | 
| 35 | 
            -
            001   30      Orr,Mark                                                           0.5    3    10 b =    20 w 0          
         | 
| 35 | 
            +
            001   30      Orr,Mark                                                           0.5    3    10 b =    20 w 0          #
         | 
| 36 36 | 
             
            EOS
         | 
| 37 | 
            +
                    @s.sub!(/#/, '')
         | 
| 37 38 | 
             
                  end
         | 
| 38 39 |  | 
| 39 40 | 
             
                  it "should serialize to Krause" do
         | 
| @@ -324,7 +325,7 @@ EOS | |
| 324 325 | 
             
                  it "can be added one at a time" do
         | 
| 325 326 | 
             
                    @t.add_result(Result.new(1, 1, 'W', :opponent => 2))
         | 
| 326 327 | 
             
                    @t.add_result(Result.new(2, 2, 'D', :opponent => 3))
         | 
| 327 | 
            -
                    @t.add_result(Result.new(3, 3, 'L', :opponent => 1))
         | 
| 328 | 
            +
                    @t.add_result(Result.new(3, 3, 'L', :opponent => 1, :rateable => false))
         | 
| 328 329 | 
             
                    @mark.results.size.should == 2
         | 
| 329 330 | 
             
                    @mark.points.should == 2.0
         | 
| 330 331 | 
             
                    @gary.results.size.should == 2
         | 
| @@ -333,10 +334,11 @@ EOS | |
| 333 334 | 
             
                    @boby.points.should == 0.5
         | 
| 334 335 | 
             
                  end
         | 
| 335 336 |  | 
| 336 | 
            -
                  it "asymmetric  | 
| 337 | 
            +
                  it "results with asymmetric scores cannot be added unless both results are unrateable" do
         | 
| 337 338 | 
             
                    @t.add_result(Result.new(1, 1, 'W', :opponent => 2))
         | 
| 338 339 | 
             
                    lambda { @t.add_result(Result.new(1, 2, 'D', :opponent => 1)) }.should raise_error(/result.*match/)
         | 
| 339 340 | 
             
                    lambda { @t.add_result(Result.new(1, 2, 'L', :opponent => 1, :rateable => false)) }.should raise_error(/result.*match/)
         | 
| 341 | 
            +
                    lambda { @t.add_result(Result.new(3, 3, 'L', :opponent => 1, :rateable => false)) }.should_not raise_error
         | 
| 340 342 | 
             
                  end
         | 
| 341 343 |  | 
| 342 344 | 
             
                  it "should have a defined player" do
         | 
| @@ -350,6 +352,13 @@ EOS | |
| 350 352 | 
             
                  it "should be consistent with the tournament's number of rounds" do
         | 
| 351 353 | 
             
                    lambda { @t.add_result(Result.new(4, 1, 'W', :opponent => 2)) }.should raise_error(/round/)
         | 
| 352 354 | 
             
                  end
         | 
| 355 | 
            +
             | 
| 356 | 
            +
                  it "documentation example should ne correct" do
         | 
| 357 | 
            +
                    @t.add_result(ICU::Result.new(3, 2, 'L', :opponent => 1, :rateable => false))
         | 
| 358 | 
            +
                    @t.add_result(ICU::Result.new(3, 1, 'L', :opponent => 2, :rateable => false))
         | 
| 359 | 
            +
                    @t.player(1).results.first.points.should == 0.0
         | 
| 360 | 
            +
                    @t.player(2).results.first.points.should == 0.0
         | 
| 361 | 
            +
                  end
         | 
| 353 362 | 
             
                end
         | 
| 354 363 |  | 
| 355 364 | 
             
                context "finding players" do
         | 
| @@ -495,6 +504,26 @@ EOS | |
| 495 504 | 
             
                    @t.player(2).id = 1350
         | 
| 496 505 | 
             
                    @t.invalid.should match(/duplicate.*ICU/)
         | 
| 497 506 | 
             
                  end
         | 
| 507 | 
            +
             | 
| 508 | 
            +
                  it "should allow players to have no results" do
         | 
| 509 | 
            +
                    (1..3).each { |r| @t.player(1).remove_result(r) }
         | 
| 510 | 
            +
                    @t.invalid.should be_false
         | 
| 511 | 
            +
                  end
         | 
| 512 | 
            +
             | 
| 513 | 
            +
                  it "should not allow asymmetric scores for rateable results" do
         | 
| 514 | 
            +
                    @t.player(1).find_result(1).score = 'L'
         | 
| 515 | 
            +
                    @t.invalid.should match(/result.*reverse/)
         | 
| 516 | 
            +
                  end
         | 
| 517 | 
            +
             | 
| 518 | 
            +
                  it "should allow asymmetric scores for unrateable results" do
         | 
| 519 | 
            +
                    @t.player(1).find_result(1).score = 'L'
         | 
| 520 | 
            +
                    (1..2).each do |p|
         | 
| 521 | 
            +
                      r = @t.player(p).find_result(1)
         | 
| 522 | 
            +
                      r.rateable = false
         | 
| 523 | 
            +
                      r.score = 'L'
         | 
| 524 | 
            +
                    end
         | 
| 525 | 
            +
                    @t.invalid.should be_false
         | 
| 526 | 
            +
                  end
         | 
| 498 527 | 
             
                end
         | 
| 499 528 |  | 
| 500 529 | 
             
                context "renumbering" do
         | 
    
        metadata
    CHANGED
    
    | @@ -2,7 +2,7 @@ | |
| 2 2 | 
             
            name: icu_tournament
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 4 4 | 
             
              prerelease: 
         | 
| 5 | 
            -
              version: 1. | 
| 5 | 
            +
              version: 1.5.0
         | 
| 6 6 | 
             
            platform: ruby
         | 
| 7 7 | 
             
            authors: 
         | 
| 8 8 | 
             
            - Mark Orr
         | 
| @@ -10,7 +10,7 @@ autorequire: | |
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 12 |  | 
| 13 | 
            -
            date: 2011- | 
| 13 | 
            +
            date: 2011-07-01 00:00:00 Z
         | 
| 14 14 | 
             
            dependencies: 
         | 
| 15 15 | 
             
            - !ruby/object:Gem::Dependency 
         | 
| 16 16 | 
             
              name: dbf
         | 
| @@ -172,7 +172,7 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 172 172 | 
             
              requirements: 
         | 
| 173 173 | 
             
              - - ">="
         | 
| 174 174 | 
             
                - !ruby/object:Gem::Version 
         | 
| 175 | 
            -
                  hash:  | 
| 175 | 
            +
                  hash: 3338096865289385673
         | 
| 176 176 | 
             
                  segments: 
         | 
| 177 177 | 
             
                  - 0
         | 
| 178 178 | 
             
                  version: "0"
         | 
| @@ -181,7 +181,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 181 181 | 
             
              requirements: 
         | 
| 182 182 | 
             
              - - ">="
         | 
| 183 183 | 
             
                - !ruby/object:Gem::Version 
         | 
| 184 | 
            -
                  hash:  | 
| 184 | 
            +
                  hash: 3338096865289385673
         | 
| 185 185 | 
             
                  segments: 
         | 
| 186 186 | 
             
                  - 0
         | 
| 187 187 | 
             
                  version: "0"
         |