icu_tournament 0.9.5 → 0.9.6

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.
@@ -4,11 +4,11 @@ module ICU
4
4
 
5
5
  == Building a Tournament
6
6
 
7
- One way to create a tournament object is by parsing one of the supported file type (e.g. ICU::Tournament::Krause).
8
- It is also possible to build one programmatically by
7
+ One way to create a tournament object is by parsing one of the supported file types (e.g. ICU::Tournament::Krause).
8
+ It is also possible to build one programmatically by:
9
9
 
10
10
  1. creating a bare tournament instance,
11
- 2. adding all the players and
11
+ 2. adding all the players,
12
12
  3. adding all the results.
13
13
 
14
14
  For example:
@@ -37,7 +37,7 @@ or equivalntly, just:
37
37
 
38
38
  puts @t.serialize('Krause')
39
39
 
40
- Would result in the following output:
40
+ would result in the following output:
41
41
 
42
42
  012 Bangor Masters
43
43
  042 2009-11-09
@@ -29,11 +29,10 @@ Suppose, for example, that the following data is the file <em>tournament.csv</em
29
29
 
30
30
  This file can be parsed as follows.
31
31
 
32
- data = open('tournament.csv') { |f| f.read }
33
32
  parser = ICU::Tournament::ForeignCSV.new
34
- tournament = parser.parse(data)
33
+ tournament = parser.parse_file('tournament.csv')
35
34
 
36
- If the file is correctly specified, the return value from the <em>parse</em> method is an instance of
35
+ If the file is correctly specified, the return value from the <em>parse_file</em> method is an instance of
37
36
  ICU::Tournament (rather than <em>nil</em>, which indicates an error). In this example the file is valid, so:
38
37
 
39
38
  tournament.name # => "Isle of Man Masters, 2007"
@@ -73,6 +72,11 @@ to which the main player belongs. For example:
73
72
  opponent.name # => "Taylor, Peter P."
74
73
  opponent.results[0].rateable # => false
75
74
 
75
+ If the file contains errors, then the return value from <em>parse_file</em> is <em>nil</em> and
76
+ an error message is returned by the <em>error</em> method of the parser object. The method
77
+ <em>parse_file!</em> is similar except that it raises errors, and the methods <em>parse</em>
78
+ and <em>parse!</em> are similar except their inputs are strings rather than file names.
79
+
76
80
  A tournament can be serialized back to CSV format (the reverse of parsing) with the _serialize_ method
77
81
  of the parser object.
78
82
 
@@ -113,17 +117,6 @@ For example, here are the commands to reproduce the example above.
113
117
  class ForeignCSV
114
118
  attr_reader :error
115
119
 
116
- # Parse CSV data returning a Tournament on success or a nil on failure.
117
- # In the case of failure, an error message can be retrived via the <em>error</em> method.
118
- def parse(csv)
119
- begin
120
- parse!(csv)
121
- rescue => ex
122
- @error = ex.message
123
- nil
124
- end
125
- end
126
-
127
120
  # Parse CSV data returning a Tournament on success or raising an exception on error.
128
121
  def parse!(csv)
129
122
  @state, @line, @round, @sum, @error = 0, 0, nil, nil, nil
@@ -171,6 +164,33 @@ For example, here are the commands to reproduce the example above.
171
164
  @tournament
172
165
  end
173
166
 
167
+ # Parse CSV data returning a Tournament on success or a nil on failure.
168
+ # In the case of failure, an error message can be retrived via the <em>error</em> method.
169
+ def parse(csv)
170
+ begin
171
+ parse!(csv)
172
+ rescue => ex
173
+ @error = ex.message
174
+ nil
175
+ end
176
+ end
177
+
178
+ # Same as <em>parse!</em> except the input is a file name rather than file contents.
179
+ def parse_file!(file)
180
+ csv = open(file) { |f| f.read }
181
+ parse!(csv)
182
+ end
183
+
184
+ # Same as <em>parse</em> except the input is a file name rather than file contents.
185
+ def parse_file(file)
186
+ begin
187
+ parse_file!(file)
188
+ rescue => ex
189
+ @error = ex.message
190
+ nil
191
+ end
192
+ end
193
+
174
194
  # Serialise a tournament back into CSV format.
175
195
  def serialize(t)
176
196
  return nil unless t.class == ICU::Tournament;
@@ -1,6 +1,6 @@
1
1
  module ICU
2
2
  class Tournament
3
-
3
+
4
4
  =begin rdoc
5
5
 
6
6
  == Krause
@@ -15,36 +15,35 @@ Suppose, for example, that the following data is the file <em>tournament.tab</em
15
15
  042 2009.09.09
16
16
  0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
17
17
  132 09.09.09 09.09.10 09.09.11
18
- 001 1 w Mouse,Minerva 1900 USA 1234567 1928.05.15 1.0 2 2 b 0 3 w 1
18
+ 001 1 w Mouse,Minerva 1900 USA 1234567 1928.05.15 1.0 2 2 b 0 3 w 1
19
19
  001 2 m m Duck,Daffy 2200 IRL 7654321 1937.04.17 2.0 1 1 w 1 3 b 1
20
20
  001 3 m g Mouse,Mickey 2600 USA 1726354 1928.05.15 0.0 3 1 b 0 2 w 0
21
21
 
22
22
  This file can be parsed as follows.
23
23
 
24
- data = open('tournament.tab') { |f| f.read }
25
24
  parser = ICU::Tournament::Krause.new
26
- tournament = parser.parse(data)
25
+ tournament = parser.parse_file('tournament.tab')
27
26
 
28
- If the file is correctly specified, the return value from the <em>parse</em> method is an instance of
27
+ If the file is correctly specified, the return value from the <em>parse_file</em> method is an instance of
29
28
  ICU::Tournament (rather than <em>nil</em>, which indicates an error). In this example the file is valid, so:
30
-
29
+
31
30
  tournament.name # => "Fantasy Tournament"
32
31
  tournament.start # => "2009-09-09"
33
32
  tournament.fed # => "IRL"
34
33
  tournament.players.size # => 9
35
34
 
36
35
  Some values, not explicitly set in the file, are deduced:
37
-
36
+
38
37
  tournament.rounds # => 3
39
38
  tournament.finish # => "2009-09-11"
40
-
39
+
41
40
  A player can be retrieved from the tournament via the _players_ array or by sending a valid player number to the _player_ method.
42
41
 
43
42
  minnie = tournament.player(1)
44
43
  minnie.name # => "Mouse, Minerva"
45
44
  minnie.points # => 1.0
46
45
  minnie.results.size # => 2
47
-
46
+
48
47
  daffy = tournament.player(2)
49
48
  daffy.title # => "IM"
50
49
  daffy.rating # => 2200
@@ -65,6 +64,11 @@ to parse another file.
65
64
 
66
65
  parser.comments # => "0123456789..."
67
66
 
67
+ If the file contains errors, then the return value from <em>parse_file</em> is <em>nil</em> and
68
+ an error message is returned by the <em>error</em> method of the parser object. The method
69
+ <em>parse_file!</em> is similar except that it raises errors, and the methods <em>parse</em>
70
+ and <em>parse!</em> are similar except their inputs are strings rather than file names.
71
+
68
72
  A tournament can be serialized back to Krause format (the reverse of parsing) with the _serialize_ method of the parser.
69
73
 
70
74
  krause = parser.serialize(tournament)
@@ -96,18 +100,7 @@ attributes in an ICU::Tournament instance.
96
100
 
97
101
  class Krause
98
102
  attr_reader :error, :comments
99
-
100
- # Parse Krause data returning a Tournament on success or a nil on failure.
101
- # In the case of failure, an error message can be retrived via the <em>error</em> method.
102
- def parse(krs)
103
- begin
104
- parse!(krs)
105
- rescue => ex
106
- @error = ex.message
107
- nil
108
- end
109
- end
110
-
103
+
111
104
  # Parse Krause data returning a Tournament on success or raising an exception on error.
112
105
  def parse!(krs)
113
106
  @lineno = 0
@@ -115,14 +108,14 @@ attributes in an ICU::Tournament instance.
115
108
  @name_set, @start_set = false, false
116
109
  @comments = ''
117
110
  @results = Array.new
118
-
111
+
119
112
  # Process all lines.
120
113
  krs.each_line do |line|
121
114
  @lineno += 1 # increment line number
122
115
  line.strip! # remove leading and trailing white space
123
116
  next if line == '' # skip blank lines
124
117
  @line = line # remember this line for later
125
-
118
+
126
119
  # Does it havea DIN or is it just a comment?
127
120
  if @line.match(/^(\d{3}) (.*)$/)
128
121
  din = $1 # data identification number (DIN)
@@ -131,7 +124,7 @@ attributes in an ICU::Tournament instance.
131
124
  add_comment
132
125
  next
133
126
  end
134
-
127
+
135
128
  # Process the line given the DIN.
136
129
  begin
137
130
  case din
@@ -156,7 +149,7 @@ attributes in an ICU::Tournament instance.
156
149
  raise err.class, "line #{@lineno}: #{err.message}", err.backtrace
157
150
  end
158
151
  end
159
-
152
+
160
153
  # Now that all players are present, add the results to the tournament.
161
154
  @results.each do |r|
162
155
  lineno, player, data, result = r
@@ -166,17 +159,44 @@ attributes in an ICU::Tournament instance.
166
159
  raise "line #{lineno}, player #{player}, result '#{data}': #{err.message}"
167
160
  end
168
161
  end
169
-
162
+
170
163
  # Certain attributes are mandatory and should have been specifically set.
171
164
  raise "tournament name missing" unless @name_set
172
165
  raise "tournament start date missing" unless @start_set
173
-
166
+
174
167
  # Finally, exercise the tournament object's internal validation, reranking if neccessary.
175
168
  @tournament.validate!(:rerank => true)
176
-
169
+
177
170
  @tournament
178
171
  end
179
-
172
+
173
+ # Parse Krause data returning a Tournament on success or a nil on failure.
174
+ # In the case of failure, an error message can be retrived via the <em>error</em> method.
175
+ def parse(krs)
176
+ begin
177
+ parse!(krs)
178
+ rescue => ex
179
+ @error = ex.message
180
+ nil
181
+ end
182
+ end
183
+
184
+ # Same as <em>parse!</em> except the input is a file name rather than file contents.
185
+ def parse_file!(file)
186
+ krause = open(file) { |f| f.read }
187
+ parse!(krause)
188
+ end
189
+
190
+ # Same as <em>parse</em> except the input is a file name rather than file contents.
191
+ def parse_file(file)
192
+ begin
193
+ parse_file!(file)
194
+ rescue => ex
195
+ @error = ex.message
196
+ nil
197
+ end
198
+ end
199
+
180
200
  # Serialise a tournament back into Krause format.
181
201
  def serialize(t)
182
202
  return nil unless t.class == ICU::Tournament;
@@ -206,20 +226,20 @@ attributes in an ICU::Tournament instance.
206
226
  end
207
227
 
208
228
  private
209
-
229
+
210
230
  def set_name
211
231
  @tournament.name = @data
212
232
  @name_set = true
213
233
  end
214
-
234
+
215
235
  def set_start
216
236
  @tournament.start = @data
217
237
  @start_set = true
218
238
  end
219
-
239
+
220
240
  def add_player
221
241
  raise "player record less than minimum length" if @line.length < 99
222
-
242
+
223
243
  # Player details.
224
244
  num = @data[0, 4]
225
245
  nam = Name.new(@data[10, 32])
@@ -235,7 +255,7 @@ attributes in an ICU::Tournament instance.
235
255
  }
236
256
  player = Player.new(nam.first, nam.last, num, opt)
237
257
  @tournament.add_player(player)
238
-
258
+
239
259
  # Results.
240
260
  points = @data[77, 4].strip
241
261
  points = points == '' ? nil : points.to_f
@@ -249,7 +269,7 @@ attributes in an ICU::Tournament instance.
249
269
  end
250
270
  raise "declared points total (#{points}) does not agree with total from summed results (#{total})" if points && points != total
251
271
  end
252
-
272
+
253
273
  def add_result(round, player, data)
254
274
  return 0.0 if data.strip! == '' # no result for this round
255
275
  raise "invalid result '#{data}'" unless data.match(/^(0{1,4}|[1-9]\d{0,3}) (w|b|-) (1|0|=|\+|-)$/)
@@ -264,7 +284,7 @@ attributes in an ICU::Tournament instance.
264
284
  @results << [@lineno, player, data, result]
265
285
  result.points
266
286
  end
267
-
287
+
268
288
  def add_team
269
289
  raise error "team record less than minimum length" if @line.length < 40
270
290
  team = Team.new(@data[0, 31])
@@ -275,7 +295,7 @@ attributes in an ICU::Tournament instance.
275
295
  end
276
296
  @tournament.add_team(team)
277
297
  end
278
-
298
+
279
299
  def add_round_dates
280
300
  raise "round dates record less than minimum length" if @line.length < 99
281
301
  index = 87
@@ -292,7 +312,7 @@ attributes in an ICU::Tournament instance.
292
312
  end
293
313
  end
294
314
  end
295
-
315
+
296
316
  class Player
297
317
  # Format a player's 001 record as it would appear in a Krause formatted file (including the final newline).
298
318
  def to_krause(rounds)
@@ -314,7 +334,7 @@ attributes in an ICU::Tournament instance.
314
334
  krause << "\n"
315
335
  end
316
336
  end
317
-
337
+
318
338
  class Result
319
339
  # Format a player's result as it would appear in a Krause formatted file (exactly 8 characters long, including leading whitespace).
320
340
  def to_krause
@@ -1,5 +1,5 @@
1
1
  module ICU
2
2
  class Tournament
3
- VERSION = "0.9.5"
3
+ VERSION = "0.9.6"
4
4
  end
5
5
  end
@@ -543,6 +543,37 @@ CSV
543
543
  @t.serialize('ForeignCSV').should == @csv
544
544
  end
545
545
  end
546
+
547
+ context "parsing files" do
548
+ before(:each) do
549
+ @p = ICU::Tournament::ForeignCSV.new
550
+ @s = File.dirname(__FILE__) + '/samples/fcsv'
551
+ end
552
+
553
+ it "should error on a non-existant valid file" do
554
+ file = "#{@s}/not_there.csv"
555
+ lambda { @p.parse_file!(file) }.should raise_error
556
+ t = @p.parse_file(file)
557
+ t.should be_nil
558
+ @p.error.should match(/no such file/i)
559
+ end
560
+
561
+ it "should error on an invalid file" do
562
+ file = "#{@s}/invalid.csv"
563
+ lambda { @p.parse_file!(file) }.should raise_error
564
+ t = @p.parse_file(file)
565
+ t.should be_nil
566
+ @p.error.should match(/expected.*event.*name/i)
567
+ end
568
+
569
+ it "should parse a valid file" do
570
+ file = "#{@s}/valid.csv"
571
+ lambda { @p.parse_file!(file) }.should_not raise_error
572
+ t = @p.parse_file(file)
573
+ t.should be_an_instance_of(ICU::Tournament)
574
+ t.players.size.should == 16
575
+ end
576
+ end
546
577
  end
547
578
  end
548
579
  end
@@ -374,6 +374,37 @@ KRAUSE
374
374
  lambda { t = @p.parse!(@k) }.should raise_error(/opponent/)
375
375
  end
376
376
  end
377
+
378
+ context "parsing files" do
379
+ before(:each) do
380
+ @p = ICU::Tournament::Krause.new
381
+ @s = File.dirname(__FILE__) + '/samples/krause'
382
+ end
383
+
384
+ it "should error on a non-existant valid file" do
385
+ file = "#{@s}/not_there.tab"
386
+ lambda { @p.parse_file!(file) }.should raise_error
387
+ t = @p.parse_file(file)
388
+ t.should be_nil
389
+ @p.error.should match(/no such file/i)
390
+ end
391
+
392
+ it "should error on an invalid file" do
393
+ file = "#{@s}/invalid.tab"
394
+ lambda { @p.parse_file!(file) }.should raise_error
395
+ t = @p.parse_file(file)
396
+ t.should be_nil
397
+ @p.error.should match(/tournament name missing/i)
398
+ end
399
+
400
+ it "should parse a valid file" do
401
+ file = "#{@s}/valid.tab"
402
+ lambda { @p.parse_file!(file) }.should_not raise_error
403
+ t = @p.parse_file(file)
404
+ t.should be_an_instance_of(ICU::Tournament)
405
+ t.players.size.should == 12
406
+ end
407
+ end
377
408
  end
378
409
  end
379
410
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: icu_tournament
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.5
4
+ version: 0.9.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Orr