tournament 0.0.1
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/History.txt +4 -0
- data/Manifest.txt +29 -0
- data/README.txt +113 -0
- data/Rakefile +27 -0
- data/bin/benchmark_pool +12 -0
- data/bin/gui.rb +236 -0
- data/bin/picker +12 -0
- data/bin/pool +118 -0
- data/lib/tournament/bracket.rb +352 -0
- data/lib/tournament/entry.rb +13 -0
- data/lib/tournament/pool.rb +341 -0
- data/lib/tournament/team.rb +19 -0
- data/lib/tournament.rb +56 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/tournament_spec.rb +8 -0
- data/static/shoes-icon.png +0 -0
- data/tasks/ann.rake +76 -0
- data/tasks/annotations.rake +22 -0
- data/tasks/bones.rake +40 -0
- data/tasks/doc.rake +48 -0
- data/tasks/gem.rake +116 -0
- data/tasks/manifest.rake +49 -0
- data/tasks/post_load.rake +32 -0
- data/tasks/rubyforge.rake +57 -0
- data/tasks/setup.rb +227 -0
- data/tasks/spec.rake +56 -0
- data/tasks/svn.rake +44 -0
- data/tasks/test.rake +38 -0
- data/test/test_tournament.rb +0 -0
- metadata +97 -0
@@ -0,0 +1,341 @@
|
|
1
|
+
# Represents a NCAA tournament pool. Contains 4 regions
|
2
|
+
# of 16 teams each. Champions of Region 1 and Region 2
|
3
|
+
# and champions of Region 3 and Region 4 play each
|
4
|
+
# other in the final four.
|
5
|
+
class Tournament::Pool
|
6
|
+
attr_reader :regions # The regions in the pool
|
7
|
+
attr_reader :entries # Tournament::Entry objects for participants
|
8
|
+
|
9
|
+
# Create a new empty pool with no Regions or Entries
|
10
|
+
def initialize
|
11
|
+
@regions = Array.new(4)
|
12
|
+
@entries = []
|
13
|
+
end
|
14
|
+
|
15
|
+
# add regions to the pool. Champ of region with index = 0 plays
|
16
|
+
# region with index = 1 and index == 2 plays index == 3.
|
17
|
+
def add_region(name, teams, index)
|
18
|
+
@regions[index] = {
|
19
|
+
:name => name,
|
20
|
+
:teams => teams
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
# Add an Tournament::Entry object to the pool
|
25
|
+
def add_entry(entry)
|
26
|
+
@entries << entry
|
27
|
+
end
|
28
|
+
|
29
|
+
# Add an Tournament::Entry object to the pool after reading the Tournament::Entry
|
30
|
+
# from the provided YAML file.
|
31
|
+
def add_entry_yaml(yaml)
|
32
|
+
@entries << YAML::load_file(yaml)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Creates a bracket for the pool by combining all the
|
36
|
+
# regions into one bracket of 64 teams. By default the
|
37
|
+
# bracket uses the basic scoring strategy.
|
38
|
+
def bracket
|
39
|
+
unless @bracket
|
40
|
+
if @regions.compact.size != 4
|
41
|
+
raise "Not all regions have been set."
|
42
|
+
end
|
43
|
+
all_teams = @regions.map do |region|
|
44
|
+
region[:teams]
|
45
|
+
end
|
46
|
+
all_teams = all_teams.flatten
|
47
|
+
@bracket = Tournament::Bracket.new(all_teams)
|
48
|
+
end
|
49
|
+
return @bracket
|
50
|
+
end
|
51
|
+
|
52
|
+
# Replaces the pool's bracket (as in after updating the bracket
|
53
|
+
# for game results)
|
54
|
+
def bracket=(new_bracket)
|
55
|
+
@bracket = new_bracket
|
56
|
+
end
|
57
|
+
|
58
|
+
# Creates a Pool object for the 2008 NCAA tournament
|
59
|
+
def self.ncaa_2008
|
60
|
+
pool = Tournament::Pool.new
|
61
|
+
pool.add_region("East",
|
62
|
+
[
|
63
|
+
Tournament::Team.new('North Carolina', 'UNC', 1),
|
64
|
+
Tournament::Team.new('Mt. St. Mary\'s', 'MSM', 16),
|
65
|
+
Tournament::Team.new('Indiana', 'Ind', 8),
|
66
|
+
Tournament::Team.new('Arkansas', 'Ark', 9),
|
67
|
+
Tournament::Team.new('Notre Dame', 'ND', 5),
|
68
|
+
Tournament::Team.new('George Mason', 'GM', 12),
|
69
|
+
Tournament::Team.new('Washington St.', 'WSt', 4),
|
70
|
+
Tournament::Team.new('Winthrop', 'Win', 13),
|
71
|
+
Tournament::Team.new('Oklahoma', 'Okl', 6),
|
72
|
+
Tournament::Team.new('St. Joseph\'s', 'StJ', 11),
|
73
|
+
Tournament::Team.new('Louisville', 'Lou', 3),
|
74
|
+
Tournament::Team.new('Boise St.', 'BSt', 14),
|
75
|
+
Tournament::Team.new('Butler', 'But', 7),
|
76
|
+
Tournament::Team.new('South Alabama', 'SAl', 10),
|
77
|
+
Tournament::Team.new('Tennessee', 'Ten', 2),
|
78
|
+
Tournament::Team.new('American', 'Am', 15)
|
79
|
+
],
|
80
|
+
0
|
81
|
+
)
|
82
|
+
pool.add_region("Midwest",
|
83
|
+
[
|
84
|
+
Tournament::Team.new('Kansas', 'Kan', 1),
|
85
|
+
Tournament::Team.new('Portland St.', 'PSt', 16),
|
86
|
+
Tournament::Team.new('UNLV', 'ULV', 8),
|
87
|
+
Tournament::Team.new('Kent St.', 'KSt', 9),
|
88
|
+
Tournament::Team.new('Clemson', 'Clm', 5),
|
89
|
+
Tournament::Team.new('Villanova', 'Vil', 12),
|
90
|
+
Tournament::Team.new('Vanderbilt', 'Van', 4),
|
91
|
+
Tournament::Team.new('Siena', 'Sie', 13),
|
92
|
+
Tournament::Team.new('USC', 'USC', 6),
|
93
|
+
Tournament::Team.new('Kansas St.', 'KSU', 11),
|
94
|
+
Tournament::Team.new('Wisconsin', 'Wis', 3),
|
95
|
+
Tournament::Team.new('CSU Fullerton', 'CSF', 14),
|
96
|
+
Tournament::Team.new('Gonzaga', 'Gon', 7),
|
97
|
+
Tournament::Team.new('Davidson', 'Dav', 10),
|
98
|
+
Tournament::Team.new('Georgetown', 'GT', 2),
|
99
|
+
Tournament::Team.new('UMBC', 'UBC', 15)
|
100
|
+
],
|
101
|
+
1
|
102
|
+
)
|
103
|
+
pool.add_region("South",
|
104
|
+
[
|
105
|
+
Tournament::Team.new('Memphis', 'Mem', 1),
|
106
|
+
Tournament::Team.new('TX Arlington', 'TxA', 16),
|
107
|
+
Tournament::Team.new('Mississippi St.', 'MiS', 8),
|
108
|
+
Tournament::Team.new('Oregon', 'Ore', 9),
|
109
|
+
Tournament::Team.new('Michigan St.', 'MSU', 5),
|
110
|
+
Tournament::Team.new('Temple', 'Tem', 12),
|
111
|
+
Tournament::Team.new('Pittsburgh', 'Pit', 4),
|
112
|
+
Tournament::Team.new('Oral Roberts', 'ORo', 13),
|
113
|
+
Tournament::Team.new('Marquette', 'Mar', 6),
|
114
|
+
Tournament::Team.new('Kentucky', 'Ken', 11),
|
115
|
+
Tournament::Team.new('Stanford', 'Sta', 3),
|
116
|
+
Tournament::Team.new('Cornell', 'Cor', 14),
|
117
|
+
Tournament::Team.new('Miami (FL)', 'Mia', 7),
|
118
|
+
Tournament::Team.new('St. Mary\'s', 'StM', 10),
|
119
|
+
Tournament::Team.new('Texas', 'Tex', 2),
|
120
|
+
Tournament::Team.new('Austin Peay', 'APe', 15)
|
121
|
+
],
|
122
|
+
2
|
123
|
+
)
|
124
|
+
pool.add_region("West",
|
125
|
+
[
|
126
|
+
Tournament::Team.new('UCLA', 'ULA', 1),
|
127
|
+
Tournament::Team.new('Mis. Valley St', 'MVS', 16),
|
128
|
+
Tournament::Team.new('BYU', 'BYU', 8),
|
129
|
+
Tournament::Team.new('Texas A&M', 'A&M', 9),
|
130
|
+
Tournament::Team.new('Drake', 'Dra', 5),
|
131
|
+
Tournament::Team.new('W. Kentucky', 'WKy', 12),
|
132
|
+
Tournament::Team.new('Connecticut', 'Con', 4),
|
133
|
+
Tournament::Team.new('San Diego', 'SD', 13),
|
134
|
+
Tournament::Team.new('Purdue', 'Pur', 6),
|
135
|
+
Tournament::Team.new('Baylor', 'Bay', 11),
|
136
|
+
Tournament::Team.new('Xavier', 'Xav', 3),
|
137
|
+
Tournament::Team.new('Georgia', 'UG', 14),
|
138
|
+
Tournament::Team.new('West Virginia', 'WVa', 7),
|
139
|
+
Tournament::Team.new('Arizona', 'UA', 10),
|
140
|
+
Tournament::Team.new('Duke', 'Duk', 2),
|
141
|
+
Tournament::Team.new('Belmont', 'Bel', 15)
|
142
|
+
],
|
143
|
+
3
|
144
|
+
)
|
145
|
+
return pool
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.test(num_picks = 10)
|
149
|
+
pool = ncaa_2008
|
150
|
+
b = pool.bracket
|
151
|
+
b.scoring_strategy = Tournament::Bracket::UpsetScoringStrategy.new
|
152
|
+
picks = (1..num_picks).map {|n| Tournament::Bracket.random_bracket(b.teams)}
|
153
|
+
# Play out the bracket
|
154
|
+
32.times { |n| b.set_winner(1,n+1, b.matchup(1, n+1)[rand(2)])}
|
155
|
+
16.times { |n| b.set_winner(2,n+1, b.matchup(2, n+1)[rand(2)])}
|
156
|
+
8.times { |n| b.set_winner(3,n+1, b.matchup(3, n+1)[rand(2)])}
|
157
|
+
4.times { |n| b.set_winner(4,n+1, b.matchup(4, n+1)[rand(2)])}
|
158
|
+
2.times { |n| b.set_winner(5,n+1, b.matchup(5, n+1)[rand(2)])}
|
159
|
+
1.times { |n| b.set_winner(6,n+1, b.matchup(6, n+1)[rand(2)])}
|
160
|
+
picks.each_with_index {|p, idx| pool.add_entry Tournament::Entry.new("picker_#{idx}", p) }
|
161
|
+
picks.each_with_index do |p, idx|
|
162
|
+
puts "Score #{idx+1}: #{p.score_against(b)}"
|
163
|
+
end
|
164
|
+
#pool.possibility_report
|
165
|
+
pool.leader_report
|
166
|
+
pool.entry_report
|
167
|
+
end
|
168
|
+
|
169
|
+
def leader_report
|
170
|
+
puts "Total games played: #{@bracket.games_played}"
|
171
|
+
puts "Number of entries: #{@entries.size}"
|
172
|
+
if @entries.size > 0
|
173
|
+
puts " Curr| Max | |Champ| Round Scores"
|
174
|
+
puts "Score|Score| Name |Live?|" + (1..bracket.rounds).to_a.map{|r| "%3d" % r}.join(" ")
|
175
|
+
sep ="-----+-----+---------------+-----+" + ("-" * 4 * bracket.rounds)
|
176
|
+
puts sep
|
177
|
+
@entries.sort_by {|e| -e.picks.score_against(bracket)}.each do |entry|
|
178
|
+
total = entry.picks.score_against(bracket)
|
179
|
+
max = entry.picks.maximum_score(bracket)
|
180
|
+
champ = entry.picks.champion
|
181
|
+
round_scores = []
|
182
|
+
1.upto(bracket.rounds) do |round|
|
183
|
+
scores = entry.picks.scores_for_round(round, bracket)
|
184
|
+
round_scores << scores.inject(0) {|sum, arr| sum += (arr[0] ? arr[0] : 0)}
|
185
|
+
end
|
186
|
+
puts "%5d|%5d|%15s|%3s %1s|%s" % [total, max, entry.name,
|
187
|
+
champ.short_name,(bracket.still_alive?(champ) ? 'Y' : 'N'), round_scores.map {|s| "%3d" % s}.join(" ")]
|
188
|
+
end
|
189
|
+
puts sep
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def score_report
|
194
|
+
# Compute scores
|
195
|
+
puts "Total games played: #{@bracket.games_played}"
|
196
|
+
puts "Number of entries: #{@entries.size}"
|
197
|
+
sep = "-----+---------------+----------------------------------------------------------------------------------"
|
198
|
+
if @entries.size > 0
|
199
|
+
puts "Total| Name | Round Scores"
|
200
|
+
puts sep
|
201
|
+
fmt1 = "%5d|%15s|%d: %3d %s"
|
202
|
+
fmt2 = " | |%d: %3d %s"
|
203
|
+
fmt3 = " | | %s"
|
204
|
+
@entries.sort_by {|e| -e.picks.score_against(bracket)}.each do |entry|
|
205
|
+
total = entry.picks.score_against(bracket)
|
206
|
+
1.upto(bracket.rounds) do |round|
|
207
|
+
scores = entry.picks.scores_for_round(round, bracket)
|
208
|
+
round_total = scores.inject(0) {|sum, arr| sum += (arr[0] ? arr[0] : 0)}
|
209
|
+
scores_str = scores.map{|arr| "#{arr[1].short_name}=#{arr[0] ? arr[0] : '?'}"}.join(" ")
|
210
|
+
if [1,2].include?(round)
|
211
|
+
scores_str_arr = Tournament::Pool.split_line(scores_str, 70)
|
212
|
+
if round == 1
|
213
|
+
puts fmt1 % [total, entry.name, round, round_total, scores_str_arr[0]]
|
214
|
+
else
|
215
|
+
puts fmt2 % [round, round_total, scores_str_arr[0]]
|
216
|
+
end
|
217
|
+
scores_str_arr[1..-1].each do |scores_str|
|
218
|
+
puts fmt3 % scores_str
|
219
|
+
end
|
220
|
+
else
|
221
|
+
puts fmt2 % [round, round_total, scores_str]
|
222
|
+
end
|
223
|
+
end
|
224
|
+
puts sep
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def self.split_line(str, len)
|
230
|
+
new_str = []
|
231
|
+
beg_idx = 0
|
232
|
+
end_idx = len - 1
|
233
|
+
while end_idx < str.length
|
234
|
+
end_idx += 1 while end_idx < (str.length - 1) && str[end_idx].chr != ' '
|
235
|
+
new_str << str[beg_idx,(end_idx-beg_idx+1)].strip
|
236
|
+
beg_idx = end_idx + 1
|
237
|
+
end_idx += len
|
238
|
+
end
|
239
|
+
new_str << str[beg_idx,str.length-1]
|
240
|
+
return new_str.reject {|s| s.nil? || s.length == 0}
|
241
|
+
end
|
242
|
+
|
243
|
+
def entry_report
|
244
|
+
puts "There are #{@entries.size} entries."
|
245
|
+
if @entries.size > 0
|
246
|
+
puts "".center(15) + "|" + "First Round".center(128)
|
247
|
+
puts "Name".center(15) + "|" + "Sweet 16".center(64) + "|" + "Elite 8".center(32) +
|
248
|
+
"|" + "Final 4".center(16) + "|" + "Final 2".center(8) + "|" + "Champion".center(15) +
|
249
|
+
"|" + "Tie Break"
|
250
|
+
puts ("-" * 15) + "+" + ("-" * 64) + "+" + ("-" * 32) +
|
251
|
+
"+" + ("-" * 16) + "+" + ("-" * 8) + "+" + ("-" * 15) +
|
252
|
+
"+" + ("-" * 10)
|
253
|
+
output = Proc.new do |name, bracket, tie_breaker|
|
254
|
+
first_round = bracket.winners[1].map {|t| "%s" % (t.short_name rescue 'Unk')}.join('-')
|
255
|
+
sweet_16 = bracket.winners[2].map {|t| "%s" % (t.short_name rescue 'Unk')}.join('-')
|
256
|
+
elite_8 = bracket.winners[3].map {|t| "%s" % (t.short_name rescue 'Unk')}.join('-')
|
257
|
+
final_4 = bracket.winners[4].map {|t| "%s" % (t.short_name rescue 'Unk')}.join('-')
|
258
|
+
final_2 = bracket.winners[5].map {|t| "%s" % (t.short_name rescue 'Unk')}.join('-')
|
259
|
+
champ = bracket.champion.name rescue 'Unk'
|
260
|
+
puts " |%128s" % first_round
|
261
|
+
puts "%15s|%64s|%32s|%16s|%8s|%15s|%s" %
|
262
|
+
[name, sweet_16, elite_8, final_4, final_2, champ, tie_breaker.to_s]
|
263
|
+
puts ("-" * 15) + "+" + ("-" * 64) + "+" + ("-" * 32) +
|
264
|
+
"+" + ("-" * 16) + "+" + ("-" * 8) + "+" + ("-" * 15) +
|
265
|
+
"+" + ("-" * 10)
|
266
|
+
end
|
267
|
+
|
268
|
+
output.call('Tournament', bracket, '-')
|
269
|
+
|
270
|
+
@entries.sort_by{|e| e.name}.each do |entry|
|
271
|
+
output.call(entry.name, entry.picks, entry.tie_breaker)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def region_report
|
277
|
+
puts " Region | Seed | Team "
|
278
|
+
current_idx = -1
|
279
|
+
@regions.each_with_index do |region, idx|
|
280
|
+
region[:teams].each do |team|
|
281
|
+
region_name = ''
|
282
|
+
if idx != current_idx
|
283
|
+
region_name = region[:name]
|
284
|
+
current_idx = idx
|
285
|
+
puts "--------+------+-------------------------"
|
286
|
+
end
|
287
|
+
puts "%8s|%6d|%25s" % [region_name, team.seed, "#{team.name} (#{team.short_name})"]
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def possibility_report
|
293
|
+
if @entries.size == 0
|
294
|
+
puts "There are no entries in the pool."
|
295
|
+
return
|
296
|
+
end
|
297
|
+
max_possible_score = @entries.map{|p| 0}
|
298
|
+
min_ranking = @entries.map{|p| @entries.size + 1}
|
299
|
+
times_winner = @entries.map{|p| 0 }
|
300
|
+
count = 0
|
301
|
+
start = Time.now.to_f
|
302
|
+
self.bracket.each_possible_bracket do |poss|
|
303
|
+
poss_scores = @entries.map{|p| p.picks.score_against(poss)}
|
304
|
+
sort_scores = poss_scores.sort.reverse
|
305
|
+
@entries.each_with_index do |entry, i|
|
306
|
+
score = poss_scores[i]
|
307
|
+
max_possible_score[i] = score if score > max_possible_score[i]
|
308
|
+
rank = sort_scores.index(score) + 1
|
309
|
+
min_ranking[i] = rank if rank < min_ranking[i]
|
310
|
+
times_winner[i] += 1 if rank == 1
|
311
|
+
end
|
312
|
+
count += 1
|
313
|
+
percentage = (count * 100 / self.bracket.number_of_outcomes) * 1000
|
314
|
+
if (percentage % 1000) == 0
|
315
|
+
percentage /= 1000
|
316
|
+
hashes = '#' * percentage + '>'
|
317
|
+
elapsed = Time.now.to_f - start
|
318
|
+
spp = elapsed / count
|
319
|
+
remaining = ((self.bracket.number_of_outcomes - count) * spp).to_i
|
320
|
+
print "\rCalculating: %3d%% +#{hashes.ljust(100, '-')}+ %5d seconds remaining" % [percentage, remaining]
|
321
|
+
|
322
|
+
end
|
323
|
+
end
|
324
|
+
puts "\n"
|
325
|
+
#puts "\n Max Scores: #{max_possible_score.inspect}"
|
326
|
+
#puts "Highest Place: #{min_ranking.inspect}"
|
327
|
+
#puts " Times Winner: #{times_winner.inspect}"
|
328
|
+
sort_array = []
|
329
|
+
times_winner.each_with_index { |n, i| sort_array << [n, i, min_ranking[i], max_possible_score[i]] }
|
330
|
+
sort_array = sort_array.sort_by {|arr| arr[0] == 0 ? (arr[2] == 0 ? -arr[3] : arr[2]) : -arr[0]}
|
331
|
+
#puts "SORT: #{sort_array.inspect}"
|
332
|
+
puts " Entry | Win Chance | Highest Place | Max Score | Tie Break "
|
333
|
+
puts "--------------------+------------+---------------+-----------+------------"
|
334
|
+
sort_array.each do |arr|
|
335
|
+
chance = arr[0].to_f * 100.0 / self.bracket.number_of_outcomes
|
336
|
+
puts "%19s | %10.2f | %13d | %9d | %d" %
|
337
|
+
[@entries[arr[1]].name, chance, min_ranking[arr[1]], max_possible_score[arr[1]], @entries[arr[1]].tie_breaker]
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Represents a team in a tournament Bracket
|
2
|
+
class Tournament::Team
|
3
|
+
attr_reader :name, :short_name, :seed
|
4
|
+
|
5
|
+
def initialize(name, short_name, seed)
|
6
|
+
@name = name
|
7
|
+
@short_name = short_name
|
8
|
+
@seed = seed
|
9
|
+
end
|
10
|
+
|
11
|
+
def ==(other)
|
12
|
+
return false unless Tournament::Team === other
|
13
|
+
@name == other.name && @short_name == other.short_name && @seed == other.seed
|
14
|
+
end
|
15
|
+
|
16
|
+
def eql?(other)
|
17
|
+
@name.eql?(other)
|
18
|
+
end
|
19
|
+
end
|
data/lib/tournament.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# $Id$
|
2
|
+
|
3
|
+
# Equivalent to a header guard in C/C++
|
4
|
+
# Used to prevent the class/module from being loaded more than once
|
5
|
+
unless defined? Tournament
|
6
|
+
|
7
|
+
module Tournament
|
8
|
+
|
9
|
+
# :stopdoc:
|
10
|
+
VERSION = '1.0.0'
|
11
|
+
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
12
|
+
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
13
|
+
# :startdoc:
|
14
|
+
|
15
|
+
# Returns the version string for the library.
|
16
|
+
#
|
17
|
+
def self.version
|
18
|
+
VERSION
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns the library path for the module. If any arguments are given,
|
22
|
+
# they will be joined to the end of the libray path using
|
23
|
+
# <tt>File.join</tt>.
|
24
|
+
#
|
25
|
+
def self.libpath( *args )
|
26
|
+
args.empty? ? LIBPATH : ::File.join(LIBPATH, *args)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns the lpath for the module. If any arguments are given,
|
30
|
+
# they will be joined to the end of the path using
|
31
|
+
# <tt>File.join</tt>.
|
32
|
+
#
|
33
|
+
def self.path( *args )
|
34
|
+
args.empty? ? PATH : ::File.join(PATH, *args)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Utility method used to rquire all files ending in .rb that lie in the
|
38
|
+
# directory below this file that has the same name as the filename passed
|
39
|
+
# in. Optionally, a specific _directory_ name can be passed in such that
|
40
|
+
# the _filename_ does not have to be equivalent to the directory.
|
41
|
+
#
|
42
|
+
def self.require_all_libs_relative_to( fname, dir = nil )
|
43
|
+
dir ||= ::File.basename(fname, '.*')
|
44
|
+
search_me = ::File.expand_path(
|
45
|
+
::File.join(::File.dirname(fname), dir, '**', '*.rb'))
|
46
|
+
|
47
|
+
Dir.glob(search_me).sort.each {|rb| require rb}
|
48
|
+
end
|
49
|
+
|
50
|
+
end # module Tournament
|
51
|
+
|
52
|
+
Tournament.require_all_libs_relative_to __FILE__
|
53
|
+
|
54
|
+
end # unless defined?
|
55
|
+
|
56
|
+
# EOF
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# $Id$
|
2
|
+
|
3
|
+
require File.expand_path(
|
4
|
+
File.join(File.dirname(__FILE__), %w[.. lib tournament]))
|
5
|
+
|
6
|
+
Spec::Runner.configure do |config|
|
7
|
+
# == Mock Framework
|
8
|
+
#
|
9
|
+
# RSpec uses it's own mocking framework by default. If you prefer to
|
10
|
+
# use mocha, flexmock or RR, uncomment the appropriate line:
|
11
|
+
#
|
12
|
+
# config.mock_with :mocha
|
13
|
+
# config.mock_with :flexmock
|
14
|
+
# config.mock_with :rr
|
15
|
+
end
|
16
|
+
|
17
|
+
# EOF
|
Binary file
|
data/tasks/ann.rake
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# $Id$
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'bones/smtp_tls'
|
5
|
+
rescue LoadError
|
6
|
+
require 'net/smtp'
|
7
|
+
end
|
8
|
+
require 'time'
|
9
|
+
|
10
|
+
namespace :ann do
|
11
|
+
|
12
|
+
file PROJ.ann_file do
|
13
|
+
puts "Generating #{PROJ.ann_file}"
|
14
|
+
File.open(PROJ.ann_file,'w') do |fd|
|
15
|
+
fd.puts("#{PROJ.name} version #{PROJ.version}")
|
16
|
+
fd.puts(" by #{Array(PROJ.authors).first}") if PROJ.authors
|
17
|
+
fd.puts(" #{PROJ.url}") if PROJ.url
|
18
|
+
fd.puts(" (the \"#{PROJ.release_name}\" release)") if PROJ.release_name
|
19
|
+
fd.puts
|
20
|
+
fd.puts("== DESCRIPTION")
|
21
|
+
fd.puts
|
22
|
+
fd.puts(PROJ.description)
|
23
|
+
fd.puts
|
24
|
+
fd.puts(PROJ.changes.sub(%r/^.*$/, '== CHANGES'))
|
25
|
+
fd.puts
|
26
|
+
PROJ.ann_paragraphs.each do |p|
|
27
|
+
fd.puts "== #{p.upcase}"
|
28
|
+
fd.puts
|
29
|
+
fd.puts paragraphs_of(PROJ.readme_file, p).join("\n\n")
|
30
|
+
fd.puts
|
31
|
+
end
|
32
|
+
fd.puts PROJ.ann_text if PROJ.ann_text
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
desc "Create an announcement file"
|
37
|
+
task :announcement => PROJ.ann_file
|
38
|
+
|
39
|
+
desc "Send an email announcement"
|
40
|
+
task :email => PROJ.ann_file do
|
41
|
+
from = PROJ.ann_email[:from] || PROJ.email
|
42
|
+
to = Array(PROJ.ann_email[:to])
|
43
|
+
|
44
|
+
### build a mail header for RFC 822
|
45
|
+
rfc822msg = "From: #{from}\n"
|
46
|
+
rfc822msg << "To: #{to.join(',')}\n"
|
47
|
+
rfc822msg << "Subject: [ANN] #{PROJ.name} #{PROJ.version}"
|
48
|
+
rfc822msg << " (#{PROJ.release_name})" if PROJ.release_name
|
49
|
+
rfc822msg << "\n"
|
50
|
+
rfc822msg << "Date: #{Time.new.rfc822}\n"
|
51
|
+
rfc822msg << "Message-Id: "
|
52
|
+
rfc822msg << "<#{"%.8f" % Time.now.to_f}@#{PROJ.ann_email[:domain]}>\n\n"
|
53
|
+
rfc822msg << File.read(PROJ.ann_file)
|
54
|
+
|
55
|
+
params = [:server, :port, :domain, :acct, :passwd, :authtype].map do |key|
|
56
|
+
PROJ.ann_email[key]
|
57
|
+
end
|
58
|
+
|
59
|
+
params[3] = PROJ.email if params[3].nil?
|
60
|
+
|
61
|
+
if params[4].nil?
|
62
|
+
STDOUT.write "Please enter your e-mail password (#{params[3]}): "
|
63
|
+
params[4] = STDIN.gets.chomp
|
64
|
+
end
|
65
|
+
|
66
|
+
### send email
|
67
|
+
Net::SMTP.start(*params) {|smtp| smtp.sendmail(rfc822msg, from, to)}
|
68
|
+
end
|
69
|
+
end # namespace :ann
|
70
|
+
|
71
|
+
desc 'Alias to ann:announcement'
|
72
|
+
task :ann => 'ann:announcement'
|
73
|
+
|
74
|
+
CLOBBER << PROJ.ann_file
|
75
|
+
|
76
|
+
# EOF
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# $Id$
|
2
|
+
|
3
|
+
if HAVE_BONES
|
4
|
+
|
5
|
+
desc "Enumerate all annotations"
|
6
|
+
task :notes do
|
7
|
+
Bones::AnnotationExtractor.enumerate(
|
8
|
+
PROJ, PROJ.annotation_tags.join('|'), :tag => true)
|
9
|
+
end
|
10
|
+
|
11
|
+
namespace :notes do
|
12
|
+
PROJ.annotation_tags.each do |tag|
|
13
|
+
desc "Enumerate all #{tag} annotations"
|
14
|
+
task tag.downcase.to_sym do
|
15
|
+
Bones::AnnotationExtractor.enumerate(PROJ, tag)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end # if HAVE_BONES
|
21
|
+
|
22
|
+
# EOF
|
data/tasks/bones.rake
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# $Id$
|
2
|
+
|
3
|
+
require 'pp'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
namespace :bones do
|
7
|
+
|
8
|
+
desc 'Show the PROJ open struct'
|
9
|
+
task :debug do |t|
|
10
|
+
atr = if ARGV.length == 2
|
11
|
+
t.application.top_level_tasks.pop
|
12
|
+
end
|
13
|
+
sio = StringIO.new
|
14
|
+
sep = "\n" + ' '*27
|
15
|
+
fmt = "%23s => %s"
|
16
|
+
|
17
|
+
if atr
|
18
|
+
PP.pp(PROJ.send(atr.to_sym), sio, 49)
|
19
|
+
sio.seek 0
|
20
|
+
val = sio.read
|
21
|
+
val = val.split("\n").join(sep)
|
22
|
+
|
23
|
+
puts fmt % [atr, val]
|
24
|
+
else
|
25
|
+
h = PROJ.instance_variable_get(:@table)
|
26
|
+
h.keys.map {|k| k.to_s}.sort.each do |k|
|
27
|
+
sio.truncate 0
|
28
|
+
PP.pp(h[k.to_sym], sio, 49)
|
29
|
+
sio.seek 0
|
30
|
+
val = sio.read
|
31
|
+
val = val.split("\n").join(sep)
|
32
|
+
|
33
|
+
puts fmt % [k, val]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end # namespace :bones
|
39
|
+
|
40
|
+
# EOF
|
data/tasks/doc.rake
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# $Id$
|
2
|
+
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
namespace :doc do
|
6
|
+
|
7
|
+
desc 'Generate RDoc documentation'
|
8
|
+
Rake::RDocTask.new do |rd|
|
9
|
+
rd.main = PROJ.rdoc_main
|
10
|
+
rd.rdoc_dir = PROJ.rdoc_dir
|
11
|
+
|
12
|
+
incl = Regexp.new(PROJ.rdoc_include.join('|'))
|
13
|
+
excl = Regexp.new(PROJ.rdoc_exclude.join('|'))
|
14
|
+
files = PROJ.files.find_all do |fn|
|
15
|
+
case fn
|
16
|
+
when excl; false
|
17
|
+
when incl; true
|
18
|
+
else false end
|
19
|
+
end
|
20
|
+
rd.rdoc_files.push(*files)
|
21
|
+
|
22
|
+
title = "#{PROJ.name}-#{PROJ.version} Documentation"
|
23
|
+
title = "#{PROJ.rubyforge_name}'s " + title if PROJ.rubyforge_name != title
|
24
|
+
|
25
|
+
rd.options << "-t #{title}"
|
26
|
+
rd.options.concat(PROJ.rdoc_opts)
|
27
|
+
end
|
28
|
+
|
29
|
+
desc 'Generate ri locally for testing'
|
30
|
+
task :ri => :clobber_ri do
|
31
|
+
sh "#{RDOC} --ri -o ri ."
|
32
|
+
end
|
33
|
+
|
34
|
+
task :clobber_ri do
|
35
|
+
rm_r 'ri' rescue nil
|
36
|
+
end
|
37
|
+
|
38
|
+
end # namespace :doc
|
39
|
+
|
40
|
+
desc 'Alias to doc:rdoc'
|
41
|
+
task :doc => 'doc:rdoc'
|
42
|
+
|
43
|
+
desc 'Remove all build products'
|
44
|
+
task :clobber => %w(doc:clobber_rdoc doc:clobber_ri)
|
45
|
+
|
46
|
+
remove_desc_for_task %w(doc:clobber_rdoc)
|
47
|
+
|
48
|
+
# EOF
|