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