]> git.cworth.org Git - sup/blob - lib/sup/person.rb
don't crash when people.txt is corrupted
[sup] / lib / sup / person.rb
1 module Redwood
2
3 class PersonManager
4   include Singleton
5
6   def initialize fn
7     @fn = fn
8     @@people = {}
9
10     ## read in stored people
11     IO.readlines(fn).map do |l|
12       l =~ /^(.*)?:\s+(\d+)\s+(.*)$/ or next
13       email, time, name = $1, $2, $3
14       @@people[email] = Person.new name, email, time, false
15     end if File.exists? fn
16
17     self.class.i_am_the_instance self
18   end
19
20   def save
21     File.open(@fn, "w") do |f|
22       @@people.each do |email, p|
23         next if p.email == p.name
24         next if p.email =~ /=/ # drop rfc2047-encoded, and lots of other useless emails. definitely a heuristic.
25         f.puts "#{p.email}: #{p.timestamp} #{p.name}"
26       end
27     end
28   end
29
30   def self.people_for s, opts={}
31     return [] if s.nil?
32     s.split_on_commas.map { |ss| self.person_for ss, opts }
33   end
34
35   def self.person_for s, opts={}
36     p = Person.from_address(s) or return nil
37     p.definitive = true if opts[:definitive]
38     register p
39   end
40   
41   def self.register p
42     oldp = @@people[p.email]
43
44     if oldp.nil? || p.better_than?(oldp)
45       @@people[p.email] = p
46     end
47
48     @@people[p.email].touch!
49     @@people[p.email]
50   end
51 end
52
53 ## don't create these by hand. rather, go through personmanager, to
54 ## ensure uniqueness and overriding.
55 class Person 
56   attr_accessor :name, :email, :timestamp
57   bool_accessor :definitive
58
59   def initialize name, email, timestamp=0, definitive=false
60     raise ArgumentError, "email can't be nil" unless email
61     
62     if name
63       @name = name.gsub(/^\s+|\s+$/, "").gsub(/\s+/, " ")
64       if @name =~ /^(['"]\s*)(.*?)(\s*["'])$/
65         @name = $2
66       end
67     end
68
69     @email = email.gsub(/^\s+|\s+$/, "").gsub(/\s+/, " ").downcase
70     @definitive = definitive
71     @timestamp = timestamp
72   end
73
74   ## heuristic: whether the name attached to this email is "real", i.e. 
75   ## we should bother to store it.
76   def generic?
77     @email =~ /no\-?reply/
78   end
79
80   def better_than? o
81     return false if o.definitive? || generic?
82     return true if definitive?
83     o.name.nil? || (name && name.length > o.name.length && name =~ /[a-z]/)
84   end
85
86   def to_s; "#@name <#@email>" end
87
88   def touch!; @timestamp = Time.now.to_i end
89
90 #   def == o; o && o.email == email; end
91 #   alias :eql? :==
92 #   def hash; [name, email].hash; end
93
94   def shortname
95     case @name
96     when /\S+, (\S+)/
97       $1
98     when /(\S+) \S+/
99       $1
100     when nil
101       @email
102     else
103       @name
104     end
105   end
106
107   def longname
108     if @name && @email
109       "#@name <#@email>"
110     else
111       @email
112     end
113   end
114
115   def mediumname; @name || @email; end
116
117   def full_address
118     if @name && @email
119       if @name =~ /[",@]/
120         "#{@name.inspect} <#{@email}>" # escape quotes
121       else
122         "#{@name} <#{@email}>"
123       end
124     else
125       email
126     end
127   end
128
129   ## when sorting addresses, sort by this 
130   def sort_by_me
131     case @name
132     when /^(\S+), \S+/
133       $1
134     when /^\S+ \S+ (\S+)/
135       $1
136     when /^\S+ (\S+)/
137       $1
138     when nil
139       @email
140     else
141       @name
142     end.downcase
143   end
144
145   def self.from_address s
146     return nil if s.nil?
147
148     ## try and parse an email address and name
149     name, email =
150       case s
151       when /["'](.*?)["'] <(.*?)>/, /([^,]+) <(.*?)>/
152         a, b = $1, $2
153         [a.gsub('\"', '"'), b]
154       when /<((\S+?)@\S+?)>/
155         [$2, $1]
156       when /((\S+?)@\S+)/
157         [$2, $1]
158       else
159         [nil, s]
160       end
161
162     Person.new name, email
163   end
164
165   def eql? o; email.eql? o.email end
166   def hash; email.hash end
167 end
168
169 end