Class Race
In: app/models/race.rb
Parent: ActiveRecord::Base

A Race is essentionally a collection of Results labelled with a Category. Races must belong to a parent Event.

Races only have some of their attributes populated. These attributes are listed in the result_columns Array.

People use say "category" where we use Race in code. Could rename this EventCategory.

Race result_columns: populated columns displayed on results page. Usually Result attributes, but also creates virtual "custom" columns.

Methods

Included Modules

Comparable Export::Races

Constants

DEFAULT_RESULT_COLUMNS = %W{place number last_name first_name team_name points time}.freeze
RESULT = Result.new   Prototype Result used for checking valid column names

Public Class methods

[Source]

   # File app/models/export/races.rb, line 5
5:     def Race.export
6:       Race.export_head
7:       Race.export_data
8:     end

[Source]

    # File app/models/export/races.rb, line 34
34:     def Race.export_columns
35:       [
36:         "id", "event_id", "category_id", "city", "state", "distance",
37:         "field_size", "laps", "time", "finishers"
38:       ]
39:     end

[Source]

    # File app/models/export/races.rb, line 16
16:     def Race.export_data
17:       Base.export(export_data_sql, "races.csv")
18:     end

[Source]

    # File app/models/export/races.rb, line 26
26:     def Race.export_data_sql
27:       "SELECT #{Race.export_columns.join(",")}
28:        INTO OUTFILE '%s'
29:        FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"'
30:        LINES TERMINATED BY '\\n'
31:        FROM races"
32:     end

[Source]

    # File app/models/export/races.rb, line 12
12:     def Race.export_head
13:       Base.export(export_head_sql, "races.txt")
14:     end

[Source]

    # File app/models/export/races.rb, line 20
20:     def Race.export_head_sql
21:       "SELECT '#{Race.export_columns.join("','")}'
22:        INTO OUTFILE '%s'
23:        FIELDS TERMINATED BY '\\n'"
24:     end

Public Instance methods

By category name

[Source]

     # File app/models/race.rb, line 303
303:   def <=>other
304:     category.name <=> other.category.name
305:   end

[Source]

     # File app/models/race.rb, line 315
315:   def==(other)
316:     return false unless other.is_a?(self.class)
317:     unless other.new_record? or new_record?
318:       return other.id == id
319:     end
320:     category == other.category
321:   end

Defaults to Event‘s BAR points

[Source]

    # File app/models/race.rb, line 29
29:   def bar_points
30:     self[:bar_points] || self.event.bar_points
31:   end

0..3

[Source]

    # File app/models/race.rb, line 34
34:   def bar_points=(value)
35:     if value == self.event.bar_points or value.nil?
36:       self[:bar_points] = nil
37:     elsif value.to_i == value.to_f
38:       self[:bar_points] = value
39:     else
40:       raise ArgumentError, "BAR points must be an integer, but was: #{value}"
41:     end
42:   end

[Source]

     # File app/models/race.rb, line 209
209:   def calculate_members_only_places!
210:     event_notification_was_enabled = event.notification_enabled?
211:     event.disable_notification!
212:     begin
213:       # count up from zero
214:       last_members_only_place = 0
215:       # assuming first result starting at zero+one (better than sorting results twice?)
216:       last_result_place = 0
217:       results.sort.each do |result|
218:         place_before = result.members_only_place.to_i
219:         result.members_only_place = ''
220:         if result.place.to_i > 0         
221:           if ((result.person.nil? || (result.person && result.person.member?(result.date))) && !non_members_on_team(result))
222:             # only increment if we have moved onto a new place
223:             last_members_only_place += 1 if (result.place.to_i != last_members_only_place && result.place.to_i!=last_result_place)
224:             result.members_only_place = last_members_only_place.to_s
225:           end
226:           # Slight optimization. Most of the time, no point in saving a result that hasn't changed
227:           result.update_attribute('members_only_place', result.members_only_place) if place_before != result.members_only_place 
228:           # store to know when switching to new placement (team result feature)
229:           last_result_place = result.place.to_i
230:         end
231:       end
232:     ensure
233:       event.enable_notification! if event_notification_was_enabled
234:     end
235:   end

[Source]

     # File app/models/race.rb, line 102
102:   def calculate_result_columns!
103:     self.result_columns = result_columns_or_default
104:     result_columns.delete_if do |result_column|
105:       results.all? do |result|
106:         value = result.send(result_column)
107:         value.blank? || value == 0 || value == 0.0
108:       end
109:     end
110:     save!
111:   end

[Source]

    # File app/models/race.rb, line 60
60:   def category_friendly_param
61:     category.try :friendly_param
62:   end

[Source]

    # File app/models/race.rb, line 56
56:   def category_name
57:     category.try :name
58:   end

[Source]

    # File app/models/race.rb, line 44
44:   def category_name=(name)
45:     if name.blank?
46:       self.category = nil
47:     else
48:       self.category = Category.new(:name => name)
49:     end
50:   end

[Source]

     # File app/models/race.rb, line 261
261:   def create_result_before(result_id)
262:     results.sort!
263:     if results.empty?
264:       return results.create(:place => "1")
265:     end
266:     
267:     if result_id
268:       result = Result.find(result_id)
269:       place = result.place
270:       start_index = results.index(result)
271:       for index in start_index...(results.size)
272:         if results[index].place.to_i > 0
273:           results[index].place = (results[index].place.to_i + 1).to_s
274:           results[index].save!
275:         end
276:       end
277:     else
278:       result = results.last
279:       if result.place.to_i > 0
280:         place = result.place.to_i + 1
281:       else
282:         place = result.place
283:       end
284:     end
285:     
286:     results.create(:place => place)
287:   end

[Source]

     # File app/models/race.rb, line 147
147:   def custom_columns
148:     self[:custom_columns] ||= []
149:   end

[Source]

    # File app/models/race.rb, line 85
85:   def date
86:     event.try :date
87:   end

Range of dates_of_birth of people in this race

[Source]

    # File app/models/race.rb, line 80
80:   def dates_of_birth
81:     raise(ArgumentError, 'Need category to calculate dates of birth') unless category
82:     Date.new(date.year - category.ages.end, 1, 1)..Date.new(date.year - category.ages.begin, 12, 31)
83:   end

[Source]

     # File app/models/race.rb, line 289
289:   def destroy_result(result)
290:     place = result.place
291:     results.sort!
292:     start_index = results.index(result) + 1
293:     for index in start_index...(results.size)
294:       if results[index].place.to_i > 0
295:         results[index].place = results[index].place.to_i - 1
296:         results[index].save!
297:       end
298:     end
299:     result.destroy
300:   end

[Source]

    # File app/models/race.rb, line 52
52:   def discipline
53:     self.event.discipline if event
54:   end

Incorrectly doubles tandem and other team events’ field sizes

[Source]

     # File app/models/race.rb, line 94
 94:   def field_size
 95:     if self[:field_size].present? && self[:field_size] > 0
 96:       self[:field_size]
 97:     else
 98:       results.size
 99:     end
100:   end

Ensure child team and people are not duplicates of existing records Tricky side effect — external references to new association records (category, bar_category, person, team) will not point to associated records FIXME Handle people with only a number

[Source]

     # File app/models/race.rb, line 155
155:   def find_associated_records
156:     if category && (category.new_record? || category.changed?)
157:       if category.name.blank?
158:         self.category = nil
159:       else
160:         existing_category = Category.find_by_name(category.name)
161:         self.category = existing_category if existing_category
162:       end
163:     end
164:   end

Combine with event name

[Source]

    # File app/models/race.rb, line 69
69:   def full_name
70:     if name == event.full_name
71:       name
72:     elsif event.full_name[name]
73:       event.full_name
74:     else
75:       "#{event.full_name}: #{name}"
76:     end
77:   end

[Source]

     # File app/models/race.rb, line 166
166:   def has_result(row_hash)
167:     if row_hash["place"].present? && row_hash["place"] != "1" && row_hash["place"] != "0"
168:       return true
169:     end
170:     if row_hash["person.first_name"].blank? && 
171:        row_hash["person.last_name"].blank? &&
172:        row_hash["person.road_number"].blank? &&
173:        row_hash["team.name"].blank?
174:       return false
175:     end
176:     true
177:   end

[Source]

     # File app/models/race.rb, line 307
307:   def hash
308:     if new_record?
309:       category.hash
310:     else
311:       id
312:     end
313:   end

[Source]

    # File app/models/race.rb, line 64
64:   def name
65:     self.category_name
66:   end

[Source]

     # File app/models/race.rb, line 237
237:   def non_members_on_team(result)
238:     non_members = false
239:     # if this is undeclared in environment.rb, assume this rule does not apply
240:     exempt_cats = RacingAssociation.current.exempt_team_categories
241:      if (exempt_cats.nil? || exempt_cats.include?(result.race.category.name))
242:        return non_members
243:      else
244:        other_results_in_place = Result.find(:all, :conditions => ["race_id = ? and place = ?", result.race.id, result.place])
245:        other_results_in_place.each { |orip|
246:           unless orip.person.nil?
247:             if !orip.person.member?(result.date)
248:              # might as well blank out this result while we're here, saves some future work
249:              result.members_only_place = ''
250:              result.update_attribute 'members_only_place', result.members_only_place
251:              # could also use other_results_in_place.size if needed for calculations
252:              non_members = true
253:             end
254:           end
255:        }
256:        # still false if no others found, or all are members, or could not be determined (non-person)
257:        non_members
258:      end
259:    end

Sort results by points, assign places Save! each result after place is set

[Source]

     # File app/models/race.rb, line 181
181:   def place_results_by_points(break_ties = true, ascending = true)
182:     for result in results
183:       result.calculate_points
184:     end
185: 
186:     results.sort! do |x, y| 
187:       x.compare_by_points(y)
188:     end
189: 
190:     if !ascending
191:       results.reverse!
192:     end
193: 
194:     previous_result = nil
195:     results.each_with_index do |result, index|
196:       if index == 0
197:         result.place = 1
198:       else
199:         if results[index - 1].compare_by_points(result, break_ties) == 0
200:           result.place = results[index - 1].place
201:         else
202:           result.place = index + 1
203:         end
204:       end
205:       result.save!
206:     end
207:   end

[Source]

     # File app/models/race.rb, line 113
113:   def result_columns=(value)
114:     if value && value.include?("name")
115:       name_index = value.index("name")
116:       value[name_index] = "first_name"
117:       value.insert(name_index + 1, "last_name")
118:     end
119: 
120:     if value && value.include?("place") && value.first != "place"
121:       value.delete("place")
122:       value.insert(0, "place")
123:     end
124:     self[:result_columns] = value
125:   end

Default columns if empty

[Source]

     # File app/models/race.rb, line 128
128:   def result_columns_or_default
129:     self.result_columns || DEFAULT_RESULT_COLUMNS.dup
130:   end

Ugh. Better here than a controller or helper, I guess.

[Source]

     # File app/models/race.rb, line 133
133:   def result_columns_or_default_for_editing
134:     columns = result_columns_or_default
135:     columns.map! do |column| 
136:       if column == "first_name" || column == "last_name"
137:         "name"
138:       else
139:         column
140:       end
141:     end
142:     columns << "bar" if RacingAssociation.current.competitions.include?(:bar)
143:     columns.uniq!
144:     columns
145:   end

[Source]

     # File app/models/race.rb, line 323
323:   def to_s
324:     "#<Race #{self.id} #{self[:event_id]} #{self[:category_id]} >"
325:   end

[Source]

    # File app/models/race.rb, line 89
89:   def year
90:     event && event.date && event.date.year
91:   end

[Validate]