Class Person
In: app/models/person.rb
Parent: ActiveRecord::Base

Someone who either appears in race results or who is added as a member of a racing association

Names are not unique. In fact, there are many business rules about names. See Aliases and Names.

Methods

<=>   account_permissions   add_alias_for_old_name   add_number   administrator?   attributes=   birthdate   birthdate=   bmx_number   bmx_number=   can_edit?   category   ccx_number   ccx_number=   city_state   city_state_zip   competition_results   created_from_result?   cyclocross_racing_age   date_of_birth=   deliver_password_reset_instructions!   destroy_shadowed_aliases   dh_number   dh_number=   email_with_name   event_results   export   export_columns   export_data   export_head   export_head_sql   find_all_by_name   find_all_by_name_like   find_all_by_name_or_alias   find_all_for_export   find_associated_records   find_by_info   find_by_name   find_by_number   find_or_create_by_name   first_name   first_name=   full_name   gender   gender=   gender_pronoun   hometown   hometown=   junior?   last_name   last_name=   last_name_or_login   lic_check   master?   member   member=   member?   member_from=   member_in_year?   member_to=   membership_dates   merge   name   name=   name_or_contact_info   name_or_login   name_was   number   people_with_same_name   per_page   possessive_pronoun   print_card?   promoter?   racing_age   renew!   renewed?   road_number   road_number=   select_by_recent_activity   senior?   set_created_by   set_last_updated_by   singlespeed_number   singlespeed_number=   state=   team_name   team_name=   third_person_pronoun   to_s   toggle!   track_number   track_number=   unique_info   updated_after_created?   validate_unique_editors   xc_number   xc_number=  

Included Modules

Comparable Names::Nameable SentientUser Export::People

Constants

CATEGORY_FIELDS = [ :bmx_category, :ccx_category, :dh_category, :mtb_category, :road_category, :track_category ]

Attributes

year  [RW] 

Public Class methods

[Source]

     # File app/models/person.rb, line 247
247:   def Person.deliver_password_reset_instructions!(people)
248:     people.each(&:reset_perishable_token!)
249:     Notifier.deliver_password_reset_instructions(people)
250:   end

[Source]

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

[Source]

    # File app/models/export/people.rb, line 51
51:     def Person.export_columns
52:       [
53:         'id', "first_name", "last_name", "city", "state", "date_of_birth", "license",
54:         "team_id", "gender", "ccx_category", "road_category", "track_category", "mtb_category", "dh_category"
55:       ]
56:     end

[Source]

    # File app/models/export/people.rb, line 12
12:     def Person.export_data
13:       basename = 'people.csv'
14:       path = Base.tmp_path basename
15:       path.unlink if path.exist?
16:       FileUtils.mkdir_p path.dirname unless path.dirname.exist?
17:       FileUtils.chmod 0777, path.dirname
18:       target = Base.public_path basename
19:       target.dirname.mkdir unless target.dirname.exist?
20: 
21:       people = Person.find_all_for_export(Date.new, nil)
22:       outfile = File.open(target, 'wb')
23:       CSV.open(outfile, "wb") do |csv|
24:         people.each do |person|
25:           person.each do |field|
26:             if field[1].blank?
27:               key=field[0]
28:               person[key] = '\N'
29:             end
30:           end
31:           csv << [person['id'], person['first_name'], person['last_name'], person['city'],
32:                   person['state'], person['date_of_birth'], person['license'], person['team_id'],
33:                   person['gender'], person['ccx_category'], person['road_category'], person['track_category'],
34:                   person['mtb_category'], person['dh_category']]
35:         end
36:       end
37: 
38:       outfile.close
39:     end

[Source]

    # File app/models/export/people.rb, line 41
41:     def Person.export_head
42:       Base.export(Person.export_head_sql, "people.txt")
43:     end

[Source]

    # File app/models/export/people.rb, line 45
45:     def Person.export_head_sql
46:       "SELECT '#{Person.export_columns.join("','")}'
47:        INTO OUTFILE '%s'
48:        FIELDS TERMINATED BY '\\n'"
49:     end

Does not consider Aliases

[Source]

    # File app/models/person.rb, line 56
56:   def Person.find_all_by_name(name)
57:     if name.blank?
58:       Person.find(
59:         :all,
60:         :conditions => ['first_name = ? and last_name = ?', '', ''],
61:         :order => 'last_name, first_name')
62:     else
63:       Person.find(
64:         :all,
65:         :conditions => ["trim(concat_ws(' ', first_name, last_name)) = ?", name],
66:         :order => 'last_name, first_name')
67:     end
68:   end

Considers aliases

[Source]

     # File app/models/person.rb, line 105
105:   def Person.find_all_by_name_like(name, limit = 100)
106:     return [] if name.blank?
107:     
108:     name_like = "%#{name.strip}%"
109:     Person.find(
110:       :all, 
111:       :conditions => ["trim(concat_ws(' ', first_name, last_name)) like ? or aliases.name like ?", name_like, name_like],
112:       :include => :aliases,
113:       :limit => limit,
114:       :order => 'last_name, first_name'
115:     )
116:   end

"Jane Doe" or "Jane", "Doe" or :name => "Jane Doe" or :first_name => "Jane", :last_name => "Doe"

[Source]

     # File app/models/person.rb, line 71
 71:   def Person.find_all_by_name_or_alias(*args)
 72:     options = args.extract_options!
 73:     options.keys.each { |key| raise(ArgumentError, "'#{key}' is not a valid key") unless [:name, :first_name, :last_name].include?(key) }
 74:     
 75:     name = args.join(" ") if options.empty?
 76:     
 77:     name = name || options[:name]
 78:     first_name = options[:first_name]
 79:     last_name = options[:last_name]
 80:     
 81:     if name.present?
 82:       return Person.find(
 83:         :all,
 84:         :conditions => ["trim(concat_ws(' ', first_name, last_name)) = ?", name]
 85:       ) | Alias.find_all_people_by_name(name)
 86:     elsif first_name.present? && last_name.blank?
 87:       Person.find(
 88:         :all,
 89:         :conditions => ['first_name = ?', first_name]
 90:       ) | Alias.find_all_people_by_name(Person.full_name(first_name, last_name))
 91:     elsif first_name.blank? && last_name.present?
 92:       Person.find(
 93:         :all,
 94:         :conditions => ['last_name = ?', last_name]
 95:       ) | Alias.find_all_people_by_name(Person.full_name(first_name, last_name))
 96:     else
 97:       Person.find(
 98:         :all,
 99:         :conditions => ['first_name = ? and last_name = ?', first_name, last_name]
100:       ) | Alias.find_all_people_by_name(Person.full_name(first_name, last_name))
101:     end
102:   end

Flattened, straight SQL dump for export to Excel, FinishLynx, or SportsBase.

[Source]

     # File app/models/person.rb, line 165
165:   def Person.find_all_for_export(date = RacingAssociation.current.today, include_people = "members_only")
166:     association_number_issuer_id = NumberIssuer.find_by_name(RacingAssociation.current.short_name).id
167:     if include_people == "members_only"
168:       where_clause = "WHERE (member_to >= '#{date.to_s}')" 
169:     elsif include_people == "print_cards"
170:       where_clause = "WHERE  (member_to >= '#{date.to_s}') and print_card is true" 
171:     end
172:     
173:     people = Person.connection.select_all(%Q{
174:       SELECT people.id, license, first_name, last_name, teams.name as team_name, team_id, people.notes,
175:              DATE_FORMAT(member_from, '%m/%d/%Y') as member_from, DATE_FORMAT(member_to, '%m/%d/%Y') as member_to, DATE_FORMAT(member_usac_to, '%m/%d/%Y') as member_usac_to,
176:              print_card, card_printed_at, membership_card, ccx_only, DATE_FORMAT(date_of_birth, '%m/01/%Y') as date_of_birth, occupation,
177:              street, people.city, people.state, zip, wants_mail, email, wants_email, home_phone, work_phone, cell_fax, gender, 
178:              ccx_category, road_category, track_category, mtb_category, dh_category, 
179:              volunteer_interest, official_interest, race_promotion_interest, team_interest,
180:              CEILING(#{date.year} - YEAR(date_of_birth)) as racing_age,
181:              ccx_numbers.value as ccx_number, dh_numbers.value as dh_number, road_numbers.value as road_number, 
182:              singlespeed_numbers.value as singlespeed_number, xc_numbers.value as xc_number,
183:              DATE_FORMAT(people.created_at, '%m/%d/%Y') as created_at, DATE_FORMAT(people.updated_at, '%m/%d/%Y') as updated_at
184:       FROM people
185:       LEFT OUTER JOIN teams ON teams.id = people.team_id 
186:       LEFT OUTER JOIN race_numbers as ccx_numbers ON ccx_numbers.person_id = people.id 
187:                       and ccx_numbers.number_issuer_id = #{association_number_issuer_id} 
188:                       and ccx_numbers.year = #{date.year} 
189:                       and ccx_numbers.discipline_id = #{Discipline[:ccx].id}
190:       LEFT OUTER JOIN race_numbers as dh_numbers ON dh_numbers.person_id = people.id 
191:                       and dh_numbers.number_issuer_id = #{association_number_issuer_id} 
192:                       and dh_numbers.year = #{date.year} 
193:                       and dh_numbers.discipline_id = #{Discipline[:downhill].id}
194:       LEFT OUTER JOIN race_numbers as road_numbers ON road_numbers.person_id = people.id 
195:                       and road_numbers.number_issuer_id = #{association_number_issuer_id} 
196:                       and road_numbers.year = #{date.year} 
197:                       and road_numbers.discipline_id = #{Discipline[:road].id}
198:       LEFT OUTER JOIN race_numbers as singlespeed_numbers ON singlespeed_numbers.person_id = people.id 
199:                       and singlespeed_numbers.number_issuer_id = #{association_number_issuer_id} 
200:                       and singlespeed_numbers.year = #{date.year} 
201:                       and singlespeed_numbers.discipline_id = #{Discipline[:singlespeed].id}
202:       LEFT OUTER JOIN race_numbers as track_numbers ON track_numbers.person_id = people.id 
203:                       and track_numbers.number_issuer_id = #{association_number_issuer_id} 
204:                       and track_numbers.year = #{date.year} 
205:                       and track_numbers.discipline_id = #{Discipline[:track].id}
206:       LEFT OUTER JOIN race_numbers as xc_numbers ON xc_numbers.person_id = people.id 
207:                       and xc_numbers.number_issuer_id = #{association_number_issuer_id} 
208:                       and xc_numbers.year = #{date.year} 
209:                       and xc_numbers.discipline_id = #{Discipline[:mountain_bike].id}
210:       #{where_clause}
211:       ORDER BY last_name, first_name, people.id
212:     })
213:     
214:     last_person = nil
215:     people.reject! do |person|
216:       if last_person && last_person["id"] == person["id"]
217:         true
218:       else
219:         last_person = person
220:         false
221:       end
222:     end
223:     
224:     people
225:   end

[Source]

     # File app/models/person.rb, line 118
118:   def Person.find_by_info(name, email = nil, home_phone = nil)
119:     if name.present?
120:       Person.find_by_name(name)
121:     else
122:       Person.find(
123:         :first, 
124:         :conditions => ["(email = ? and email <> '' and email is not null) or (home_phone = ? and home_phone <> '' and home_phone is not null)", 
125:                         email, home_phone]
126:       )
127:     end
128:   end

[Source]

     # File app/models/person.rb, line 130
130:   def Person.find_by_name(name)
131:     Person.find(
132:       :first, 
133:       :conditions => ["trim(concat_ws(' ', first_name, last_name)) = ?", name]
134:     )
135:   end

[Source]

     # File app/models/person.rb, line 141
141:   def Person.find_by_number(number)
142:     Person.find(:all, 
143:                :include => :race_numbers,
144:                :conditions => [ 'race_numbers.year in (?) and race_numbers.value = ?', [ RacingAssociation.current.year, RacingAssociation.current.next_year ], number ])
145:   end

[Source]

     # File app/models/person.rb, line 137
137:   def Person.find_or_create_by_name(name)
138:     Person.find_by_name(name) || Person.create(:name => name)
139:   end

if/else to avoid strip

[Source]

     # File app/models/person.rb, line 148
148:   def Person.full_name(first_name, last_name)
149:     if first_name.present?
150:       if last_name.present?
151:         "#{first_name} #{last_name}"
152:       else
153:         first_name
154:       end
155:     else
156:       if last_name.present?
157:         last_name
158:       else
159:         ""
160:       end
161:     end
162:   end

interprets dates returned in sql above for member export

[Source]

     # File app/models/person.rb, line 228
228:   def Person.lic_check(lic, lic_date)
229:     if lic.to_i > 0
230:       (lic_date && (Date.strptime(lic_date, "%m/%d/%Y") > RacingAssociation.current.today)) ? "current" : "CHECK LIC!"
231:     else
232:       "NOT ON FILE"
233:     end
234:   end

[Source]

    # File app/models/person.rb, line 51
51:   def self.per_page
52:     50
53:   end

Find Person with most recent. If no results, select the most recently updated Person.

[Source]

     # File app/models/person.rb, line 237
237:   def Person.select_by_recent_activity(people)
238:     results = people.to_a.inject([]) { |results, person| results + person.results }
239:     if results.empty?
240:       people.to_a.sort_by(&:updated_at).last
241:     else
242:       results = results.sort_by(&:date)
243:       results.last.person
244:     end
245:   end

Public Instance methods

TODO Any reason not to change this to last name, first name?

[Source]

      # File app/models/person.rb, line 1002
1002:   def <=>(other)
1003:     if other
1004:       id <=> other.id
1005:     else
1006:       -1
1007:     end
1008:   end

[Source]

     # File app/models/person.rb, line 995
995:   def account_permissions
996:     (editors + editable_people).reject { |person| person == self }.uniq.map do |person|
997:       AccountPermission.new(person, editable_people.include?(person), editors.include?(person))
998:     end
999:   end

[Source]

     # File app/models/person.rb, line 969
969:   def add_alias_for_old_name
970:     if !new_record? &&
971:        !@old_name.blank? && 
972:        !name.blank? && 
973:        @old_name.casecmp(name) != 0 && 
974:        !Alias.exists?(['name = ? and person_id = ?', @old_name, id]) && 
975:        !Person.exists?(["trim(concat_ws(' ', first_name, last_name)) = ?", @old_name])
976:       
977:       new_alias = Alias.new(:name => @old_name, :person => self)
978:       unless new_alias.save
979:         logger.error("Could not save alias #{new_alias}: #{new_alias.errors.full_messages.join(", ")}")
980:       end
981:       new_alias
982:     end
983:   end

Look for RaceNumber year in attributes. Not sure if there‘s a simple and better way to do that.

[Source]

     # File app/models/person.rb, line 560
560:   def add_number(value, discipline, association = nil, _year = year)
561:     association = NumberIssuer.find_by_name(RacingAssociation.current.short_name) if association.nil?
562:     _year ||= RacingAssociation.current.year
563: 
564:     if discipline.nil? || !discipline.numbers?
565:       discipline = Discipline[:road]
566:     end
567:     
568:     if value.blank?
569:       unless new_record?
570:         # Delete ALL numbers for RacingAssociation.current and this discipline?
571:         # FIXME Delete number individually in UI
572:         RaceNumber.destroy_all(
573:           ['person_id=? and discipline_id=? and year=? and number_issuer_id=?', 
574:           self.id, discipline.id, _year, association.id])
575:       end
576:     else
577:       if new_record?
578:         existing_number = race_numbers.any? do |number|
579:           number.value == value && number.discipline == discipline && number.association == association && number.year == _year
580:         end
581:         race_numbers.build(
582:           :person => self, :value => value, :discipline => discipline, :year => _year, :number_issuer => association, 
583:           :updated_by => self.updated_by
584:         ) unless existing_number
585:       else
586:         race_number = RaceNumber.find(
587:           :first,
588:           :conditions => ['value=? and person_id=? and discipline_id=? and year=? and number_issuer_id=?', 
589:                            value, self.id, discipline.id, _year, association.id])
590:         unless race_number
591:           race_numbers.create(
592:             :person => self, :value => value, :discipline => discipline, :year => _year, 
593:             :number_issuer => association, :updated_by => self.updated_by
594:           )
595:         end
596:       end
597:     end
598:   end

[Source]

     # File app/models/person.rb, line 656
656:   def administrator?
657:     roles.any? { |role| role.name == "Administrator" }
658:   end

Workaround Rails date param-parsing. Also convert :team attribute to Team. Not sure this is needed.

[Source]

     # File app/models/person.rb, line 260
260:   def attributes=(attributes)
261:     unless attributes.nil?
262:       if attributes["member_to(1i)"] && !attributes["member_to(2i)"]
263:         attributes["member_to(2i)"] = '12'
264:         attributes["member_to(3i)"] = '31'
265:       end
266:       if attributes[:team] && attributes[:team].is_a?(Hash)
267:         team = Team.new(attributes[:team])
268:         team.created_by = attributes[:created_by]
269:         attributes[:team] = team
270:       end
271:       self.created_by = attributes[:created_by]
272:       self.updated_by = attributes[:updated_by]
273:       self.year = attributes[:year]
274:     end
275:     super(attributes)
276:   end

[Source]

     # File app/models/person.rb, line 477
477:   def birthdate
478:     date_of_birth
479:   end

[Source]

     # File app/models/person.rb, line 481
481:   def birthdate=(value)
482:     self.date_of_birth = value
483:   end

[Source]

     # File app/models/person.rb, line 600
600:   def bmx_number(reload = false, year = nil)
601:     number(Discipline[:bmx], reload, year)
602:   end

[Source]

     # File app/models/person.rb, line 628
628:   def bmx_number=(value)
629:     add_number(value, Discipline[:bmx])
630:   end

[Source]

     # File app/models/person.rb, line 948
948:   def can_edit?(person)
949:     person == self || administrator? || person.editors.include?(self)
950:   end

[Source]

     # File app/models/person.rb, line 519
519:   def category(discipline)
520:     if discipline.is_a?(String)
521:       _discipline = Discipline[discipline]
522:     else
523:       _discipline = discipline
524:     end
525:     
526:     case _discipline
527:     when Discipline[:road], Discipline[:track], Discipline[:criterium], Discipline[:time_trial], Discipline[:circuit]
528:       self["road_category"]
529:     when Discipline[:road], Discipline[:criterium], Discipline[:time_trial], Discipline[:circuit]
530:       self["track_category"]
531:     when Discipline[:cyclocross]
532:       self["ccx_category"]
533:     when Discipline[:dh]
534:       self["dh_category"]
535:     when Discipline[:bmx]
536:       self["bmx_category"]
537:     when Discipline[:mtb]
538:       self["xc_category"]
539:     end
540:   end

[Source]

     # File app/models/person.rb, line 604
604:   def ccx_number(reload = false, year = nil)
605:     number(Discipline[:cyclocross], reload, year)
606:   end

[Source]

     # File app/models/person.rb, line 632
632:   def ccx_number=(value)
633:     add_number(value, Discipline[:cyclocross])
634:   end

[Source]

     # File app/models/person.rb, line 772
772:   def city_state
773:     if city.present?
774:       if state.present?
775:         "#{city}, #{state}"
776:       else
777:         "#{city}"
778:       end
779:     else
780:       if state.present?
781:         "#{state}"
782:       else
783:         nil
784:       end
785:     end
786:   end

[Source]

     # File app/models/person.rb, line 788
788:   def city_state_zip
789:     if city.present?
790:       if state.present?
791:         "#{city}, #{state} #{zip}"
792:       else
793:         "#{city} #{zip}"
794:       end
795:     else
796:       if state.present?
797:         "#{state} #{zip}"
798:       else
799:         zip || ''
800:       end
801:     end
802:   end

BAR, Oregon Cup, Ironman

[Source]

     # File app/models/person.rb, line 866
866:   def competition_results
867:     results.select do |result|
868:       result.competition_result?
869:     end
870:   end

[Source]

     # File app/models/person.rb, line 757
757:   def created_from_result?
758:     !created_by.nil? && created_by.kind_of?(Event)
759:   end

[Source]

     # File app/models/person.rb, line 513
513:   def cyclocross_racing_age
514:     if date_of_birth
515:       racing_age + 1
516:     end
517:   end

[Source]

     # File app/models/person.rb, line 443
443:   def date_of_birth=(value)
444:     if value.is_a?(String)
445:       if value[%r{^\d\d/\d\d/\d\d$}]
446:         value.gsub! %r{(\d+)/(\d+)/(\d+)}, '19\3/\1/\2'
447:       else
448:         value.gsub!(/^00/, '19')
449:         value.gsub!(/^(\d+\/\d+\/)(\d\d)$/, '\119\2')
450:       end
451:     end
452:     
453:     if value && value.to_s.size < 5
454:       int_value = value.to_i
455:       if int_value > 10 && int_value <= 99
456:         value = "01/01/19#{value}"
457:       end
458:       if int_value > 0 && int_value <= 10
459:         value = "01/01/20#{value}"
460:       end
461:     end
462:     
463:     # Don't overwrite month and day if we're just passing in the same year
464:     if self[:date_of_birth] && value
465:       if value.is_a?(String)
466:         new_date = Date.parse(value)
467:       else
468:         new_date = value
469:       end
470:       if new_date.year == self[:date_of_birth].year && new_date.month == 1 && new_date.day == 1
471:         return
472:       end
473:     end
474:     super
475:   end

If name changes to match existing alias, destroy the alias

[Source]

     # File app/models/person.rb, line 965
965:   def destroy_shadowed_aliases
966:     Alias.destroy_all(['name = ?', name]) if first_name_changed? || last_name_changed?
967:   end

[Source]

     # File app/models/person.rb, line 608
608:   def dh_number(reload = false, year = nil)
609:     number(Discipline[:downhill], reload, year)
610:   end

[Source]

     # File app/models/person.rb, line 636
636:   def dh_number=(value)
637:     add_number(value, Discipline[:downhill])
638:   end

[Source]

     # File app/models/person.rb, line 294
294:   def email_with_name
295:     "#{name} <#{email}>"
296:   end

All non-Competition results reload does an optimized load with joins

[Source]

     # File app/models/person.rb, line 852
852:   def event_results(reload = true)
853:     if reload
854:       return Result.find(
855:         :all,
856:         :include => [:team, :person, :scores, :category, {:race => [:event, :category]}],
857:         :conditions => ['people.id = ?', id]
858:       ).reject {|r| r.competition_result?}
859:     end
860:     results.reject do |result|
861:       result.competition_result?
862:     end
863:   end

Replace team with exising Team if current team is an unsaved duplicate of an existing Team

[Source]

     # File app/models/person.rb, line 937
937:   def find_associated_records
938:     if self.team && team.new_record?
939:       if team.name.blank? or team.name == 'N/A'
940:         self.team = nil
941:       else
942:         existing_team = Team.find_by_name_or_alias(team.name)
943:         self.team = existing_team if existing_team
944:       end
945:     end
946:   end

[Source]

     # File app/models/person.rb, line 284
284:   def first_name(date_or_year = nil)
285:     year = parse_year(date_or_year)
286:     name_record_for_year(year).try(:first_name) || read_attribute(:first_name)
287:   end

[Source]

     # File app/models/person.rb, line 371
371:   def first_name=(value)
372:     @old_name = name unless @old_name
373:     self[:first_name] = value
374:   end

Non-nil for happier sorting

[Source]

     # File app/models/person.rb, line 423
423:   def gender
424:     self[:gender] || ''
425:   end

[Source]

     # File app/models/person.rb, line 427
427:   def gender=(value)
428:     if value.nil?
429:       self[:gender] = nil
430:     else
431:       value.upcase!
432:       case value
433:       when 'M', 'MALE', 'BOY'
434:         self[:gender] = 'M'
435:       when 'F', 'FEMALE', 'GIRL'
436:         self[:gender] = 'F'
437:       else
438:         self[:gender] = 'M'
439:       end
440:     end
441:   end

[Source]

     # File app/models/person.rb, line 398
398:   def gender_pronoun
399:     if gender == "F"
400:       "herself"
401:     else
402:       "himself"
403:     end
404:   end

[Source]

     # File app/models/person.rb, line 804
804:   def hometown
805:     if city.blank?
806:       if state.blank?
807:         ''
808:       else
809:         if state == RacingAssociation.current.state
810:           ''
811:         else
812:           state
813:         end
814:       end
815:     else
816:       if state.blank?
817:         city
818:       else
819:         if state == RacingAssociation.current.state
820:           city
821:         else
822:           "#{city}, #{state}"
823:         end
824:       end
825:     end
826:   end

[Source]

     # File app/models/person.rb, line 828
828:   def hometown=(value)
829:     self.city = nil
830:     self.state = nil
831:     return value if value.blank?
832:     parts = value.split(',')
833:     if parts.size > 1
834:       self.state = parts.last.strip
835:     end
836:     self.city = parts.first.strip
837:   end

Under 18 years old

[Source]

     # File app/models/person.rb, line 493
493:   def junior?
494:     if date_of_birth
495:       date_of_birth >= Date.new(18.years.ago.year, 1, 1)
496:     end
497:   end

[Source]

     # File app/models/person.rb, line 289
289:   def last_name(date_or_year = nil)
290:     year = parse_year(date_or_year)
291:     name_record_for_year(year).try(:last_name) || read_attribute(:last_name)
292:   end

[Source]

     # File app/models/person.rb, line 376
376:   def last_name=(value)
377:     @old_name = name unless @old_name
378:     self[:last_name] = value
379:   end

[Source]

     # File app/models/person.rb, line 308
308:   def last_name_or_login
309:     if last_name.present?
310:       last_name
311:     elsif login.present?
312:       login
313:     else
314:       email
315:     end
316:   end

30 years old or older

[Source]

     # File app/models/person.rb, line 486
486:   def master?
487:     if date_of_birth
488:       date_of_birth <= Date.new(RacingAssociation.current.masters_age.years.ago.year, 12, 31)
489:     end
490:   end

[Source]

     # File app/models/person.rb, line 676
676:   def member
677:     member?
678:   end

Is Person a current member of the bike racing association?

[Source]

     # File app/models/person.rb, line 681
681:   def member=(value)
682:     if value
683:       self.member_from = RacingAssociation.current.today if member_from.nil? || member_from.to_date >= RacingAssociation.current.today.to_date
684:       unless member_to && (member_to.to_date >= Time.zone.local(RacingAssociation.current.effective_year, 12, 31).to_date)
685:         self.member_to = Time.zone.local(RacingAssociation.current.effective_year, 12, 31) 
686:       end
687:     elsif !value && member?
688:       if self.member_from.year == RacingAssociation.current.year
689:         self.member_from = nil
690:         self.member_to = nil
691:       else
692:         self.member_to = Time.zone.local(RacingAssociation.current.year - 1, 12, 31)
693:       end
694:     end
695:   end

Is Person a current member of the bike racing association?

[Source]

     # File app/models/person.rb, line 665
665:   def member?(date = RacingAssociation.current.today)
666:     member_to.present? && member_from.present? && (member_from.to_date <= date.to_date && member_to.to_date >= date.to_date)
667:   end

Also sets member_to if it is blank

[Source]

     # File app/models/person.rb, line 698
698:   def member_from=(date)
699:     if date.nil?
700:       self[:member_from] = nil
701:       self[:member_to] = nil
702:       return date
703:     end
704: 
705:     date_as_date = case date
706:     when Date
707:       date
708:     when DateTime, Time
709:       Date.new(date.year, date.month, date.day)
710:     else
711:       Date.parse(date)
712:     end
713: 
714:     if member_to.nil?
715:       self[:member_to] = Date.new(date_as_date.year, 12, 31)
716:     end
717:     self[:member_from] = date_as_date
718:   end

Is/was Person a current member of the bike racing association at any point during date‘s year?

[Source]

     # File app/models/person.rb, line 670
670:   def member_in_year?(date = RacingAssociation.current.today)
671:     year = date.year
672:     member_to && member_from && (member_from.year <= year && member_to.year >= year)
673:     member_to.present? && member_from.present? && (member_from.year <= year && member_to.year >= year)
674:   end

Also sets member_from if it is blank

[Source]

     # File app/models/person.rb, line 721
721:   def member_to=(date)
722:     unless date.nil?
723:       self[:member_from] = RacingAssociation.current.today if member_from.nil?
724:       self[:member_from] = date if member_from.to_date > date.to_date
725:     end
726:     self[:member_to] = date
727:   end

Validates member_from and member_to

[Source]

     # File app/models/person.rb, line 730
730:   def membership_dates
731:     if member_to && !member_from
732:       errors.add('member_from', "cannot be nil if member_to is not nil (#{member_to})")
733:     end
734:     if member_from && !member_to
735:       errors.add('member_to', "cannot be nil if member_from is not nil (#{member_from})")
736:     end
737:     if member_from && member_to && member_from.to_date > member_to.to_date
738:       errors.add('member_to', "cannot be greater than member_from: #{member_from}")
739:     end
740:   end

Moves another people’ aliases, results, and race numbers to this person, and delete the other person. Also adds the other people’ name as a new alias

[Source]

     # File app/models/person.rb, line 875
875:   def merge(other_person)
876:     # Consider just using straight SQL for this --
877:     # it's not complicated, and the current process generates an
878:     # enormous amount of SQL
879:     raise(ArgumentError, 'Cannot merge nil person') unless other_person
880:     raise(ArgumentError, 'Cannot merge person onto itself') if other_person == self
881: 
882:     Person.transaction do
883:       ActiveRecord::Base.lock_optimistically = false
884:       self.merge_version do
885:         events_with_results = other_person.results.collect do |result|
886:           event = result.event
887:           event.disable_notification! if event
888:           event
889:         end.compact || []
890:         if login.blank? && other_person.login.present?
891:           self.login = other_person.login
892:           self.crypted_password = other_person.crypted_password
893:           other_person.skip_version do
894:             other_person.update_attribute :login, nil
895:           end
896:         end
897:         if member_from.nil? || (other_person.member_from && other_person.member_from < member_from)
898:           self.member_from = other_person.member_from
899:         end
900:         if member_to.nil? || (other_person.member_to && other_person.member_to > member_to)
901:           self.member_to = other_person.member_to
902:         end
903: 
904:         if license.blank?
905:           self.license = other_person.license
906:         end
907: 
908:         save!
909:         aliases << other_person.aliases
910:         events << other_person.events
911:         names << other_person.names
912:         results << other_person.results
913:         race_numbers << other_person.race_numbers
914: 
915:         versions << other_person.versions
916:         versions.sort_by(&:created_at).each_with_index do |version, index|
917:           version.number = index + 2
918:           version.save!
919:         end
920: 
921:         Person.delete other_person.id
922:         existing_alias = aliases.detect{ |a| a.name.casecmp(other_person.name) == 0 }
923:         if existing_alias.nil? and Person.find_all_by_name(other_person.name).empty?
924:           aliases.create(:name => other_person.name) 
925:         end
926:         events_with_results.each do |event|
927:           event.reload
928:           event.enable_notification!
929:         end
930:       end
931:       ActiveRecord::Base.lock_optimistically = true
932:     end
933:     true
934:   end

Name on year. Could be rolled into Nameable?

[Source]

     # File app/models/person.rb, line 279
279:   def name(date_or_year = nil)
280:     year = parse_year(date_or_year)
281:     name_record_for_year(year).try(:name) || Person.full_name(first_name(year), last_name(year))
282:   end

Tries to split name into first_name and last_name TODO Handle name, Jr. This looks too complicated …

[Source]

     # File app/models/person.rb, line 342
342:   def name=(value)
343:     @old_name = name unless @old_name
344:     if value.blank?
345:       self.first_name = ''
346:       self.last_name = ''
347:       return
348:     end
349: 
350:     if value.include?(',')
351:       parts = value.split(',')
352:       if parts.size > 0
353:         self.last_name = parts[0].strip
354:         if parts.size > 1
355:           self.first_name = parts[1..(parts.size - 1)].join
356:           self.first_name.strip!
357:         end
358:       end
359:     else
360:       parts = value.split(' ')
361:       if parts.size > 0
362:         self.first_name = parts[0].strip
363:         if parts.size > 1
364:           self.last_name = parts[1..(parts.size - 1)].join(" ")
365:           self.last_name.strip!
366:         end
367:       end
368:     end
369:   end

Name. If name is blank, returns email and phone

[Source]

     # File app/models/person.rb, line 319
319:   def name_or_contact_info
320:     if name.blank?
321:       [email, home_phone].join(', ')
322:     else
323:       name
324:     end
325:   end

[Source]

     # File app/models/person.rb, line 298
298:   def name_or_login
299:     if name.present?
300:       name
301:     elsif login.present?
302:       login
303:     else
304:       email
305:     end
306:   end

[Source]

     # File app/models/person.rb, line 327
327:   def name_was
328:     @name_was ||= Person.full_name(first_name_was, last_name_was)
329:   end

[Source]

     # File app/models/person.rb, line 542
542:   def number(discipline_param, reload = false, year = nil)
543:     return nil if discipline_param.nil?
544: 
545:     year = year || RacingAssociation.current.year
546:     if discipline_param.is_a?(Discipline)
547:       discipline_param = discipline_param.to_param
548:     end
549:     number = race_numbers(reload).detect do |race_number|
550:       race_number.year == year && race_number.discipline_id == RaceNumber.discipline_id(discipline_param) && race_number.number_issuer.name == RacingAssociation.current.short_name
551:     end
552:     if number
553:       number.value
554:     else
555:       nil
556:     end
557:   end

[Source]

     # File app/models/person.rb, line 252
252:   def people_with_same_name
253:     people = Person.find_all_by_name(self.name) | Alias.find_all_people_by_name(self.name)
254:     people.reject! { |person| person == self }
255:     people
256:   end

[Source]

     # File app/models/person.rb, line 406
406:   def possessive_pronoun
407:     if gender == "F"
408:       "her"
409:     else
410:       "his"
411:     end
412:   end

[Source]

     # File app/models/person.rb, line 753
753:   def print_card?
754:     self.print_card
755:   end

[Source]

     # File app/models/person.rb, line 660
660:   def promoter?
661:     Event.exists?([ "promoter_id = ?", self.id ])
662:   end

Oldest age person will be at any point in year

[Source]

     # File app/models/person.rb, line 507
507:   def racing_age
508:     if date_of_birth
509:       (RacingAssociation.current.year - date_of_birth.year).ceil
510:     end
511:   end

[Source]

     # File app/models/person.rb, line 746
746:   def renew!(license_type)
747:     self.member = true
748:     self.print_card = true
749:     self.license_type = license_type
750:     save!
751:   end

[Source]

     # File app/models/person.rb, line 742
742:   def renewed?
743:     member_to && member_to.year >= RacingAssociation.current.effective_year
744:   end

[Source]

     # File app/models/person.rb, line 612
612:   def road_number(reload = false, year = nil)
613:     number(Discipline[:road], reload, year)
614:   end

[Source]

     # File app/models/person.rb, line 640
640:   def road_number=(value)
641:     add_number(value, Discipline[:road])
642:   end

Over 18 years old

[Source]

     # File app/models/person.rb, line 500
500:   def senior?
501:     if date_of_birth
502:       date_of_birth < Date.new(18.years.ago.year, 1, 1)
503:     end
504:   end

[Source]

     # File app/models/person.rb, line 958
958:   def set_created_by
959:     if created_by.nil? && Person.current
960:       self.created_by = Person.current
961:     end
962:   end

[Source]

     # File app/models/person.rb, line 952
952:   def set_last_updated_by
953:     if Person.current
954:       self.last_updated_by = Person.current.name_or_login
955:     end
956:   end

[Source]

     # File app/models/person.rb, line 616
616:   def singlespeed_number(reload = false, year = nil)
617:     number(Discipline[:singlespeed], reload, year)
618:   end

[Source]

     # File app/models/person.rb, line 644
644:   def singlespeed_number=(value)
645:     add_number(value, Discipline[:singlespeed])
646:   end

[Source]

     # File app/models/person.rb, line 765
765:   def state=(value)
766:     if value and value.size == 2
767:       value.upcase!
768:     end
769:     super
770:   end

[Source]

     # File app/models/person.rb, line 381
381:   def team_name
382:     if team
383:       team.name || ''
384:     else
385:       ''
386:     end
387:   end

[Source]

     # File app/models/person.rb, line 389
389:   def team_name=(value)
390:     if value.blank? || value == 'N/A'
391:       self.team = nil
392:     else
393:       self.team = Team.find_by_name_or_alias(value)
394:       self.team = Team.new(:name => value, :created_by => new_record? ? self.created_by : nil) unless self.team
395:     end
396:   end

[Source]

     # File app/models/person.rb, line 414
414:   def third_person_pronoun
415:     if gender == "F"
416:       "her"
417:     else
418:       "him"
419:     end
420:   end

[Source]

      # File app/models/person.rb, line 1010
1010:   def to_s
1011:     "#<Person #{id} #{first_name} #{last_name} #{team_id}>"
1012:   end

Hack around in-place editing

[Source]

     # File app/models/person.rb, line 840
840:   def toggle!(attribute)
841:     logger.debug("toggle! #{attribute} #{attribute == 'member'}")
842:     if attribute == 'member'
843:       self.member = !member?
844:       save!
845:     else
846:       super
847:     end
848:   end

[Source]

     # File app/models/person.rb, line 620
620:   def track_number(reload = false, year = nil)
621:     number(Discipline[:track], reload, year)
622:   end

[Source]

     # File app/models/person.rb, line 648
648:   def track_number=(value)
649:     add_number(value, Discipline[:track])
650:   end

Cannot have promoters with duplicate contact information

[Source]

     # File app/models/person.rb, line 332
332:   def unique_info
333:     person = Person.find_by_info(name, email, home_phone)
334:     if person && person != self
335:       errors.add("existing person with name '#{name}'")
336:     end
337:   end

[Source]

     # File app/models/person.rb, line 761
761:   def updated_after_created?
762:     created_at && updated_at && ((updated_at - created_at) > 1.hour) && updated_by
763:   end

[Source]

     # File app/models/person.rb, line 985
985:   def validate_unique_editors(editor)
986:     if editors.include?(editor)
987:       raise ActiveRecord::ActiveRecordError, "Can't add duplicate editor #{editor.name} for #{name}"
988:     end
989:     
990:     if editor == self
991:       raise ActiveRecord::ActiveRecordError, "Can't be editor for self"
992:     end
993:   end

[Source]

     # File app/models/person.rb, line 624
624:   def xc_number(reload = false, year = nil)
625:     number(Discipline[:mountain_bike], reload, year)
626:   end

[Source]

     # File app/models/person.rb, line 652
652:   def xc_number=(value)
653:     add_number(value, Discipline[:mountain_bike])
654:   end

[Validate]