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