module Redwood
-class PersonManager
- include Singleton
-
- def initialize fn
- @fn = fn
- @names = {}
- IO.readlines(fn).map { |l| l =~ /^(.*)?:\s+(\d+)\s+(.*)$/ && @names[$1] = [$2.to_i, $3] } if File.exists? fn
- self.class.i_am_the_instance self
- end
-
- def name_for email; @names[email][1]; end
- def register email, name
- return unless name
-
- name = name.gsub(/^\s+|\s+$/, "").gsub(/\s+/, " ")
-
- ## all else being equal, prefer longer names, unless the prior name
- ## doesn't contain any capitalization
- oldcount, oldname = @names[email]
- @names[email] = [0, name] if oldname.nil? || oldname.length < name.length || (oldname !~ /[A-Z]/ && name =~ /[A-Z]/)
- @names[email][0] = Time.now.to_i
- end
-
- def save; File.open(@fn, "w") { |f| @names.each { |email, (time, name)| f.puts "#{email}: #{time} #{name}" } }; end
-end
-
-class Person
- @@email_map = {}
-
+class Person
attr_accessor :name, :email
def initialize name, email
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
end
- def == o; o && o.email == email; end
- alias :eql? :==
- def hash; [name, email].hash; end
+ def to_s; "#@name <#@email>" end
+
+# def == o; o && o.email == email; end
+# alias :eql? :==
+# def hash; [name, email].hash; end
def shortname
case @name
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
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
- name, email =
- case s
+ name, email = case s
+ when /(.+?) ((\S+?)@\S+) \3/
+ ## ok, this first match cause is insane, but bear with me. email
+ ## addresses are stored in the to/from/etc fields of the index in a
+ ## weird format: "name address first-part-of-address", i.e. spaces
+ ## separating those three bits, and no <>'s. this is the output of
+ ## #indexable_content. here, we reverse-engineer that format to extract
+ ## a valid address.
+ ##
+ ## we store things this way to allow searches on a to/from/etc field to
+ ## match any of those parts. a more robust solution would be to store a
+ ## separate, non-indexed field with the proper headers. but this way we
+ ## save precious bits, and it's backwards-compatible with older indexes.
+ [$1, $2]
when /["'](.*?)["'] <(.*?)>/, /([^,]+) <(.*?)>/
a, b = $1, $2
[a.gsub('\"', '"'), b]
[nil, s]
end
- @@email_map[email] ||= Person.new name, email
+ Person.new name, email
end
- def self.for_several s
- return [] if s.nil?
+ def self.from_address_list ss
+ return [] if ss.nil?
+ ss.split_on_commas.map { |s| self.from_address s }
+ end
- begin
- s.split_on_commas.map { |ss| self.for ss }
- rescue StandardError => e
- raise "#{e.message}: for #{s.inspect}"
- end
+ ## see comments in self.from_address
+ 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