icu_tournament 1.0.13 → 1.1.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/tournament.rb +32 -2
- data/lib/icu_tournament/tournament_fcsv.rb +53 -24
- data/lib/icu_tournament/tournament_krause.rb +6 -1
- data/lib/icu_tournament/tournament_sp.rb +6 -1
- data/lib/icu_tournament/version.rb +1 -1
- data/spec/spec_helper.rb +5 -0
- data/spec/tournament_fcsv_spec.rb +67 -6
- data/spec/tournament_krause_spec.rb +6 -10
- data/spec/tournament_spec.rb +21 -0
- metadata +3 -3
| @@ -35,7 +35,7 @@ and then: | |
| 35 35 |  | 
| 36 36 | 
             
            or equivalntly, just:
         | 
| 37 37 |  | 
| 38 | 
            -
              puts  | 
| 38 | 
            +
              puts t.serialize('Krause')
         | 
| 39 39 |  | 
| 40 40 | 
             
            would result in the following output:
         | 
| 41 41 |  | 
| @@ -72,6 +72,22 @@ Side effects of calling <em>validate!</em> or _invalid_ include: | |
| 72 72 | 
             
            * the number of rounds will be set if not set already
         | 
| 73 73 | 
             
            * the finish date will be set if not set already and if there are round dates
         | 
| 74 74 |  | 
| 75 | 
            +
            Optionally, additional validation checks can be performed given a tournament
         | 
| 76 | 
            +
            parser/serializer. For example:
         | 
| 77 | 
            +
             | 
| 78 | 
            +
              t.validate!(:type => ICU::Tournament.ForeignCSV.new)
         | 
| 79 | 
            +
             | 
| 80 | 
            +
            Or equivalently:
         | 
| 81 | 
            +
             | 
| 82 | 
            +
              t.validate!(:type => 'ForeignCSV')
         | 
| 83 | 
            +
             | 
| 84 | 
            +
            Such additional validation is always performed before a tournament is serialized.
         | 
| 85 | 
            +
            For example, the following are equivalent and will throw an exception if
         | 
| 86 | 
            +
            the tournament is invalid according to either the general rules or the rules
         | 
| 87 | 
            +
            specific for the type used:
         | 
| 88 | 
            +
             | 
| 89 | 
            +
              t.serialize('ForeignCSV')
         | 
| 90 | 
            +
              ICU::Tournament::ForeignCSV.new.serialize(t)
         | 
| 75 91 |  | 
| 76 92 | 
             
            == Ranking
         | 
| 77 93 |  | 
| @@ -370,6 +386,7 @@ in which case any options supplied to this method will be silently ignored. | |
| 370 386 | 
             
                  check_dates
         | 
| 371 387 | 
             
                  check_teams
         | 
| 372 388 | 
             
                  check_ranks(:allow_none => true)
         | 
| 389 | 
            +
                  check_type(options[:type]) if options[:type]
         | 
| 373 390 | 
             
                  true
         | 
| 374 391 | 
             
                end
         | 
| 375 392 |  | 
| @@ -386,12 +403,14 @@ in which case any options supplied to this method will be silently ignored. | |
| 386 403 | 
             
                end
         | 
| 387 404 |  | 
| 388 405 | 
             
                # Convenience method to serialise the tournament into a supported format.
         | 
| 389 | 
            -
                # Throws  | 
| 406 | 
            +
                # Throws an exception unless the name of a supported format is supplied
         | 
| 407 | 
            +
                # or if the tournament is unsuitable for serialisation in that format.
         | 
| 390 408 | 
             
                def serialize(format)
         | 
| 391 409 | 
             
                  serializer = case format.to_s.downcase
         | 
| 392 410 | 
             
                    when 'krause'       then ICU::Tournament::Krause.new
         | 
| 393 411 | 
             
                    when 'foreigncsv'   then ICU::Tournament::ForeignCSV.new
         | 
| 394 412 | 
             
                    when 'swissperfect' then ICU::Tournament::SwissPerfect.new
         | 
| 413 | 
            +
                    when ''             then raise "no format supplied"
         | 
| 395 414 | 
             
                    else raise "unsupported serialisation format: '#{format}'"
         | 
| 396 415 | 
             
                  end
         | 
| 397 416 | 
             
                  serializer.serialize(self)
         | 
| @@ -486,6 +505,17 @@ in which case any options supplied to this method will be silently ignored. | |
| 486 505 | 
             
                  end
         | 
| 487 506 | 
             
                end
         | 
| 488 507 |  | 
| 508 | 
            +
                # Validate against a specific type.
         | 
| 509 | 
            +
                def check_type(type)
         | 
| 510 | 
            +
                  if type.respond_to?(:validate!)
         | 
| 511 | 
            +
                    type.validate!(self)
         | 
| 512 | 
            +
                  elsif type.to_s.match(/^(ForeignCSV|Krause|SwissPerfect)$/)
         | 
| 513 | 
            +
                    parser = "ICU::Tournament::#{type.to_s}".constantize.new.validate!(self)
         | 
| 514 | 
            +
                  else
         | 
| 515 | 
            +
                    raise "invalid type supplied for validation check"
         | 
| 516 | 
            +
                  end
         | 
| 517 | 
            +
                end
         | 
| 518 | 
            +
             | 
| 489 519 | 
             
                # Return an array of tie break methods and an array of tie break orders (+1 for asc, -1 for desc).
         | 
| 490 520 | 
             
                # The first and most important method is always "score", the last and least important is always "name".
         | 
| 491 521 | 
             
                def tie_break_data
         | 
| @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            module ICU
         | 
| 2 2 | 
             
              class Tournament
         | 
| 3 | 
            -
             | 
| 3 | 
            +
             | 
| 4 4 | 
             
            =begin rdoc
         | 
| 5 5 |  | 
| 6 6 | 
             
            == Foreign CSV
         | 
| @@ -34,7 +34,7 @@ This file can be parsed as follows. | |
| 34 34 |  | 
| 35 35 | 
             
            If the file is correctly specified, the return value from the <em>parse_file</em> method is an instance of
         | 
| 36 36 | 
             
            ICU::Tournament (rather than <em>nil</em>, which indicates an error). In this example the file is valid, so:
         | 
| 37 | 
            -
             | 
| 37 | 
            +
             | 
| 38 38 | 
             
              tournament.name                                     # => "Isle of Man Masters, 2007"
         | 
| 39 39 | 
             
              tournament.start                                    # => "2007-09-22"
         | 
| 40 40 | 
             
              tournament.rounds                                   # => 9
         | 
| @@ -44,7 +44,7 @@ The main player (the player whose results are being reported for rating) played | |
| 44 44 | 
             
            but only 8 other players (he had a bye in round 6), so the total number of players is 9.
         | 
| 45 45 |  | 
| 46 46 | 
             
              tournament.players.size                             # => 9
         | 
| 47 | 
            -
             | 
| 47 | 
            +
             | 
| 48 48 | 
             
            Each player has a unique number for the tournament. The main player always occurs first in this type of file, so his number is 1.
         | 
| 49 49 |  | 
| 50 50 | 
             
              player = tournament.player(1)
         | 
| @@ -86,6 +86,20 @@ Or equivalently, the _serialize_ instance method of the tournament, if the appro | |
| 86 86 |  | 
| 87 87 | 
             
              csv = tournament.serialize('ForeignCSV')
         | 
| 88 88 |  | 
| 89 | 
            +
            Extra condtions, over and above the normal validation rules, apply before any tournament validates or can be serialized in this format.
         | 
| 90 | 
            +
             | 
| 91 | 
            +
            * the tournament must have a _site_ attribute
         | 
| 92 | 
            +
            * there must be at least one player with an _id_ (interpreted as an ICU ID number)
         | 
| 93 | 
            +
            * all foreign players (those without an ICU ID) must have a _fed_ attribute (federation)
         | 
| 94 | 
            +
            * all ICU players must have a result in every round (even if it is just bye or is unrateable)
         | 
| 95 | 
            +
            * the opponents of all ICU players must have a federation (this could include other ICU players)
         | 
| 96 | 
            +
             | 
| 97 | 
            +
            If any of these are not satisfied, then the following method calls will all raise an exception:
         | 
| 98 | 
            +
             | 
| 99 | 
            +
              tournament.validate!(:type => 'ForeignCSV')
         | 
| 100 | 
            +
              tournament.serialize('ForeignCSV')
         | 
| 101 | 
            +
              ICU::Tournament::ForeignCSV.new.serialize(tournament)
         | 
| 102 | 
            +
             | 
| 89 103 | 
             
            You can also build the tournament object from scratch using your own data and then serialize it.
         | 
| 90 104 | 
             
            For example, here are the commands to reproduce the example above.
         | 
| 91 105 |  | 
| @@ -105,30 +119,29 @@ For example, here are the commands to reproduce the example above. | |
| 105 119 | 
             
              t.add_result(ICU::Result.new(3, 1, 'D', :opponent => 4, :colour => 'B'))
         | 
| 106 120 | 
             
              t.add_result(ICU::Result.new(4, 1, 'W', :opponent => 5, :colour => 'W'))
         | 
| 107 121 | 
             
              t.add_result(ICU::Result.new(5, 1, 'W', :opponent => 6, :colour => 'B'))
         | 
| 108 | 
            -
              t.add_result(ICU::Result.new(6, 1, 'L')) | 
| 122 | 
            +
              t.add_result(ICU::Result.new(6, 1, 'L'))
         | 
| 109 123 | 
             
              t.add_result(ICU::Result.new(7, 1, 'D', :opponent => 7, :colour => 'W'))
         | 
| 110 124 | 
             
              t.add_result(ICU::Result.new(8, 1, 'L', :opponent => 8, :colour => 'B'))
         | 
| 111 125 | 
             
              t.add_result(ICU::Result.new(9, 1, 'D', :opponent => 9, :colour => 'W'))
         | 
| 112 | 
            -
              t.validate!
         | 
| 113 126 | 
             
              puts t.serialize('ForeignCSV')
         | 
| 114 | 
            -
             | 
| 127 | 
            +
             | 
| 115 128 | 
             
            =end
         | 
| 116 129 |  | 
| 117 130 | 
             
                class ForeignCSV
         | 
| 118 131 | 
             
                  attr_reader :error
         | 
| 119 | 
            -
             | 
| 132 | 
            +
             | 
| 120 133 | 
             
                  # Parse CSV data returning a Tournament on success or raising an exception on error.
         | 
| 121 134 | 
             
                  def parse!(csv)
         | 
| 122 135 | 
             
                    @state, @line, @round, @sum, @error = 0, 0, nil, nil, nil
         | 
| 123 136 | 
             
                    @tournament = Tournament.new('Dummy', '2000-01-01')
         | 
| 124 | 
            -
             | 
| 137 | 
            +
             | 
| 125 138 | 
             
                    Util::CSV.parse(csv, :row_sep => :auto) do |r|
         | 
| 126 139 | 
             
                      @line += 1                            # increment line number
         | 
| 127 140 | 
             
                      next if r.size == 0                   # skip empty lines
         | 
| 128 141 | 
             
                      r = r.map{|c| c.nil? ? '' : c.strip}  # trim all spaces, turn nils to blanks
         | 
| 129 142 | 
             
                      next if r[0] == ''                    # skip blanks in column 1
         | 
| 130 143 | 
             
                      @r = r                                # remember this record for later
         | 
| 131 | 
            -
             | 
| 144 | 
            +
             | 
| 132 145 | 
             
                      begin
         | 
| 133 146 | 
             
                        case @state
         | 
| 134 147 | 
             
                          when 0 then event
         | 
| @@ -145,7 +158,7 @@ For example, here are the commands to reproduce the example above. | |
| 145 158 | 
             
                        raise
         | 
| 146 159 | 
             
                      end
         | 
| 147 160 | 
             
                    end
         | 
| 148 | 
            -
             | 
| 161 | 
            +
             | 
| 149 162 | 
             
                    unless @state == 4
         | 
| 150 163 | 
             
                      exp = case @state
         | 
| 151 164 | 
             
                            when 0 then "the event name"
         | 
| @@ -158,12 +171,12 @@ For example, here are the commands to reproduce the example above. | |
| 158 171 | 
             
                      raise "line #{@line}: premature termination - expected #{exp}"
         | 
| 159 172 | 
             
                    end
         | 
| 160 173 | 
             
                    raise "line #{@line}: no players found in file" if @tournament.players.size == 0
         | 
| 161 | 
            -
             | 
| 174 | 
            +
             | 
| 162 175 | 
             
                    @tournament.validate!
         | 
| 163 176 |  | 
| 164 177 | 
             
                    @tournament
         | 
| 165 178 | 
             
                  end
         | 
| 166 | 
            -
             | 
| 179 | 
            +
             | 
| 167 180 | 
             
                  # Parse CSV data returning a Tournament on success or a nil on failure.
         | 
| 168 181 | 
             
                  # In the case of failure, an error message can be retrived via the <em>error</em> method.
         | 
| 169 182 | 
             
                  def parse(csv)
         | 
| @@ -174,13 +187,13 @@ For example, here are the commands to reproduce the example above. | |
| 174 187 | 
             
                      nil
         | 
| 175 188 | 
             
                    end
         | 
| 176 189 | 
             
                  end
         | 
| 177 | 
            -
             | 
| 190 | 
            +
             | 
| 178 191 | 
             
                  # Same as <em>parse!</em> except the input is a file name rather than file contents.
         | 
| 179 192 | 
             
                  def parse_file!(file)
         | 
| 180 193 | 
             
                    csv = open(file) { |f| f.read }
         | 
| 181 194 | 
             
                    parse!(csv)
         | 
| 182 195 | 
             
                  end
         | 
| 183 | 
            -
             | 
| 196 | 
            +
             | 
| 184 197 | 
             
                  # Same as <em>parse</em> except the input is a file name rather than file contents.
         | 
| 185 198 | 
             
                  def parse_file(file)
         | 
| 186 199 | 
             
                    begin
         | 
| @@ -190,10 +203,10 @@ For example, here are the commands to reproduce the example above. | |
| 190 203 | 
             
                      nil
         | 
| 191 204 | 
             
                    end
         | 
| 192 205 | 
             
                  end
         | 
| 193 | 
            -
             | 
| 206 | 
            +
             | 
| 194 207 | 
             
                  # Serialise a tournament back into CSV format.
         | 
| 195 208 | 
             
                  def serialize(t)
         | 
| 196 | 
            -
                     | 
| 209 | 
            +
                    t.validate!(:type => self)
         | 
| 197 210 | 
             
                    Util::CSV.generate do |csv|
         | 
| 198 211 | 
             
                      csv << ["Event", t.name]
         | 
| 199 212 | 
             
                      csv << ["Start", t.start]
         | 
| @@ -226,36 +239,52 @@ For example, here are the commands to reproduce the example above. | |
| 226 239 | 
             
                    end
         | 
| 227 240 | 
             
                  end
         | 
| 228 241 |  | 
| 242 | 
            +
                  # Additional tournament validation rules for this specific type.
         | 
| 243 | 
            +
                  def validate!(t)
         | 
| 244 | 
            +
                    raise "missing site" unless t.site.to_s.length > 0
         | 
| 245 | 
            +
                    icu = t.players.find_all { |p| p.id }
         | 
| 246 | 
            +
                    raise "there must be at least one ICU player (with an ID number)" if icu.size == 0
         | 
| 247 | 
            +
                    foreign = t.players.find_all { |p| !p.id }
         | 
| 248 | 
            +
                    raise "all foreign players must have a federation" if foreign.detect { |f| !f.fed }
         | 
| 249 | 
            +
                    icu.each do |p|
         | 
| 250 | 
            +
                      (1..t.rounds).each do |r|
         | 
| 251 | 
            +
                        result = p.find_result(r)
         | 
| 252 | 
            +
                        raise "ICU players must have a result in every round" unless result
         | 
| 253 | 
            +
                        raise "all opponents of ICU players must have a federation" if result.opponent && !t.player(result.opponent).fed
         | 
| 254 | 
            +
                      end
         | 
| 255 | 
            +
                    end
         | 
| 256 | 
            +
                  end
         | 
| 257 | 
            +
             | 
| 229 258 | 
             
                  private
         | 
| 230 | 
            -
             | 
| 259 | 
            +
             | 
| 231 260 | 
             
                  def event
         | 
| 232 261 | 
             
                    abort "the 'Event' keyword", 0 unless @r[0].match(/^(Event|Tournament)$/i)
         | 
| 233 262 | 
             
                    abort "the event name",      1 unless @r.size > 1 && @r[1] != ''
         | 
| 234 263 | 
             
                    @tournament.name = @r[1]
         | 
| 235 264 | 
             
                    @state = 1
         | 
| 236 265 | 
             
                  end
         | 
| 237 | 
            -
             | 
| 266 | 
            +
             | 
| 238 267 | 
             
                  def start
         | 
| 239 268 | 
             
                    abort "the 'Start' keyword", 0 unless @r[0].match(/^(Start(\s+Date)?|Date)$/i)
         | 
| 240 269 | 
             
                    abort "the start date",      1 unless @r.size > 1 && @r[1] != ''
         | 
| 241 270 | 
             
                    @tournament.start = @r[1]
         | 
| 242 271 | 
             
                    @state = 2
         | 
| 243 272 | 
             
                  end
         | 
| 244 | 
            -
             | 
| 273 | 
            +
             | 
| 245 274 | 
             
                  def rounds
         | 
| 246 275 | 
             
                    abort "the 'Rounds' keyword", 0 unless @r[0].match(/(Number of )?Rounds$/)
         | 
| 247 276 | 
             
                    abort "the number of rounds", 1 unless @r.size > 1 && @r[1].match(/^[1-9]\d*/)
         | 
| 248 277 | 
             
                    @tournament.rounds = @r[1]
         | 
| 249 278 | 
             
                    @state = 3
         | 
| 250 279 | 
             
                  end
         | 
| 251 | 
            -
             | 
| 280 | 
            +
             | 
| 252 281 | 
             
                  def website
         | 
| 253 282 | 
             
                    abort "the 'Website' keyword", 0 unless @r[0].match(/^(Web(\s?site)?|Site)$/i)
         | 
| 254 283 | 
             
                    abort "the event website",     1 unless @r.size > 1 && @r[1] != ''
         | 
| 255 284 | 
             
                    @tournament.site = @r[1]
         | 
| 256 285 | 
             
                    @state = 4
         | 
| 257 286 | 
             
                  end
         | 
| 258 | 
            -
             | 
| 287 | 
            +
             | 
| 259 288 | 
             
                  def player
         | 
| 260 289 | 
             
                    abort "the 'Player' keyword",  0 unless @r[0].match(/^Player$/i)
         | 
| 261 290 | 
             
                    abort "a player's ICU number", 1 unless @r.size > 1 && @r[1].match(/^[1-9]/i)
         | 
| @@ -274,7 +303,7 @@ For example, here are the commands to reproduce the example above. | |
| 274 303 | 
             
                    @round = 0
         | 
| 275 304 | 
             
                    @state = 5
         | 
| 276 305 | 
             
                  end
         | 
| 277 | 
            -
             | 
| 306 | 
            +
             | 
| 278 307 | 
             
                  def result
         | 
| 279 308 | 
             
                    @round+= 1
         | 
| 280 309 | 
             
                    abort "round number #{round}",              0 unless @r[0].to_i == @round
         | 
| @@ -309,14 +338,14 @@ For example, here are the commands to reproduce the example above. | |
| 309 338 | 
             
                    end
         | 
| 310 339 | 
             
                    @state = 6 if @round == @tournament.rounds
         | 
| 311 340 | 
             
                  end
         | 
| 312 | 
            -
             | 
| 341 | 
            +
             | 
| 313 342 | 
             
                  def total
         | 
| 314 343 | 
             
                    points = @player.points
         | 
| 315 344 | 
             
                    abort "the 'Total' keyword", 0 unless @r[0].match(/^Total$/i)
         | 
| 316 345 | 
             
                    abort "the player's (#{@player.object_id}, #{@player.results.size}) total points to be #{points}", 1 unless @r[1].to_f == points
         | 
| 317 346 | 
             
                    @state = 4
         | 
| 318 347 | 
             
                  end
         | 
| 319 | 
            -
             | 
| 348 | 
            +
             | 
| 320 349 | 
             
                  def abort(expected, cell)
         | 
| 321 350 | 
             
                    got = @r[cell]
         | 
| 322 351 | 
             
                    error = "line #{@line}"
         | 
| @@ -199,7 +199,7 @@ attributes in an ICU::Tournament instance. | |
| 199 199 |  | 
| 200 200 | 
             
                  # Serialise a tournament back into Krause format.
         | 
| 201 201 | 
             
                  def serialize(t)
         | 
| 202 | 
            -
                     | 
| 202 | 
            +
                    t.validate!(:type => self)
         | 
| 203 203 | 
             
                    krause = ''
         | 
| 204 204 | 
             
                    krause << "012 #{t.name}\n"
         | 
| 205 205 | 
             
                    krause << "022 #{t.city}\n"         if t.city
         | 
| @@ -225,6 +225,11 @@ attributes in an ICU::Tournament instance. | |
| 225 225 | 
             
                    krause
         | 
| 226 226 | 
             
                  end
         | 
| 227 227 |  | 
| 228 | 
            +
                  # Additional tournament validation rules for this specific type.
         | 
| 229 | 
            +
                  def validate!(t)
         | 
| 230 | 
            +
                    # None.
         | 
| 231 | 
            +
                  end
         | 
| 232 | 
            +
             | 
| 228 233 | 
             
                  private
         | 
| 229 234 |  | 
| 230 235 | 
             
                  def set_name
         | 
| @@ -146,7 +146,7 @@ See ICU::Tournament for more about tie-breaks. | |
| 146 146 |  | 
| 147 147 | 
             
                  # Serialise a tournament to SwissPerfect text export format.
         | 
| 148 148 | 
             
                  def serialize(t)
         | 
| 149 | 
            -
                     | 
| 149 | 
            +
                    t.validate!(:type => self)
         | 
| 150 150 |  | 
| 151 151 | 
             
                    # Ensure a nice set of numbers.
         | 
| 152 152 | 
             
                    t.renumber(:order)
         | 
| @@ -173,6 +173,11 @@ See ICU::Tournament for more about tie-breaks. | |
| 173 173 | 
             
                    sp
         | 
| 174 174 | 
             
                  end
         | 
| 175 175 |  | 
| 176 | 
            +
                  # Additional tournament validation rules for this specific type.
         | 
| 177 | 
            +
                  def validate!(t)
         | 
| 178 | 
            +
                    # None.
         | 
| 179 | 
            +
                  end
         | 
| 180 | 
            +
             | 
| 176 181 | 
             
                  private
         | 
| 177 182 |  | 
| 178 183 | 
             
                  def get_files(file, arg)
         | 
    
        data/spec/spec_helper.rb
    CHANGED
    
    
| @@ -412,10 +412,6 @@ CSV | |
| 412 412 | 
             
                    it "should serialize back to the original" do
         | 
| 413 413 | 
             
                      @f.serialize(@t).should == @csv
         | 
| 414 414 | 
             
                    end
         | 
| 415 | 
            -
             | 
| 416 | 
            -
                    it "should return nil on invalid input" do
         | 
| 417 | 
            -
                      @f.serialize('Rubbish').should be_nil
         | 
| 418 | 
            -
                    end
         | 
| 419 415 | 
             
                  end
         | 
| 420 416 |  | 
| 421 417 | 
             
                  context "serialisation of ForeignCSV documentation example" do
         | 
| @@ -515,9 +511,8 @@ Player,456,Fox,Anthony | |
| 515 511 | 
             
            9,=,W,Phillips,Roy,2271,,MAU
         | 
| 516 512 | 
             
            Total,4.0
         | 
| 517 513 | 
             
            CSV
         | 
| 518 | 
            -
                      @t = ICU::Tournament.new("Isle of Man Masters, 2007", '2007-09-22' | 
| 514 | 
            +
                      @t = ICU::Tournament.new("Isle of Man Masters, 2007", '2007-09-22')
         | 
| 519 515 | 
             
                      @t.site = 'http://www.bcmchess.co.uk/monarch2007/'
         | 
| 520 | 
            -
                      @t.rounds = 9
         | 
| 521 516 | 
             
                      @t.add_player(ICU::Player.new('Anthony', 'Fox', 1, :id => 456, :rating => 2100, :fed => 'IRL'))
         | 
| 522 517 | 
             
                      @t.add_player(ICU::Player.new('Peter P.', 'Taylor', 2, :rating => 2209, :fed => 'ENG'))
         | 
| 523 518 | 
             
                      @t.add_player(ICU::Player.new('Egozi', 'Nadav', 3, :rating => 2205, :fed => 'ISR'))
         | 
| @@ -574,6 +569,72 @@ CSV | |
| 574 569 | 
             
                      t.players.size.should == 16
         | 
| 575 570 | 
             
                    end
         | 
| 576 571 | 
             
                  end
         | 
| 572 | 
            +
             | 
| 573 | 
            +
                  context "type validation" do
         | 
| 574 | 
            +
                    before(:each) do
         | 
| 575 | 
            +
                      @p = ICU::Tournament::ForeignCSV.new
         | 
| 576 | 
            +
                      @t = ICU::Tournament.new("Isle of Man Masters, 2007", '2007-09-22')
         | 
| 577 | 
            +
                      @t.site = 'http://www.bcmchess.co.uk/monarch2007/'
         | 
| 578 | 
            +
                      @t.add_player(ICU::Player.new('Anthony', 'Fox', 1, :id => 456))
         | 
| 579 | 
            +
                      @t.add_player(ICU::Player.new('Peter', 'Cafolla', 2, :id => 159))
         | 
| 580 | 
            +
                      @t.add_player(ICU::Player.new('Peter P.', 'Taylor', 10, :rating => 2209, :fed => 'ENG'))
         | 
| 581 | 
            +
                      @t.add_player(ICU::Player.new('Egozi', 'Nadav', 20, :rating => 2205, :fed => 'ISR'))
         | 
| 582 | 
            +
                      @t.add_player(ICU::Player.new('Tim R.', 'Spanton', 30, :rating => 1982, :fed => 'ENG'))
         | 
| 583 | 
            +
                      @t.add_player(ICU::Player.new('Alan', 'Grant', 40, :rating => 2223, :fed => 'SCO'))
         | 
| 584 | 
            +
                      @t.add_result(ICU::Result.new(1, 1, 'W', :opponent => 10, :colour => 'W'))
         | 
| 585 | 
            +
                      @t.add_result(ICU::Result.new(1, 2, 'L', :opponent => 20, :colour => 'B'))
         | 
| 586 | 
            +
                      @t.add_result(ICU::Result.new(2, 1, 'D', :opponent => 30, :colour => 'B'))
         | 
| 587 | 
            +
                      @t.add_result(ICU::Result.new(2, 2, 'L', :opponent => 40, :colour => 'W'))
         | 
| 588 | 
            +
                    end
         | 
| 589 | 
            +
             | 
| 590 | 
            +
                    it "should pass" do
         | 
| 591 | 
            +
                      @t.invalid.should be_false
         | 
| 592 | 
            +
                      @t.invalid(:type => @p).should be_false
         | 
| 593 | 
            +
                    end
         | 
| 594 | 
            +
             | 
| 595 | 
            +
                    it "should fail if there's no site" do
         | 
| 596 | 
            +
                      @t.site = nil;
         | 
| 597 | 
            +
                      @t.invalid(:type => @p).to_s.should match(/site/)
         | 
| 598 | 
            +
                    end
         | 
| 599 | 
            +
             | 
| 600 | 
            +
                    it "should fail if there are no ICU players" do
         | 
| 601 | 
            +
                      [1, 2].each { |n| @t.player(n).id = nil }
         | 
| 602 | 
            +
                      @t.player(2).id = nil;
         | 
| 603 | 
            +
                      @t.invalid(:type => @p).to_s.should match(/ID/)
         | 
| 604 | 
            +
                    end
         | 
| 605 | 
            +
             | 
| 606 | 
            +
                    it "should fail unless all foreign players have a federation" do
         | 
| 607 | 
            +
                      @t.player(10).fed = nil;
         | 
| 608 | 
            +
                      @t.invalid(:type => @p).to_s.should match(/federation/)
         | 
| 609 | 
            +
                    end
         | 
| 610 | 
            +
             | 
| 611 | 
            +
                    it "should fail unless every ICU player has a result in every round" do
         | 
| 612 | 
            +
                      @t.add_player(ICU::Player.new('Mark', 'Orr', 3, :id => 1350))
         | 
| 613 | 
            +
                      @t.add_result(ICU::Result.new(1, 3, 'W', :opponent => 30, :colour => 'B'))
         | 
| 614 | 
            +
                      @t.invalid(:type => @p).to_s.should match(/result/)
         | 
| 615 | 
            +
                      @t.add_result(ICU::Result.new(2, 3, 'W', :opponent => 10, :colour => 'W'))
         | 
| 616 | 
            +
                      @t.invalid(:type => @p).should be_false
         | 
| 617 | 
            +
                    end
         | 
| 618 | 
            +
             | 
| 619 | 
            +
                    it "should fail unless every ICU player's opponents have a federation" do
         | 
| 620 | 
            +
                      @t.add_player(ICU::Player.new('Mark', 'Orr', 3, :id => 1350))
         | 
| 621 | 
            +
                      @t.add_result(ICU::Result.new(1, 3, 'W', :opponent => 30, :colour => 'B'))
         | 
| 622 | 
            +
                      @t.add_result(ICU::Result.new(2, 3, 'W', :opponent => 10, :colour => 'W'))
         | 
| 623 | 
            +
                      @t.add_result(ICU::Result.new(3, 1, 'D', :opponent => 40, :colour => 'W'))
         | 
| 624 | 
            +
                      @t.add_result(ICU::Result.new(3, 2, 'L', :opponent => 3,  :colour => 'B'))
         | 
| 625 | 
            +
                      @t.invalid(:type => @p).to_s.should match(/opponents.*federation/)
         | 
| 626 | 
            +
                      @t.player(2).fed = 'IRL'
         | 
| 627 | 
            +
                      @t.invalid(:type => @p).to_s.should match(/opponents.*federation/)
         | 
| 628 | 
            +
                      @t.player(3).fed = 'IRL'
         | 
| 629 | 
            +
                      @t.invalid(:type => @p).should be_false
         | 
| 630 | 
            +
                    end
         | 
| 631 | 
            +
             | 
| 632 | 
            +
                    it "should be serializable unless invalid" do
         | 
| 633 | 
            +
                      lambda { @p.serialize(@t) }.should_not raise_error
         | 
| 634 | 
            +
                      @t.site = nil;
         | 
| 635 | 
            +
                      lambda { @p.serialize(@t) }.should raise_error
         | 
| 636 | 
            +
                    end
         | 
| 637 | 
            +
                  end
         | 
| 577 638 | 
             
                end
         | 
| 578 639 | 
             
              end
         | 
| 579 640 | 
             
            end
         | 
| @@ -158,7 +158,7 @@ KRAUSE | |
| 158 158 | 
             
                      @t.add_result(ICU::Result.new(1, 1, 'L', :opponent => 2, :colour => 'B'))
         | 
| 159 159 | 
             
                      @t.add_result(ICU::Result.new(2, 1, 'L', :opponent => 2, :colour => 'W', :rateable => false))
         | 
| 160 160 | 
             
                      @t.add_result(ICU::Result.new(3, 1, 'W', :opponent => 2, :colour => 'B'))
         | 
| 161 | 
            -
                      @t.add_result(ICU::Result.new(4, 1, ' | 
| 161 | 
            +
                      @t.add_result(ICU::Result.new(4, 1, 'D', :opponent => 2, :colour => 'W'))
         | 
| 162 162 | 
             
                      serializer = ICU::Tournament::Krause.new
         | 
| 163 163 | 
             
                      @k = serializer.serialize(@t)
         | 
| 164 164 | 
             
                    end
         | 
| @@ -173,7 +173,7 @@ KRAUSE | |
| 173 173 | 
             
                  end
         | 
| 174 174 |  | 
| 175 175 | 
             
                  context "serialisation" do
         | 
| 176 | 
            -
                    before(: | 
| 176 | 
            +
                    before(:each) do
         | 
| 177 177 | 
             
                      @krause = <<KRAUSE
         | 
| 178 178 | 
             
            012 Las Vegas National Open
         | 
| 179 179 | 
             
            022 Las Vegas
         | 
| @@ -204,10 +204,6 @@ KRAUSE | |
| 204 204 | 
             
                    it "should serialize using the convenience method of the tournament object" do
         | 
| 205 205 | 
             
                      @t.serialize('Krause').should == @krause
         | 
| 206 206 | 
             
                    end
         | 
| 207 | 
            -
             | 
| 208 | 
            -
                    it "should return nil on invalid input" do
         | 
| 209 | 
            -
                      @q.serialize('Rubbish').should be_nil
         | 
| 210 | 
            -
                    end
         | 
| 211 207 | 
             
                  end
         | 
| 212 208 |  | 
| 213 209 | 
             
                  context "auto-ranking" do
         | 
| @@ -374,13 +370,13 @@ KRAUSE | |
| 374 370 | 
             
                       lambda { t = @p.parse!(@k) }.should raise_error(/opponent/)
         | 
| 375 371 | 
             
                    end
         | 
| 376 372 | 
             
                  end
         | 
| 377 | 
            -
             | 
| 373 | 
            +
             | 
| 378 374 | 
             
                  context "parsing files" do
         | 
| 379 375 | 
             
                    before(:each) do
         | 
| 380 376 | 
             
                      @p = ICU::Tournament::Krause.new
         | 
| 381 377 | 
             
                      @s = File.dirname(__FILE__) + '/samples/krause'
         | 
| 382 378 | 
             
                    end
         | 
| 383 | 
            -
             | 
| 379 | 
            +
             | 
| 384 380 | 
             
                    it "should error on a non-existant valid file" do
         | 
| 385 381 | 
             
                      file = "#{@s}/not_there.tab"
         | 
| 386 382 | 
             
                      lambda { @p.parse_file!(file) }.should raise_error
         | 
| @@ -388,7 +384,7 @@ KRAUSE | |
| 388 384 | 
             
                      t.should be_nil
         | 
| 389 385 | 
             
                      @p.error.should match(/no such file/i)
         | 
| 390 386 | 
             
                    end
         | 
| 391 | 
            -
             | 
| 387 | 
            +
             | 
| 392 388 | 
             
                    it "should error on an invalid file" do
         | 
| 393 389 | 
             
                      file = "#{@s}/invalid.tab"
         | 
| 394 390 | 
             
                      lambda { @p.parse_file!(file) }.should raise_error
         | 
| @@ -396,7 +392,7 @@ KRAUSE | |
| 396 392 | 
             
                      t.should be_nil
         | 
| 397 393 | 
             
                      @p.error.should match(/tournament name missing/i)
         | 
| 398 394 | 
             
                    end
         | 
| 399 | 
            -
             | 
| 395 | 
            +
             | 
| 400 396 | 
             
                    it "should parse a valid file" do
         | 
| 401 397 | 
             
                      file = "#{@s}/valid.tab"
         | 
| 402 398 | 
             
                      lambda { @p.parse_file!(file) }.should_not raise_error
         | 
    
        data/spec/tournament_spec.rb
    CHANGED
    
    | @@ -853,5 +853,26 @@ EOS | |
| 853 853 | 
             
                    lambda { @c.parse_file!("#{@s}/krause/valid.tab", 'NoSuchType') }.should raise_error(/invalid format/i)
         | 
| 854 854 | 
             
                  end
         | 
| 855 855 | 
             
                end
         | 
| 856 | 
            +
             | 
| 857 | 
            +
                context "type specific validation" do
         | 
| 858 | 
            +
                  before(:all) do
         | 
| 859 | 
            +
                    @t = Tournament.new('Bangor Bash', '2009-11-09')
         | 
| 860 | 
            +
                    @t.add_player(Player.new('Bobby', 'Fischer', 1))
         | 
| 861 | 
            +
                    @t.add_player(Player.new('Garry', 'Kasparov', 2))
         | 
| 862 | 
            +
                    @t.add_player(Player.new('Mark', 'Orr', 3))
         | 
| 863 | 
            +
                    @t.add_result(Result.new(1, 1, '=', :opponent => 2, :colour => 'W'))
         | 
| 864 | 
            +
                    @t.add_result(Result.new(2, 2, 'L', :opponent => 3, :colour => 'W'))
         | 
| 865 | 
            +
                    @t.add_result(Result.new(3, 3, 'W', :opponent => 1, :colour => 'W'))
         | 
| 866 | 
            +
                  end
         | 
| 867 | 
            +
             | 
| 868 | 
            +
                  it "should pass generic validation" do
         | 
| 869 | 
            +
                    @t.invalid.should be_false
         | 
| 870 | 
            +
                  end
         | 
| 871 | 
            +
             | 
| 872 | 
            +
                  it "should fail type-specific validation when the type supplied is inappropriate" do
         | 
| 873 | 
            +
                    @t.invalid(:type => String).should match(/invalid type/)
         | 
| 874 | 
            +
                    @t.invalid(:type => "AbCd").should match(/invalid type/)
         | 
| 875 | 
            +
                  end
         | 
| 876 | 
            +
                end
         | 
| 856 877 | 
             
              end
         | 
| 857 878 | 
             
            end
         | 
    
        metadata
    CHANGED
    
    | @@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version | |
| 4 4 | 
             
              prerelease: false
         | 
| 5 5 | 
             
              segments: 
         | 
| 6 6 | 
             
              - 1
         | 
| 7 | 
            +
              - 1
         | 
| 7 8 | 
             
              - 0
         | 
| 8 | 
            -
               | 
| 9 | 
            -
              version: 1.0.13
         | 
| 9 | 
            +
              version: 1.1.0
         | 
| 10 10 | 
             
            platform: ruby
         | 
| 11 11 | 
             
            authors: 
         | 
| 12 12 | 
             
            - Mark Orr
         | 
| @@ -14,7 +14,7 @@ autorequire: | |
| 14 14 | 
             
            bindir: bin
         | 
| 15 15 | 
             
            cert_chain: []
         | 
| 16 16 |  | 
| 17 | 
            -
            date: 2010-10- | 
| 17 | 
            +
            date: 2010-10-17 00:00:00 +01:00
         | 
| 18 18 | 
             
            default_executable: 
         | 
| 19 19 | 
             
            dependencies: 
         | 
| 20 20 | 
             
            - !ruby/object:Gem::Dependency 
         |