class Hermes::Addr

A parser and generator for mail address fields.

Examples

a = Addr.create "dummy@example.com", "John Doe"
a.to_s      #=>  "John Doe <dummy@example.com>"
a.quote     #=>  "John Doe <dummy@example.com>"
a.encode    #=>  "John Doe <dummy@example.com>"

a = Addr.create "dummy@example.com", "Müller, Fritz"
a.to_s      #=>  "Müller, Fritz <dummy@example.com>"
a.quote     #=>  "\"Müller, Fritz\" <dummy@example.com>"
a.encode    #=>  "=?utf-8?q?M=C3=BCller=2C_Fritz?= <dummy@example.com>"

Parsing

x = <<-'EOT'
Jörg Q. Müller <jmuell@example.com>, "Meier, Hans"
  <hmei@example.com>, Möller\, Fritz <fmoel@example.com>
EOT
Addr.parse x do |a,g|
  puts a.quote
end

# Output:
#   Jörg Q. Müller <jmuell@example.com>
#   "Meier, Hans" <hmei@example.com>
#   "Möller, Fritz" <fmoel@example.com>

x = "some: =?utf-8?q?M=C3=B6ller=2C_Fritz?= " +
    "<fmoeller@example.com> webmaster@example.com; foo@example.net"
Addr.parse_decode x do |a,g|
  puts g.to_s
  puts a.quote
end

# Output:
#   some
#   "Möller, Fritz" <fmoeller@example.com>
#   some
#   <webmaster@example.com>
#
#   <foo@example.net>

Attributes

encoding_parameters[R]
mail[R]
real[R]

Public Class Methods

new(mail, real) click to toggle source
# File lib/hermes/addrs.rb, line 99
def initialize mail, real
  @mail, @real = mail, real
  @mail.compact!
  @real.compact! if @real
end

Public Instance Methods

==(oth) click to toggle source
# File lib/hermes/addrs.rb, line 105
def == oth
  plain == case oth
    when Addr then oth.plain
    else           oth.to_s.downcase
  end
end
create(mail, real = nil) click to toggle source
# File lib/hermes/addrs.rb, line 87
def create mail, real = nil
  m = Token[ :addr, (Token.lexer mail)]
  r = Token[ :text, (Token.lexer real)] if real
  new m, r
end
encode() click to toggle source
# File lib/hermes/addrs.rb, line 132
def encode
  tokenized.encode
end
inspect() click to toggle source
# File lib/hermes/addrs.rb, line 120
def inspect
  "<##{self.class}: mail=#{@mail.inspect} real=#{@real.inspect}>"
end
plain() click to toggle source
# File lib/hermes/addrs.rb, line 112
def plain
  @plain ||= mk_plain
end
quote() click to toggle source
# File lib/hermes/addrs.rb, line 128
def quote
  tokenized.quote
end
to_s() click to toggle source
# File lib/hermes/addrs.rb, line 124
def to_s
  tokenized.to_s
end
tokenized() click to toggle source
# File lib/hermes/addrs.rb, line 136
def tokenized
  r = Token[ :addr, [ Token[ :lang] , @mail, Token[ :rang]]]
  if @real then
    r = Token[ :text, [ @real, Token[ :space], r]]
  end
  r
end

Private Instance Methods

compile(l, &block) click to toggle source
# File lib/hermes/addrs.rb, line 438
def compile l, &block
  l = unspace l
  l = uncomment l
  g = split_groups l
  groups_compile g, &block
end
find_one_of(l, s, t) click to toggle source
# File lib/hermes/addrs.rb, line 558
def find_one_of l, s, t
  l.each_with_index { |e,i|
    if e === s then
      return i, nil
    elsif e === t then
      return nil, i
    end
  }
  nil
end
groups_compile(g) { |a, k| ... } click to toggle source
# File lib/hermes/addrs.rb, line 445
def groups_compile g
  if block_given? then
    g.each { |k,v|
      split_list v do |m,r|
        a = new m, r
        yield a, k
      end
    }
    return
  end
  t = []
  groups_compile g do |a,| t.push a end
  t
end
matches(l, *tokens) click to toggle source
# File lib/hermes/addrs.rb, line 460
def matches l, *tokens
  z = tokens.zip l
  z.each { |(s,e)|
    e === s or return
  }
  true
end
mk_plain() click to toggle source
# File lib/hermes/addrs.rb, line 146
def mk_plain
  p = @mail.to_s
  p.downcase!
  p
end
parse(str, &block) click to toggle source

Parse a line from a string that was entered by the user.

x = "Meier, Hans <hmei@example.com>, foo@example.net"
Addr.parse x do |a,g|
  puts a.quote
end

# Output:
  "Meier, Hans" <hmei@example.com>
  <foo@example.net>
# File lib/hermes/addrs.rb, line 412
def parse str, &block
  l = Token.lexer str
  compile l, &block
end
parse_decode(str, &block) click to toggle source

Parse a line from a mail header field and make addresses of it.

Internally the encoding class HeaderExt will be used.

x = "some: =?utf-8?q?M=C3=B6ller=2C_Fritz?= <fmoeller@example.com>"
Addr.parse_decode x do |addr,group|
  puts group.to_s
  puts addr.quote
end

# Output:
#   some
#   "Möller, Fritz" <fmoeller@example.com>
# File lib/hermes/addrs.rb, line 431
def parse_decode str, &block
  l = Token.lexer_decode str
  compile l, &block
end
split_groups(l) click to toggle source
# File lib/hermes/addrs.rb, line 510
def split_groups l
  g = []
  n = nil
  while l.any? do
    n = if matches l, :text, :colon then
      e = l.shift
      l.shift
      e.to_s
    end
    s = []
    until matches l, :semicol or l.empty? do
      s.push l.shift
    end
    l.shift
    g.push [ n, s]
  end
  g
end
split_list(l) { |Token, real&&Token| ... } click to toggle source
# File lib/hermes/addrs.rb, line 529
def split_list l
  while l.any? do
    if matches l, :text, :comma, :text, :lang then
      t = l.first.to_s
      if t =~ /[^a-z0-9_]/ then
        e = Token[ :text, []]
        e.data.push l.shift
        e.data.push l.shift, Token[ :space]
        e.data.push l.shift
        l.unshift e
      end
    end
    a, c = find_one_of l, :lang, :comma
    if a then
      real = l.shift a if a.nonzero?
      l.shift
      a, c = find_one_of l, :rang, :comma
      mail = l.shift a||c||l.length
      l.shift
      l.shift if matches l, :comma
    else
      mail = l.shift c||l.length
      l.shift
      real = nil
    end
    yield Token[ :addr, mail], real&&Token[ :text, real]
  end
end
uncomment(l) click to toggle source
# File lib/hermes/addrs.rb, line 494
def uncomment l
  r = []
  while l.any? do
    if matches l, :lparen then
      l.shift
      l = uncomment l
      until matches l, :rparen or l.empty? do
        l.shift
      end
      l.shift
    end
    r.push l.shift
  end
  r
end
unspace(l) click to toggle source
# File lib/hermes/addrs.rb, line 468
def unspace l
  r = []
  while l.any? do
    if matches l, :space then
      l.shift
      next
    end
    if matches l, :char then
      e = Token[ :text, [ l.shift]]
      loop do
        if matches l, :char then
          e.data.push l.shift
        elsif matches l, :space, :char then
          e.data.push l.shift
          e.data.push l.shift
        else
          break
        end
      end
      l.unshift e
    end
    r.push l.shift
  end
  r
end