tournament 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|