radiant-race_results-extension 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. data/README.md +8 -0
  2. data/Rakefile +137 -0
  3. data/app/controllers/admin/race_clubs_controller.rb +15 -0
  4. data/app/controllers/admin/race_competitors_controller.rb +15 -0
  5. data/app/controllers/admin/race_instances_controller.rb +17 -0
  6. data/app/controllers/admin/races_controller.rb +35 -0
  7. data/app/controllers/race_instances_controller.rb +23 -0
  8. data/app/controllers/race_performances_controller.rb +16 -0
  9. data/app/controllers/races_controller.rb +13 -0
  10. data/app/helpers/races_helper.rb +38 -0
  11. data/app/models/race.rb +64 -0
  12. data/app/models/race_category.rb +114 -0
  13. data/app/models/race_checkpoint.rb +31 -0
  14. data/app/models/race_checkpoint_time.rb +64 -0
  15. data/app/models/race_club.rb +48 -0
  16. data/app/models/race_club_alias.rb +9 -0
  17. data/app/models/race_competitor.rb +20 -0
  18. data/app/models/race_instance.rb +178 -0
  19. data/app/models/race_page.rb +69 -0
  20. data/app/models/race_performance.rb +175 -0
  21. data/app/models/race_performance_status.rb +58 -0
  22. data/app/models/race_record.rb +27 -0
  23. data/app/views/admin/race_checkpoints/_checkpoint.html.haml +19 -0
  24. data/app/views/admin/race_club_aliases/_race_club_alias.html.haml +10 -0
  25. data/app/views/admin/race_clubs/_club.html.haml +19 -0
  26. data/app/views/admin/race_clubs/_form.html.haml +21 -0
  27. data/app/views/admin/race_clubs/edit.html.haml +20 -0
  28. data/app/views/admin/race_clubs/index.html.haml +27 -0
  29. data/app/views/admin/race_competitors/_competitor.html.haml +14 -0
  30. data/app/views/admin/race_competitors/_form.html.haml +21 -0
  31. data/app/views/admin/race_competitors/edit.html.haml +20 -0
  32. data/app/views/admin/race_competitors/index.html.haml +27 -0
  33. data/app/views/admin/race_instances/_form.html.haml +98 -0
  34. data/app/views/admin/race_instances/edit.html.haml +17 -0
  35. data/app/views/admin/race_instances/new.html.haml +18 -0
  36. data/app/views/admin/race_records/_record.html.haml +20 -0
  37. data/app/views/admin/races/_form.html.haml +82 -0
  38. data/app/views/admin/races/_race.html.haml +36 -0
  39. data/app/views/admin/races/edit.html.haml +19 -0
  40. data/app/views/admin/races/index.html.haml +26 -0
  41. data/app/views/admin/races/new.html.haml +19 -0
  42. data/app/views/race_categories/show.html.haml +36 -0
  43. data/app/views/race_clubs/show.html.haml +25 -0
  44. data/app/views/race_instances/index.html.haml +14 -0
  45. data/app/views/race_instances/show.html.haml +52 -0
  46. data/app/views/race_performances/_headings.html.haml +4 -0
  47. data/app/views/race_performances/_performance.html.haml +21 -0
  48. data/app/views/races/index.html.haml +38 -0
  49. data/app/views/races/show.html.haml +81 -0
  50. data/cucumber.yml +1 -0
  51. data/db/migrate/20091116130836_race_data.rb +128 -0
  52. data/db/migrate/20091124095157_competitor_details.rb +9 -0
  53. data/db/migrate/20091126110634_club_aliases.rb +17 -0
  54. data/db/migrate/20091223094002_instance_details.rb +11 -0
  55. data/db/migrate/20091224100524_race_distance.rb +11 -0
  56. data/db/migrate/20091224100734_race_records.rb +19 -0
  57. data/db/migrate/20091224105637_category_details.rb +13 -0
  58. data/db/migrate/20091224115909_filters.rb +11 -0
  59. data/db/migrate/20091228122837_record_holder.rb +11 -0
  60. data/db/migrate/20100106104850_remember_calculations.rb +9 -0
  61. data/db/migrate/20100426104801_race_attachments.rb +11 -0
  62. data/features/support/env.rb +16 -0
  63. data/features/support/paths.rb +14 -0
  64. data/lib/duration_extensions.rb +61 -0
  65. data/lib/race_results/admin_ui.rb +76 -0
  66. data/lib/race_results/race_tags.rb +722 -0
  67. data/lib/tasks/race_results_extension_tasks.rake +28 -0
  68. data/public/images/admin/calendar_down.png +0 -0
  69. data/public/images/admin/new-race.png +0 -0
  70. data/public/javascripts/admin/races.js +29 -0
  71. data/public/stylesheets/sass/admin/races.sass +131 -0
  72. data/race_results_extension.rb +38 -0
  73. data/spec/datasets/competitors_dataset.rb +81 -0
  74. data/spec/datasets/race_sites_dataset.rb +8 -0
  75. data/spec/datasets/races_dataset.rb +179 -0
  76. data/spec/files/dunnerdale_2009.csv +277 -0
  77. data/spec/files/long_duddon_2008.csv +230 -0
  78. data/spec/lib/duration_spec.rb +50 -0
  79. data/spec/models/race_category_spec.rb +65 -0
  80. data/spec/models/race_club_spec.rb +32 -0
  81. data/spec/models/race_instance_spec.rb +104 -0
  82. data/spec/models/race_performance_spec.rb +7 -0
  83. data/spec/models/race_performance_status_spec.rb +31 -0
  84. data/spec/models/race_spec.rb +39 -0
  85. data/spec/spec.opts +6 -0
  86. data/spec/spec_helper.rb +36 -0
  87. data/vendor/plugins/acts_as_list/README +23 -0
  88. data/vendor/plugins/acts_as_list/init.rb +3 -0
  89. data/vendor/plugins/acts_as_list/lib/active_record/acts/list.rb +256 -0
  90. data/vendor/plugins/acts_as_list/test/list_test.rb +332 -0
  91. metadata +195 -0
@@ -0,0 +1,28 @@
1
+ namespace :radiant do
2
+ namespace :extensions do
3
+ namespace :race_results do
4
+
5
+ desc "Runs the migration of the Race Results extension"
6
+ task :migrate => :environment do
7
+ require 'radiant/extension_migrator'
8
+ if ENV["VERSION"]
9
+ RaceResultsExtension.migrator.migrate(ENV["VERSION"].to_i)
10
+ else
11
+ RaceResultsExtension.migrator.migrate
12
+ end
13
+ end
14
+
15
+ desc "Copies public assets of the Race Results to the instance public/ directory."
16
+ task :update => :environment do
17
+ is_svn_or_dir = proc {|path| path =~ /\.svn/ || File.directory?(path) }
18
+ puts "Copying assets from RaceResultsExtension"
19
+ Dir[RaceResultsExtension.root + "/public/**/*"].reject(&is_svn_or_dir).each do |file|
20
+ path = file.sub(RaceResultsExtension.root, '')
21
+ directory = File.dirname(path)
22
+ mkdir_p RAILS_ROOT + directory, :verbose => false
23
+ cp file, RAILS_ROOT + path, :verbose => false
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
Binary file
@@ -0,0 +1,29 @@
1
+ var date_picker_options = {
2
+ yearPicker: true,
3
+ timePicker: true,
4
+ dayShort: 3,
5
+ format: 'D d M Y @ h:ia',
6
+ inputOutputFormat: 'Y-m-d H:i:s',
7
+ allowEmpty: true,
8
+ positionOffset: { x: 20, y: -10 }
9
+ };
10
+
11
+ var AutoSlug = new Class({
12
+ initialize: function (slugfield) {
13
+ this.slug = slugfield;
14
+ this.title = $(this.slug.id.replace('_slug', '_title'));
15
+ this.old_title = this.title.value;
16
+ this.title.addEvent('change', this.updateSlug.bind(this));
17
+ },
18
+ updateSlug: function () {
19
+ if (this.old_title.toSlug() == this.slug.get('value')) {
20
+ this.old_title = this.title.get('value');
21
+ this.slug.set('value', this.old_title.toSlug());
22
+ }
23
+ }
24
+ });
25
+
26
+ activations.push(function (scope) {
27
+ scope.getElements('input.date').each (function (input) { new DatePicker(input, date_picker_options); });
28
+ scope.getElements('input.slug').each (function (input) { new AutoSlug(input); });
29
+ });
@@ -0,0 +1,131 @@
1
+ @import modules/rounded.sass
2
+ @import modules/opacity.sass
3
+ @import modules/grid.sass
4
+
5
+ =standard_input
6
+ :width 100%
7
+ :padding 6px 0
8
+ :border 1px solid #666
9
+ :font-family Monaco,"Courier New",Courier,monospace
10
+ :font-size 95%
11
+
12
+ =quiet
13
+ :font-family helvetica, arial, sans-serif
14
+ :font-weight lighter
15
+ :font-size 85%
16
+ :color #ccc
17
+ :line-height 1.3
18
+ :text-shadow none
19
+
20
+ =flatbottom
21
+ :margin-bottom 0
22
+
23
+ table.index
24
+ td.race_name
25
+ :font-size 115%
26
+ :font-weight bold
27
+ :vertical-align top
28
+ a
29
+ :color black
30
+ :text-decoration none
31
+
32
+ td.race_instances
33
+ :padding 8px
34
+ :width 75%
35
+ :font-size 90%
36
+ :vertical-align top
37
+ ul.instances
38
+ :list-style square
39
+ :padding-left 0
40
+ li
41
+ :margin-bottom 6px
42
+ :float left
43
+ :margin-right 32px
44
+ a
45
+ :font-size 115%
46
+ :font-weight bold
47
+ :text-decoration none
48
+ p.new_instance
49
+ :clear left
50
+
51
+ a.remove
52
+ :font-weight bold
53
+ :color #c00
54
+ :text-decoration none
55
+
56
+ span.formnote, span.info
57
+ :color #666
58
+ :font-size 85%
59
+
60
+ #content .form-area p.body
61
+ :margin-top 20px
62
+
63
+ ul.help
64
+ :list-style square
65
+ :color #666
66
+ :font-size 85%
67
+
68
+ code
69
+ :color #c00
70
+ :font-size 115%
71
+
72
+ p.haserror
73
+ :border-left 4px solid #c00
74
+ :margin-left -16px
75
+ :padding-left 12px
76
+ :color #c00 !important
77
+
78
+
79
+ #content form
80
+ input.textbox, select.textbox, textarea
81
+ +standard_input
82
+ :margin-top 0.3em
83
+ p.title
84
+ +grid(8)
85
+ p.slug
86
+ +grid(4)
87
+ input.textbox
88
+ :margin-top 4px
89
+ :font-family Georgia, Palatino, "Times New Roman", Times, serif
90
+ :font-size 200%
91
+ :width 99.5%
92
+ p.distance
93
+ +grid(4)
94
+ p.climb
95
+ +grid(4)
96
+ p.description
97
+ +clear
98
+ +grid(8)
99
+ textarea
100
+ :width 100%
101
+ :height 8em
102
+ #attachments
103
+ +grid(4)
104
+ img
105
+ :float left
106
+ :margin 4px 10px 10px 0
107
+ #records, #checkpoints
108
+ +grid(6)
109
+ .category
110
+ +grid(1)
111
+ +flush
112
+ .time
113
+ +grid(3)
114
+ .holder
115
+ +grid(6)
116
+ .name, .location
117
+ +grid(6)
118
+ .year
119
+ +grid(2)
120
+ +flush_right
121
+ .options
122
+ +clear
123
+
124
+ a.toggleMCE
125
+ :display block
126
+ :width 24px
127
+ :height 24px
128
+ :padding-left 24px
129
+ :float right
130
+
131
+
@@ -0,0 +1,38 @@
1
+ # Uncomment this if you reference any of your controllers in activate
2
+ # require_dependency 'application_controller'
3
+
4
+ class RaceResultsExtension < Radiant::Extension
5
+ version "1.0.1"
6
+ description "Makes easy the uploading, analysis and display of race results. Built for fell races but should work for most timed or score events."
7
+ url "http://spanner.org/radiant/race_results"
8
+
9
+ extension_config do |config|
10
+ config.after_initialize do
11
+ ActiveSupport::Inflector.inflections do |inflect|
12
+ inflect.singular /(alias)$/i, 'alias'
13
+ end
14
+ end
15
+ end
16
+
17
+ def activate
18
+ require 'duration_extensions' # string to duration and vice versa
19
+ Page.send :include, RaceResults::RaceTags
20
+
21
+ unless defined? admin.race
22
+ Radiant::AdminUI.send :include, RaceResults::AdminUI
23
+ admin.race = Radiant::AdminUI.load_default_race_regions
24
+ admin.race_instance = Radiant::AdminUI.load_default_race_instance_regions
25
+ admin.race_club = Radiant::AdminUI.load_default_race_club_regions
26
+ admin.race_competitor = Radiant::AdminUI.load_default_race_competitor_regions
27
+ end
28
+
29
+ tab("Races") do
30
+ add_item "Races", "/admin/races"
31
+ add_item 'Competitors', '/admin/race_competitors'
32
+ add_item 'Clubs', '/admin/race_clubs'
33
+ end
34
+ end
35
+
36
+ def deactivate
37
+ end
38
+ end
@@ -0,0 +1,81 @@
1
+ class CompetitorsDataset < Dataset::Base
2
+ datasets = [:races]
3
+ datasets << :race_sites if defined? Site
4
+ uses *datasets
5
+
6
+ def load
7
+ create_race "Caw" do
8
+ create_instance "2007", :started_at => DateTime.civil(2007, 5, 7, 19, 30, 0)
9
+ create_instance "2008", :started_at => DateTime.civil(2008, 5, 6, 19, 30, 0)
10
+ end
11
+ create_race "Dunnerdale" do
12
+ create_instance "2009", :started_at => DateTime.civil(2009, 11, 14, 12, 0, 0) do
13
+ create_checkpoint "Knott"
14
+ create_checkpoint "Raven Crag"
15
+ create_checkpoint "Stickle Pike"
16
+ create_checkpoint "Great Stickle"
17
+ end
18
+ end
19
+ create_race "Scored" do
20
+ create_instance "2010", :started_at => DateTime.civil(2008, 10, 6, 9, 0, 0) do
21
+ create_checkpoint "Big", :value => 50
22
+ create_checkpoint "Small", :value => 10
23
+ create_checkpoint "Middling", :value => 25
24
+ end
25
+ end
26
+ end
27
+
28
+ helpers do
29
+ def create_race(name, attributes={})
30
+ attributes = race_attributes(attributes.update(:name => name))
31
+ symbol = name.symbolize
32
+ race = create_model :race, symbol, attributes
33
+ race.update_attribute(:created_by, users(:existing)) # the post-hook will have nulled it after creation
34
+ if block_given?
35
+ @race_id = race_id(symbol)
36
+ yield
37
+ end
38
+ end
39
+ def race_attributes(attributes={})
40
+ attributes[:name] ||= "race"
41
+ attributes[:slug] ||= attributes[:name].symbolize.to_s
42
+ attributes[:site] ||= sites(:test) if defined? Site
43
+ attributes
44
+ end
45
+
46
+ def create_instance(name, attributes={})
47
+ attributes = race_instance_params(attributes.update(:name => name))
48
+ symbol = name.symbolize
49
+ create_record :race_instance, symbol, attributes
50
+ if block_given?
51
+ @race_instance_id = race_instance_id(symbol)
52
+ yield
53
+ end
54
+ end
55
+ def race_instance_params(attributes={})
56
+ attributes.reverse_merge({
57
+ :name => 'Example',
58
+ :start_time => Time.now - 1.year
59
+ })
60
+ attributes[:race_id] = @race_id
61
+ attributes[:slug] ||= attributes[:name].symbolize.to_s
62
+ attributes[:site] = sites(:test) if defined? Site
63
+ attributes
64
+ end
65
+
66
+ def create_checkpoint(name, attributes={})
67
+ attributes = checkpoint_params(attributes.reverse_merge(:name => name))
68
+ create_record :race_checkpoint, name.symbolize, attributes
69
+ end
70
+ def checkpoint_params(attributes={})
71
+ attributes.reverse_merge({
72
+ :name => "innominate",
73
+ :description => "Checkpoint description",
74
+ :location => "SD555555"
75
+ })
76
+ attributes[:race_instance_id] = @race_instance_id
77
+ attributes[:site] = sites(:test) if defined? Site
78
+ attributes
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,8 @@
1
+ class RaceSitesDataset < Dataset::Base
2
+ uses :home_page
3
+
4
+ def load
5
+ create_record Site, :test, :name => 'Test host', :domain => '^test\.', :base_domain => 'test.host', :position => 6
6
+ Page.current_site = sites(:test) if defined? Site
7
+ end
8
+ end
@@ -0,0 +1,179 @@
1
+ class RacesDataset < Dataset::Base
2
+ datasets = [:users]
3
+ datasets << :race_sites if defined? Site
4
+ uses *datasets
5
+
6
+ def load
7
+ create_club "Ambleside" do
8
+ create_competitor "Ben Abdelnoor"
9
+ create_competitor "Gary Thorpe"
10
+ end
11
+ create_club "Dark Peak" do
12
+ create_competitor "Mike Robinson"
13
+ end
14
+ create_club "Helm Hill" do
15
+ create_competitor "Chris Robinson"
16
+ create_competitor "Tom Doyle"
17
+ end
18
+ create_club "Black Combe" do
19
+ create_competitor "Pete Tayler"
20
+ create_competitor "William Ross"
21
+ create_club_alias "BCR"
22
+ create_club_alias "Black Combe Runners"
23
+ end
24
+
25
+ create_category 'M', :age_above => 0, :age_below => nil, :gender => 'M'
26
+ create_category 'MV40', :age_above => 40, :age_below => nil, :gender => 'M'
27
+ create_category 'MV50', :age_above => 50, :age_below => nil, :gender => 'M'
28
+ create_category 'MV60', :age_above => 60, :age_below => nil, :gender => 'M'
29
+ create_category 'LV70', :age_above => 70, :age_below => nil, :gender => 'F'
30
+ create_category 'U21', :age_above => nil, :age_below => 21, :gender => nil
31
+
32
+ create_race "Caw" do
33
+ create_instance "2008", :started_at => DateTime.civil(2008, 5, 7, 19, 30, 0)
34
+ create_instance "2009", :started_at => DateTime.civil(2009, 5, 6, 19, 30, 0) do
35
+ create_performance :race_competitor_id => race_competitor_id(:ben_abdelnoor), :elapsed_time => '50:40', :category => race_categories(:m)
36
+ create_performance :race_competitor_id => race_competitor_id(:mike_robinson), :elapsed_time => '52:50', :category => race_categories(:m)
37
+ create_performance :race_competitor_id => race_competitor_id(:chris_robinson), :elapsed_time => '53:34', :category => race_categories(:m)
38
+ create_performance :race_competitor_id => race_competitor_id(:tom_doyle), :elapsed_time => '54:34', :category => race_categories(:m)
39
+ create_performance :race_competitor_id => race_competitor_id(:gary_thorpe), :elapsed_time => '55:45', :category => race_categories(:mv40)
40
+ end
41
+ end
42
+ create_race "Dunnerdale" do
43
+ create_checkpoint "Knott"
44
+ create_checkpoint "Raven Crag"
45
+ create_checkpoint "Stickle Pike"
46
+ create_checkpoint "Great Stickle"
47
+ create_instance "2007", :started_at => DateTime.civil(2009, 11, 14, 12, 0, 0) do
48
+ create_performance :race_competitor_id => race_competitor_id(:ben_abdelnoor), :category => race_categories(:m), :elapsed_time => '41:41' do
49
+ create_checkpoint_time "9:12", :race_checkpoint_id => race_checkpoint_id(:knott)
50
+ create_checkpoint_time "18:12", :race_checkpoint_id => race_checkpoint_id(:raven_crag)
51
+ create_checkpoint_time "27:12", :race_checkpoint_id => race_checkpoint_id(:stickle_pike)
52
+ create_checkpoint_time "35:12", :race_checkpoint_id => race_checkpoint_id(:great_stickle)
53
+ end
54
+ create_performance :race_competitor_id => race_competitor_id(:pete_tayler), :category => race_categories(:mv40), :elapsed_time => '47:54' do
55
+ create_checkpoint_time "12:12", :race_checkpoint_id => race_checkpoint_id(:knott)
56
+ create_checkpoint_time "17:12", :race_checkpoint_id => race_checkpoint_id(:raven_crag)
57
+ create_checkpoint_time "29:12", :race_checkpoint_id => race_checkpoint_id(:stickle_pike)
58
+ create_checkpoint_time "39:12", :race_checkpoint_id => race_checkpoint_id(:great_stickle)
59
+ end
60
+ create_performance :race_competitor_id => race_competitor_id(:william_ross), :category => race_categories(:m), :elapsed_time => '53:11' do
61
+ create_checkpoint_time "13:12", :race_checkpoint_id => race_checkpoint_id(:knott)
62
+ create_checkpoint_time "24:12", :race_checkpoint_id => race_checkpoint_id(:raven_crag)
63
+ create_checkpoint_time "36:12", :race_checkpoint_id => race_checkpoint_id(:stickle_pike)
64
+ create_checkpoint_time "47:12", :race_checkpoint_id => race_checkpoint_id(:great_stickle)
65
+ end
66
+ end
67
+ end
68
+ create_race "Scored" do
69
+ create_instance "2010", :started_at => DateTime.civil(2008, 10, 6, 9, 0, 0) do
70
+ create_checkpoint "Big", :value => 50
71
+ create_checkpoint "Small", :value => 10
72
+ create_checkpoint "Middling", :value => 25
73
+ end
74
+ end
75
+ end
76
+
77
+ helpers do
78
+ def create_club(name, attributes={})
79
+ symbol = name.symbolize
80
+ attributes[:name] = name
81
+ club = create_model :race_club, symbol, attributes
82
+ club.update_attribute(:created_by, users(:existing)) # the post-hook will have nulled it after creation
83
+ if block_given?
84
+ @race_club_id = club.id
85
+ yield
86
+ end
87
+ end
88
+
89
+ def create_competitor(name, attributes={})
90
+ symbol = name.symbolize
91
+ attributes[:name] = name
92
+ attributes[:race_club_id] ||= @race_club_id
93
+ competitor = create_model :race_competitor, symbol, attributes
94
+ end
95
+
96
+ def create_club_alias(name, attributes={})
97
+ symbol = name.symbolize
98
+ attributes[:name] = name
99
+ attributes[:race_club_id] ||= @race_club_id
100
+ club_alias = create_model :race_club_alias, symbol, attributes
101
+ end
102
+
103
+ def create_race(name, attributes={})
104
+ symbol = name.symbolize
105
+ attributes[:name] = name
106
+ attributes[:slug] ||= symbol.to_s
107
+ attributes[:site] ||= sites(:test) if defined? Site
108
+ race = create_model :race, symbol, attributes
109
+ race.update_attribute(:created_by, users(:existing)) # the post-hook will have nulled it after creation
110
+ if block_given?
111
+ @race_id = race.id
112
+ yield
113
+ end
114
+ end
115
+
116
+ def create_instance(name, attributes={})
117
+ symbol = name.symbolize
118
+ attributes = {
119
+ :name => name,
120
+ :slug => symbol.to_s,
121
+ :started_at => Time.now - 1.year,
122
+ :race_id => @race_id
123
+ }.update(attributes)
124
+ attributes[:site_id] = site_id(:test) if defined? Site
125
+ ri = create_model :race_instance, symbol, attributes
126
+ ri.update_attribute(:created_by, users(:existing)) # the post-hook will have nulled it after creation
127
+ if block_given?
128
+ @race_instance_id = ri.id
129
+ yield
130
+ end
131
+ end
132
+
133
+ def create_category(name, attributes={})
134
+ symbol = name.symbolize
135
+ attributes[:name] ||= name
136
+ category = create_model :race_category, symbol, attributes
137
+ end
138
+
139
+ def create_checkpoint(name, attributes={})
140
+ symbol = name.symbolize
141
+ attributes = {
142
+ :name => name,
143
+ :description => "Checkpoint description",
144
+ :location => "SD555555",
145
+ :race_id => @race_id
146
+ }.update(attributes)
147
+ attributes[:site_id] = site_id(:test) if defined? Site
148
+ create_record :race_checkpoint, symbol, attributes
149
+ end
150
+
151
+ def create_performance(attributes={})
152
+ symbol = make_unique_id(:race_performance)
153
+ attributes[:race_instance_id] ||= @race_instance_id
154
+ attributes[:site_id] = site_id(:test) if defined? Site
155
+ attributes[:status] = RacePerformanceStatus.from_time(attributes[:elapsed_time])
156
+ performance = create_model :race_performance, symbol, attributes
157
+ if block_given?
158
+ @race_performance_id = performance.id
159
+ yield
160
+ end
161
+ end
162
+
163
+ def create_checkpoint_time(time, attributes={})
164
+ symbol = make_unique_id(:checkpoint_time)
165
+ attributes[:elapsed_time] = time
166
+ attributes[:race_performance_id] ||= @race_performance_id
167
+ attributes[:site_id] = site_id(:test) if defined? Site
168
+ create_record :race_checkpoint_time, symbol, attributes
169
+ end
170
+
171
+ private
172
+ @@call_count = 0
173
+ def make_unique_id(klass)
174
+ @@call_count += 1
175
+ "#{klass.to_s} #{@@call_count}"
176
+ end
177
+
178
+ end
179
+ end