icu_tournament 0.9.5 → 0.9.6
Sign up to get free protection for your applications and to get access to all the features.
@@ -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
|