opensprints-core 0.5.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/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +625 -0
- data/README.rdoc +18 -0
- data/Rakefile +58 -0
- data/VERSION +1 -0
- data/lib/categorization.rb +5 -0
- data/lib/category.rb +15 -0
- data/lib/migrations/001_create_categories.rb +14 -0
- data/lib/migrations/002_create_racers.rb +14 -0
- data/lib/migrations/003_create_categorizations.rb +17 -0
- data/lib/migrations/004_create_races.rb +24 -0
- data/lib/migrations/005_create_tournaments.rb +24 -0
- data/lib/opensprints-core.rb +21 -0
- data/lib/race.rb +27 -0
- data/lib/race_participation.rb +60 -0
- data/lib/racer.rb +12 -0
- data/lib/tournament.rb +46 -0
- data/lib/tournament_participation.rb +31 -0
- data/test/opensprints-core_spec.rb +7 -0
- data/test/spec_helper.rb +8 -0
- data/test/test_category.rb +55 -0
- data/test/test_race.rb +112 -0
- data/test/test_race_paricipation.rb +37 -0
- data/test/test_racer.rb +45 -0
- data/test/test_tournament.rb +155 -0
- data/test/test_tournament_participation.rb +52 -0
- metadata +108 -0
data/Rakefile
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rake'
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
require 'jeweler'
|
|
6
|
+
Jeweler::Tasks.new do |gem|
|
|
7
|
+
gem.name = "opensprints-core"
|
|
8
|
+
gem.summary = %Q{A lib for interacting with opensprints race records data}
|
|
9
|
+
gem.description = %Q{This contains everything common between the stats app and the opensprints shoes app. If you want to write a new application to display opensprints stats or run races, this is your gem. }
|
|
10
|
+
gem.email = "evanfarrar@gmail.com"
|
|
11
|
+
gem.homepage = "http://github.com/evanfarrar/opensprints-core"
|
|
12
|
+
gem.authors = ["Evan Farrar"]
|
|
13
|
+
gem.add_development_dependency "bacon"
|
|
14
|
+
gem.add_dependency "sequel"
|
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
|
16
|
+
end
|
|
17
|
+
Jeweler::GemcutterTasks.new
|
|
18
|
+
rescue LoadError
|
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
require 'rake/testtask'
|
|
23
|
+
Rake::TestTask.new(:spec) do |spec|
|
|
24
|
+
spec.libs << 'lib' << 'spec'
|
|
25
|
+
spec.pattern = 'test/test_*.rb'
|
|
26
|
+
spec.verbose = true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
begin
|
|
30
|
+
require 'rcov/rcovtask'
|
|
31
|
+
Rcov::RcovTask.new do |spec|
|
|
32
|
+
spec.libs << 'spec'
|
|
33
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
|
34
|
+
spec.verbose = true
|
|
35
|
+
end
|
|
36
|
+
rescue LoadError
|
|
37
|
+
task :rcov do
|
|
38
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
task :spec => :check_dependencies
|
|
43
|
+
|
|
44
|
+
task :default => :spec
|
|
45
|
+
|
|
46
|
+
require 'rake/rdoctask'
|
|
47
|
+
Rake::RDocTask.new do |rdoc|
|
|
48
|
+
if File.exist?('VERSION')
|
|
49
|
+
version = File.read('VERSION')
|
|
50
|
+
else
|
|
51
|
+
version = ""
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
55
|
+
rdoc.title = "opensprints-core #{version}"
|
|
56
|
+
rdoc.rdoc_files.include('README*')
|
|
57
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
58
|
+
end
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.5.1
|
data/lib/category.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class Category < Sequel::Model
|
|
2
|
+
def to_s
|
|
3
|
+
self.name
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def Category.next_after(other)
|
|
7
|
+
if(other)
|
|
8
|
+
category = Category.filter(:id > other.pk).order(:id).first
|
|
9
|
+
else
|
|
10
|
+
category = Category.order(:id).first
|
|
11
|
+
end
|
|
12
|
+
category ? category.pk : nil
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class CreateCategorizations < Sequel::Migration
|
|
2
|
+
def up
|
|
3
|
+
create_table(:categorizations, :ignore_index_errors=>true) do
|
|
4
|
+
primary_key :id
|
|
5
|
+
Integer :racer_id, :null=>false
|
|
6
|
+
Integer :category_id, :null=>false
|
|
7
|
+
|
|
8
|
+
index [:category_id], :name=>:index_categorizations_category
|
|
9
|
+
index [:racer_id], :name=>:index_categorizations_racer
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def down
|
|
14
|
+
drop_table(:categorizations)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
class CreateRaces < Sequel::Migration
|
|
2
|
+
def up
|
|
3
|
+
create_table :races do
|
|
4
|
+
primary_key :id
|
|
5
|
+
TrueClass :raced, :default => false
|
|
6
|
+
Integer :tournament_id
|
|
7
|
+
end
|
|
8
|
+
create_table(:race_participations, :ignore_index_errors=>true) do
|
|
9
|
+
primary_key :id
|
|
10
|
+
BigDecimal :finish_time, :size=>[10, 0]
|
|
11
|
+
Integer :racer_id, :null=>false
|
|
12
|
+
Integer :race_id, :null=>false
|
|
13
|
+
|
|
14
|
+
index [:race_id], :name=>:index_race_participations_race
|
|
15
|
+
index [:racer_id], :name=>:index_race_participations_racer
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def down
|
|
20
|
+
drop_table(:races)
|
|
21
|
+
drop_table(:race_participations)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
class CreateTournaments < Sequel::Migration
|
|
2
|
+
def up
|
|
3
|
+
create_table(:tournament_participations, :ignore_index_errors=>true) do
|
|
4
|
+
primary_key :id
|
|
5
|
+
TrueClass :eliminated
|
|
6
|
+
Integer :racer_id, :null=>false
|
|
7
|
+
Integer :tournament_id, :null=>false
|
|
8
|
+
|
|
9
|
+
index [:racer_id], :name=>:index_tournament_participations_racer
|
|
10
|
+
index [:tournament_id], :name=>:index_tournament_participations_tournament
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
create_table(:tournaments) do
|
|
14
|
+
primary_key :id
|
|
15
|
+
String :name, :size=> 140
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def down
|
|
20
|
+
drop_table(:tournaments)
|
|
21
|
+
drop_table(:tournament_participations)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
|
3
|
+
|
|
4
|
+
require 'sequel'
|
|
5
|
+
require 'sequel/extensions/migration'
|
|
6
|
+
require 'sequel/extensions/schema_dumper'
|
|
7
|
+
require 'sqlite3'
|
|
8
|
+
if(defined? Shoes) #Real environment
|
|
9
|
+
DB = Sequel.connect("sqlite://#{DATABASE_PATH}")
|
|
10
|
+
else #Test environment
|
|
11
|
+
DB = Sequel.connect("sqlite::memory:")
|
|
12
|
+
end
|
|
13
|
+
Sequel::Migrator.apply(DB, 'lib/migrations/')
|
|
14
|
+
Infinity = 1/0.0
|
|
15
|
+
require 'lib/tournament'
|
|
16
|
+
require 'lib/tournament_participation'
|
|
17
|
+
require 'lib/category'
|
|
18
|
+
require 'lib/racer'
|
|
19
|
+
require 'lib/race'
|
|
20
|
+
require 'lib/race_participation'
|
|
21
|
+
require 'lib/categorization'
|
data/lib/race.rb
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
class Race < Sequel::Model
|
|
2
|
+
one_to_many :race_participations
|
|
3
|
+
many_to_many :racers, :join_table => :race_participations
|
|
4
|
+
many_to_one :tournament
|
|
5
|
+
|
|
6
|
+
def finished?
|
|
7
|
+
race_participations.all?(&:finish_time)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def winner
|
|
11
|
+
if unraced?
|
|
12
|
+
return nil
|
|
13
|
+
else
|
|
14
|
+
standings = self.race_participations.sort_by { |racer| racer.finish_time||Infinity }
|
|
15
|
+
standings.first
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def unraced?
|
|
20
|
+
!raced
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def next_race
|
|
24
|
+
(Race.filter(:raced => false, :tournament_id => tournament.pk).all - [self]).first
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
class RaceParticipation < Sequel::Model
|
|
2
|
+
many_to_one :race
|
|
3
|
+
many_to_one :racer
|
|
4
|
+
|
|
5
|
+
def color
|
|
6
|
+
@color ||= $BIKES[self.race.race_participations.index(self)]
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
attr_accessor :ticks
|
|
10
|
+
|
|
11
|
+
def percent_complete
|
|
12
|
+
[1.0, self.distance / $RACE_DISTANCE].min
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def distance
|
|
16
|
+
if self.ticks
|
|
17
|
+
self.ticks * $ROLLER_CIRCUMFERENCE
|
|
18
|
+
else
|
|
19
|
+
0.0
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
METERS_PER_MILLISECOND_TO_MILES_PER_HOUR = 2236.93629
|
|
24
|
+
METERS_PER_MILLISECOND_TO_KILOMETERS_PER_HOUR = 3600.0
|
|
25
|
+
def unit_conversion
|
|
26
|
+
case UNIT_SYSTEM
|
|
27
|
+
when :mph
|
|
28
|
+
@unit_conversion = METERS_PER_MILLISECOND_TO_MILES_PER_HOUR
|
|
29
|
+
when :kph
|
|
30
|
+
@unit_conversion = METERS_PER_MILLISECOND_TO_KILOMETERS_PER_HOUR
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# yikes.
|
|
35
|
+
def speed(time)
|
|
36
|
+
@unit_conversion ||= unit_conversion
|
|
37
|
+
@distance = self.distance
|
|
38
|
+
@time = time
|
|
39
|
+
@distance_old ||= 0
|
|
40
|
+
@time_old ||= 0
|
|
41
|
+
@speed ||= 0
|
|
42
|
+
if(@time_old > @time)
|
|
43
|
+
@time_old = time
|
|
44
|
+
end
|
|
45
|
+
if time == 0
|
|
46
|
+
0
|
|
47
|
+
else
|
|
48
|
+
if(@time-@time_old > 999)
|
|
49
|
+
if(@distance_old > 0)
|
|
50
|
+
@speed = "%.2f" % (((@distance - @distance_old) / (@time - @time_old)) * @unit_conversion).to_f
|
|
51
|
+
else
|
|
52
|
+
@speed = 0
|
|
53
|
+
end
|
|
54
|
+
@distance_old = @distance
|
|
55
|
+
@time_old = @time
|
|
56
|
+
end
|
|
57
|
+
@speed
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/racer.rb
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class Racer < Sequel::Model
|
|
2
|
+
one_to_many :categorizations
|
|
3
|
+
many_to_many :categories, :join_table => :categorizations
|
|
4
|
+
def to_s
|
|
5
|
+
name
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def best_time
|
|
9
|
+
best = DB[:race_participations].filter(:racer_id => self.pk).order(:finish_time).select(:finish_time).first
|
|
10
|
+
best[:finish_time] if best
|
|
11
|
+
end
|
|
12
|
+
end
|
data/lib/tournament.rb
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
class Tournament < Sequel::Model
|
|
2
|
+
one_to_many :tournament_participations
|
|
3
|
+
many_to_many :racers, :join_table => :tournament_participations
|
|
4
|
+
one_to_many :races
|
|
5
|
+
|
|
6
|
+
def unregistered_racers
|
|
7
|
+
Racer.exclude(:id => Racer.join(:tournament_participations, :racer_id => :id).select(:racers__id)).all
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def autofill(racer_list=nil)
|
|
11
|
+
racer_list ||= reload.unmatched_racers.to_a
|
|
12
|
+
racer_list.each_slice($BIKES.length) { |a|
|
|
13
|
+
race = Race.create(:tournament => self)
|
|
14
|
+
a.map{|r| RaceParticipation.create(:racer => r, :race => race)}
|
|
15
|
+
}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def unmatched_racers
|
|
19
|
+
racers - matched_racers - tournament_participations.select{|tp|tp.eliminated}.map{|tp|tp.racer}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def matched_racers
|
|
23
|
+
matched = []
|
|
24
|
+
matches = self.races.select{|race| race.unraced? }
|
|
25
|
+
matches.each { |race|
|
|
26
|
+
race.race_participations.each {|rp|
|
|
27
|
+
matched << rp.racer
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
matched
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
#TODO: test
|
|
34
|
+
def never_raced_and_not_eliminated
|
|
35
|
+
matched = []
|
|
36
|
+
races.each { |race|
|
|
37
|
+
race.race_participations.each {|rp|
|
|
38
|
+
matched << rp.racer
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
racers - matched - tournament_participations.select{|tp|tp.eliminated}.map{|tp|tp.racer}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
class TournamentParticipation < Sequel::Model
|
|
2
|
+
many_to_one :racer
|
|
3
|
+
many_to_one :tournament
|
|
4
|
+
|
|
5
|
+
def best_time
|
|
6
|
+
best = DB[:race_participations].filter(:racer_id => racer.id).exclude(:finish_time => nil).join(:races, :tournament_id => tournament.id).filter(:raced => true).order(:finish_time).select(:finish_time).first
|
|
7
|
+
|
|
8
|
+
best[:finish_time] if best
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def rank
|
|
12
|
+
standings = self.tournament.tournament_participations.sort_by{|tp|tp.best_time||Infinity}
|
|
13
|
+
standings.index(self)+1
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def losses
|
|
17
|
+
RaceParticipation.filter(:racer_id => racer.id).join(:races, :tournament_id => tournament.id).group(:id).all.select do |rp|
|
|
18
|
+
winner = rp.race ? rp.race.winner : nil
|
|
19
|
+
winner && (winner.racer.pk != self.racer.pk)
|
|
20
|
+
end.length
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def eliminate
|
|
24
|
+
self.update(:eliminated => true)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def race_participations
|
|
28
|
+
RaceParticipation.filter(:racer_id => racer.id).join(:races, :tournament_id => tournament.id).group(:id).all
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
data/test/spec_helper.rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'bacon'
|
|
3
|
+
require 'lib/opensprints-core.rb'
|
|
4
|
+
|
|
5
|
+
describe 'A category' do
|
|
6
|
+
before do
|
|
7
|
+
@category = Category.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'should have a name' do
|
|
11
|
+
@category.name = "Women"
|
|
12
|
+
@category.name.should=="Women"
|
|
13
|
+
end
|
|
14
|
+
it 'should save' do
|
|
15
|
+
c = Category.new(:name => "Test")
|
|
16
|
+
(!!c.save).should==(true)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'should load from the database' do
|
|
20
|
+
c = Category.new(:name => "Test")
|
|
21
|
+
(!!c.save).should==(true)
|
|
22
|
+
Category[c.id].should.not.be.nil?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'should be convertible to string' do
|
|
26
|
+
Category.new(:name => "Men").to_s.should=="Men"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe "next after" do
|
|
30
|
+
before do
|
|
31
|
+
Category.all.each{|c|c.destroy}
|
|
32
|
+
@c1 = Category.create(:name => "one")
|
|
33
|
+
@c2 = Category.create(:name => "two")
|
|
34
|
+
end
|
|
35
|
+
it "should know what category comes after no category" do
|
|
36
|
+
Category.next_after(nil).should==(@c1.pk)
|
|
37
|
+
end
|
|
38
|
+
it "should know what category comes after a given category" do
|
|
39
|
+
Category.next_after(@c1).should==(@c2.pk)
|
|
40
|
+
end
|
|
41
|
+
it "should know no category comes after a the last category" do
|
|
42
|
+
Category.next_after(@c2).should==(nil)
|
|
43
|
+
end
|
|
44
|
+
it 'should work with no categories' do
|
|
45
|
+
Category.all.each{|c|c.destroy}
|
|
46
|
+
Category.next_after(nil).should==(nil)
|
|
47
|
+
end
|
|
48
|
+
after do
|
|
49
|
+
Category.all.each{|c|c.destroy}
|
|
50
|
+
Category.create(:name => "Men")
|
|
51
|
+
Category.create(:name => "Women")
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
data/test/test_race.rb
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'bacon'
|
|
3
|
+
require 'lib/opensprints-core.rb'
|
|
4
|
+
$BIKES = ["red","blue"]
|
|
5
|
+
|
|
6
|
+
describe 'A race' do
|
|
7
|
+
before do
|
|
8
|
+
@race = Race.new
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'should be able to create race with racers' do
|
|
12
|
+
racers = [Racer.create, Racer.create, Racer.create, Racer.create]
|
|
13
|
+
r = Race.create
|
|
14
|
+
racers.map{|e| RaceParticipation.create(:racer => e, :race => r)}
|
|
15
|
+
r.save
|
|
16
|
+
Race[r.pk].racers.length.should==4
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
describe "racers" do
|
|
20
|
+
it "should have a color" do
|
|
21
|
+
racers = [Racer.create, Racer.create, Racer.create, Racer.create]
|
|
22
|
+
r = Race.create
|
|
23
|
+
racers.map{|e| RaceParticipation.create(:racer => e, :race => r)}
|
|
24
|
+
r.save
|
|
25
|
+
r.race_participations.first.color.should== $BIKES.first
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'should have times' do
|
|
30
|
+
racers = [Racer.create, Racer.create, Racer.create, Racer.create]
|
|
31
|
+
r = Race.create
|
|
32
|
+
racers.map{|e| RaceParticipation.create(:racer => e, :race => r)}
|
|
33
|
+
r.save
|
|
34
|
+
r.race_participations.first.finish_time = 10.116
|
|
35
|
+
r.race_participations.first.save
|
|
36
|
+
r.reload
|
|
37
|
+
r.race_participations.first.finish_time.should==(10.116)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'should be finished if everyone has times.' do
|
|
41
|
+
racers = [Racer.create, Racer.create, Racer.create, Racer.create]
|
|
42
|
+
r = Race.create
|
|
43
|
+
racers.map{|e| RaceParticipation.create(:racer => e, :race => r)}
|
|
44
|
+
r.save
|
|
45
|
+
r.race_participations.first.finish_time = 10.116
|
|
46
|
+
r.race_participations.first.save
|
|
47
|
+
r.reload
|
|
48
|
+
r.finished?.should==(false)
|
|
49
|
+
r.race_participations.each{|rp|rp.finish_time = 10.116; rp.save}
|
|
50
|
+
r.reload
|
|
51
|
+
r.finished?.should==(true)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
describe 'winner' do
|
|
55
|
+
it 'should be the lowest (fastest) time' do
|
|
56
|
+
racers = [Racer.create(:name => "Steve"),
|
|
57
|
+
Racer.create(:name => "Joe")]
|
|
58
|
+
r = Race.create(:raced => true)
|
|
59
|
+
racers.each{|racer| RaceParticipation.create(:racer => racer,:race => r)}
|
|
60
|
+
r.save
|
|
61
|
+
r.race_participations.first.finish_time = 10.116
|
|
62
|
+
r.save
|
|
63
|
+
r.reload
|
|
64
|
+
r.winner.racer.name.should==("Steve")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
it 'should be nil if the race has not been run' do
|
|
68
|
+
racers = [Racer.create(:name => "Steve"),
|
|
69
|
+
Racer.create(:name => "Joe")]
|
|
70
|
+
r = Race.create
|
|
71
|
+
racers.each{|racer| RaceParticipation.create(:racer => racer,:race => r)}
|
|
72
|
+
r.save
|
|
73
|
+
r.race_participations.first.finish_time = 10.116
|
|
74
|
+
r.save
|
|
75
|
+
r.reload
|
|
76
|
+
r.winner.should==(nil)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
describe 'raced' do
|
|
80
|
+
it 'should track whether or not the race has been run' do
|
|
81
|
+
r = Race.create
|
|
82
|
+
r.raced.should==(false)
|
|
83
|
+
r.raced = true
|
|
84
|
+
r.raced.should==(true)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it 'should have a converse: unraced?' do
|
|
88
|
+
r = Race.create
|
|
89
|
+
r.unraced?.should==(true)
|
|
90
|
+
r.raced = true
|
|
91
|
+
r.unraced?.should==(false)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
describe 'next race' do
|
|
96
|
+
it 'should be the next one after this one' do
|
|
97
|
+
t = Tournament.create
|
|
98
|
+
r1 = Race.create(:tournament => t)
|
|
99
|
+
r2 = Race.create(:tournament => t)
|
|
100
|
+
r1.next_race.should==(r2)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
it 'should only show the next unraced race' do
|
|
104
|
+
t = Tournament.create
|
|
105
|
+
r1 = Race.create(:tournament => t)
|
|
106
|
+
r2 = Race.create(:tournament => t, :raced => true)
|
|
107
|
+
r3 = Race.create(:tournament => t)
|
|
108
|
+
r1.next_race.should==(r3)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'bacon'
|
|
3
|
+
require 'lib/opensprints-core.rb'
|
|
4
|
+
|
|
5
|
+
describe 'A race participation' do
|
|
6
|
+
before do
|
|
7
|
+
@r = RaceParticipation.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'should have a temporary place for keeping track of ticks' do
|
|
11
|
+
@r.ticks = 12
|
|
12
|
+
@r.ticks.should==(12)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'should have a distance' do
|
|
16
|
+
$ROLLER_CIRCUMFERENCE = 0.5
|
|
17
|
+
@r.ticks = 42
|
|
18
|
+
@r.distance.should==(21.0)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe 'percent complete' do
|
|
22
|
+
it 'should be the ratio of race distance to distance' do
|
|
23
|
+
$RACE_DISTANCE = 84.0
|
|
24
|
+
$ROLLER_CIRCUMFERENCE = 0.5
|
|
25
|
+
@r.ticks = 42
|
|
26
|
+
@r.percent_complete.should==(0.25)
|
|
27
|
+
@r.ticks = 0
|
|
28
|
+
@r.percent_complete.should==(0.0)
|
|
29
|
+
@r.ticks = 1200.0
|
|
30
|
+
@r.percent_complete.should==(1.0)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe 'speed' do
|
|
35
|
+
# it 'should give a speed based on a certain time'
|
|
36
|
+
end
|
|
37
|
+
end
|
data/test/test_racer.rb
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'bacon'
|
|
3
|
+
require 'lib/opensprints-core.rb'
|
|
4
|
+
|
|
5
|
+
describe 'A racer' do
|
|
6
|
+
before do
|
|
7
|
+
@racer = Racer.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it 'should have a name' do
|
|
11
|
+
@racer.name = "Evan F"
|
|
12
|
+
@racer.name.should=="Evan F"
|
|
13
|
+
@racer.to_s.should=="Evan F"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'should save' do
|
|
17
|
+
r = Racer.new(:name => "Test")
|
|
18
|
+
(!!r.save).should==(true)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'should load from the database' do
|
|
22
|
+
r = Racer.new(:name => "Test")
|
|
23
|
+
(!!r.save).should==(true)
|
|
24
|
+
Racer[r.pk].should.not.be.nil?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should have categories" do
|
|
28
|
+
c = Category.create(:name => "Men")
|
|
29
|
+
# @racer.categories << c
|
|
30
|
+
|
|
31
|
+
@racer.save
|
|
32
|
+
Categorization.create(:category => c, :racer => @racer)
|
|
33
|
+
@racer.reload.categorizations.map(&:category).should.include? c
|
|
34
|
+
@racer.categories.should.include? c
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "should know the best time ever" do
|
|
38
|
+
racer = Racer.create
|
|
39
|
+
[7.0, 12.0, 2.7, 10.0].each do |time|
|
|
40
|
+
race = Race.create
|
|
41
|
+
RaceParticipation.create(:finish_time => time, :race => race, :racer => racer)
|
|
42
|
+
end
|
|
43
|
+
racer.best_time.should==(2.7)
|
|
44
|
+
end
|
|
45
|
+
end
|