mailread.rb

1.9ではなくなるらしい。
そんな使ってるわけでもないけど、ちっこいライブラリなのは知ってたので書き直してみた。


クラス名が変わっているので完全互換! とは謳えないが、中身は互換性があるはず。
どれくらいまで動くのかな。Ruby1.8.7系の新機能は使ってないからわりと動くと思うんだけど。

mailread.rb

class MailRead
  def initialize(f)
    @header = {}
    @body   = []
    parse f
  end
  
  attr_reader :header
  attr_reader :body
  
  def [](field)
    @header[field.capitalize]
  end
  
  private
  
  FROM_LINE_RE        = /^From /
  HEADER_PART_END_RE  = /^$/
  HEADER_BEGIN_RE     = /^(\S+?):\s*(.*)/
  
  def parse(src)
    io_or_path_handling(src) do |f|
      # parse header part
      header_block = []
      while line = f.gets
        line.chomp!
        case line
        when FROM_LINE_RE       then ;     # skip From line
        when HEADER_PART_END_RE then break # end of header
        else header_block.push(line)
        end
      end
      parse_header_block(header_block)
      # parse body part
      while line = f.gets
        break if FROM_LINE_RE =~ line
        body.push(line)
      end
    end
  end
  
  def parse_header_block(block)
    # at first, find first header
    while line = block.first
      break if HEADER_BEGIN_RE =~ line
      block.shift
    end
    until block.empty?
      HEADER_BEGIN_RE =~ block.shift
      attr, buf = $1.capitalize, [$2]
      while line = block.first
        break if HEADER_BEGIN_RE =~ line
        buf << block.shift.lstrip
      end
      header[attr] = buf.join("\n")
    end
  end
  
  def io_or_path_handling(f)
    f.respond_to?(:gets) ? yield(f) : File.open(f){|io| yield(io) }
  end
  
end #class MailRead


まずテストを旧版(標準添付してたヤツ)のために書いた。Rubyソースコードのtest/には見当たらなかった。ちゃんと探せばあるのかもしんないけど。

そのテストコード。とりあえず旧版と書き直した版の両方のC0カバレッジは100%になる。
Ruby1.8.7 + expectations。

test_mailread.rb

require 'expectations'
require 'stringio'
require 'tempfile'
require './mailread'

# for compatibility of "mailread.rb"
MailRead = Mail unless defined?(MailRead)
header = {
  "X-header-one"    => "one",
  "X-header-two"    => "two\ntwo",
  "X-header-three"  => "three\n  three",
}
body = ["body\n", "lines\n"]
test_mail = StringIO.new(
  "From - #{Time.now}\n" +
  "Hogeee!!!\n" + 
  header.map{|k, v| "#{k}: #{v}\n" }.join +
  "\n" +
  body.join
)
header["X-header-three"] = "three\nthree"

Expectations do
  mail = MailRead.new(test_mail)
  
  expect header do
    {}.tap do |h|
      mail.header.each{|k, v| h[k] = v }
    end
  end
  
  expect body do
    [].tap do |a|
      mail.body.each{|s| a << s }
    end
  end
  
  expect header["X-header-one"] do
    mail["X-header-one"]
  end
  
  expect header["X-header-one"] do
    mail["X-HEADER-ONE"]
  end
  
  expect MailRead do
    ret = nil
    Tempfile.open('test_mailread') do |f|
      f.write test_mail.string
      ret = MailRead.new(f.path)
    end
    ret
  end
end