]> git.cworth.org Git - sup/blobdiff - lib/sup/person.rb
Merge commit 'origin/reply-from-hook'
[sup] / lib / sup / person.rb
index 206885b5c8c1d97a164ae084a775f9c5b3b92b3e..fb58f23889d53503bce539407b2583767b40a49c 100644 (file)
@@ -5,41 +5,91 @@ class PersonManager
 
   def initialize fn
     @fn = fn
-    @names = {}
-    IO.readlines(fn).map { |l| l =~ /^(.*)?:\s+(.*)$/ && @names[$1] = $2 } if File.exists? fn
+    @@people = {}
+
+    ## read in stored people
+    IO.readlines(fn).map do |l|
+      l =~ /^(.*)?:\s+(\d+)\s+(.*)$/ or next
+      email, time, name = $1, $2, $3
+      @@people[email] = Person.new name, email, time, false
+    end if File.exists? fn
+
     self.class.i_am_the_instance self
   end
 
-  def name_for email; @names[email]; end
-  def register email, name
-    return unless name
+  def save
+    File.open(@fn, "w") do |f|
+      @@people.each do |email, p|
+        next if p.email == p.name
+        next if p.name =~ /=/ # drop rfc2047-encoded, and lots of other useless emails. definitely a heuristic.
+        f.puts "#{p.email}: #{p.timestamp} #{p.name}"
+      end
+    end
+  end
 
-    name = name.gsub(/^\s+|\s+$/, "").gsub(/\s+/, " ")
+  def self.people_for s, opts={}
+    return [] if s.nil?
+    s.split_on_commas.map { |ss| self.person_for ss, opts }
+  end
 
-    ## all else being equal, prefer longer names, unless the prior name
-    ## doesn't contain any capitalization
-    oldname = @names[email]
-    @names[email] = name if oldname.nil? || oldname.length < name.length || (oldname !~ /[A-Z]/ && name =~ /[A-Z]/)
+  def self.person_for s, opts={}
+    p = Person.from_address(s) or return nil
+    p.definitive = true if opts[:definitive]
+    register p
   end
+  
+  def self.register p
+    oldp = @@people[p.email]
 
-  def save; File.open(@fn, "w") { |f| @names.each { |email, name| f.puts "#{email}: #{name}" } }; end
-end
+    if oldp.nil? || p.better_than?(oldp)
+      @@people[p.email] = p
+    end
 
-class Person
-  @@email_map = {}
+    @@people[p.email].touch!
+    @@people[p.email]
+  end
+end
 
-  attr_accessor :name, :email
+## don't create these by hand. rather, go through personmanager, to
+## ensure uniqueness and overriding.
+class Person 
+  attr_accessor :name, :email, :timestamp
+  bool_accessor :definitive
 
-  def initialize name, email
+  def initialize name, email, timestamp=0, definitive=false
     raise ArgumentError, "email can't be nil" unless email
+    
+    if name
+      @name = name.gsub(/^\s+|\s+$/, "").gsub(/\s+/, " ")
+      if @name =~ /^(['"]\s*)(.*?)(\s*["'])$/
+        @name = $2
+      end
+    end
+
     @email = email.gsub(/^\s+|\s+$/, "").gsub(/\s+/, " ").downcase
-    PersonManager.register @email, name
-    @name = PersonManager.name_for @email
+    @definitive = definitive
+    @timestamp = timestamp
+  end
+
+  ## heuristic: whether the name attached to this email is "real", i.e. 
+  ## we should bother to store it.
+  def generic?
+    @email =~ /no\-?reply/
   end
 
-  def == o; o && o.email == email; end
-  alias :eql? :==
-  def hash; [name, email].hash; end
+  def better_than? o
+    return false if o.definitive? || generic?
+    return true if definitive?
+    o.name.nil? || (name && name.length > o.name.length && name =~ /[a-z]/)
+  end
+
+  def to_s; "#@name <#@email>" end
+
+  def touch!; @timestamp = Time.now.to_i end
+
+#   def == o; o && o.email == email; end
+#   alias :eql? :==
+#   def hash; [name, email].hash; end
 
   def shortname
     case @name
@@ -66,13 +116,13 @@ class Person
 
   def full_address
     if @name && @email
-      if @name =~ /"/
-        "#{@name.inspect} <#@email>" # escape quotes
+      if @name =~ /[",@]/
+        "#{@name.inspect} <#{@email}>" # escape quotes
       else
-        "#@name <#@email>"
+        "#{@name} <#{@email}>"
       end
     else
-      @email
+      email
     end
   end
 
@@ -92,7 +142,7 @@ class Person
     end.downcase
   end
 
-  def self.for s
+  def self.from_address s
     return nil if s.nil?
 
     ## try and parse an email address and name
@@ -109,18 +159,15 @@ class Person
         [nil, s]
       end
 
-    @@email_map[email] ||= Person.new name, email
+    Person.new name, email
   end
 
-  def self.for_several s
-    return [] if s.nil?
-
-    begin
-      s.split_on_commas.map { |ss| self.for ss }
-    rescue StandardError => e
-      raise "#{e.message}: for #{s.inspect}"
-    end
+  def indexable_content
+    [name, email, email.split(/@/).first].join(" ")
   end
+
+  def eql? o; email.eql? o.email end
+  def hash; email.hash end
 end
 
 end