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
|
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
|
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
|
-
|
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.
|
33
|
+
tournament = parser.parse_file('tournament.csv')
|
35
34
|
|
36
|
-
If the file is correctly specified, the return value from the <em>
|
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.
|
25
|
+
tournament = parser.parse_file('tournament.tab')
|
27
26
|
|
28
|
-
If the file is correctly specified, the return value from the <em>
|
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
|
@@ -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
|