わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

Ruby 1.9で日本語を含むメールを送る

半月ほど前の話です.毎週木曜日の演習科目で,1回だけ,それまでの課題の解答をTAと私とでレビューして,学生ごとにメールを送りました.
2年前にも実施していました*1.レビュー結果のExcelファイルをCSV形式で保存して,集計用のRubyスクリプトと,メール送信用のRubyスクリプトを,順に実行すればいいのです.
去年は,ファイルがないなあ,せんかったんかなあ….
ともあれ,2年前のプログラムを見直し,Ruby 1.9対応にしとこかと手を加えました.dry run*2はOK.そして実行とすると,さっそくエラーが出ました.
Rubyのバージョンは以下のとおり.

$ ruby -v
ruby 1.9.2dev (2009-11-04 trunk 25650) [i686-linux]

スクリプトの概要はこうなっていて…

# server, header, body, from, to, ccに適切な値(文字列)を代入
# bodyの文字コードは,初めはUTF-8で,すべて格納したらbody=body.tojis
Net::SMTP.start(server, 25) {|smtp|
  smtp.ready(from, to, cc) {|s|
    s.puts header
    s.puts
    s.puts body
  }
}

エラーメッセージは

(略)/lib/ruby/1.9.1/net/protocol.rb:359:in `puts': incompatible character encodings: ISO-2022-JP and US-ASCII (Encoding::CompatibilityError)

とか,少しコードを変えても

(略)/lib/ruby/1.9.1/net/protocol.rb:305:in `slice!': incompatible encoding regexp match (US-ASCII regexp with ISO-2022-JP string) (Encoding::CompatibilityError)

とか.
protocol.rbを開いてみたものの,どうにも対策できません.
呼び出し方としてはputsメソッドですが,内部では文字列処理をしていて,そのあたりに起因するエラーなのはわかるのですが.
さてあまり時間がありません.午後の授業開始までに,メールを送り終えたいのです.授業の初めに解説をしたいし,そもそも授業中にスクリプトいじりをする時間的・精神的余裕はないでしょう….
といったところで,悪魔がささやきました.tojisしたあとのbodyを,中身(バイト列)はそのままで,エンコーディングを「US-ASCII」にすればいいじゃないかと.
やってみました:

# server, header, body, from, to, ccに適切な値を入れておく
# bodyの文字コードはUTF-8
Net::SMTP.start(server, 25) {|smtp|
  if RUBY_VERSION >= "1.9"
    smtp.send_message (header + "\n" + body).tojis.force_encoding("US-ASCII"), from, to, cc
  else
    smtp.ready(from, to, cc) {|s|
      s.puts header
      s.puts
      s.puts body.tojis
    }
  end
}

いきなり実行というのは,危険があぶないので,header生成時に「toを自分」「1通送ったら終了」とする処理を暫定的に入れて,実行すると,問題なかったようなので,暫定コードを外して実行しました.うまくいったっぽいです.「いったっぽい」というのは,実はsmtp.send_messageの呼び出しのところを

    smtp.send_message (header + "\n" + body).tojis.force_encoding("US-ASCII"), from, to

と書いていて,学生に届くメールには,「Cc: 俺のアドレス」が書かれているのに,俺んとこには送っていないからでした!
インスタンスメソッドNet::SMTP#send_message*3の引数は,最初は本文,次は送信元のメールアドレス,残りは送り先のメールアドレスです.ヘッダにToとかCcとかBccとか書いていても,それは文字列と見なされるわけですね.逆に,ToにもCcにもないメールアドレスを,引数に追加すれば,それがBcc扱いになるということか….
年1回のスクリプト,来年に向けての課題は:

  • 日本語を含むメールをRubyで送るための,より良い方法を知る.
  • dry runオプションをつける.

*1:日記に書いたはず…と見直したところ,2年前ではなく3年前に書いていたのでした! 演習のレビュー結果を一斉送信, 一斉送信への対応 - わさっき

*2:dry runの意味・使い方・読み方 | Weblio英和辞書, http://english.kyounotubo.mond.jp/?eid=581902, http://kawama.jp/archives/2007/10/rsyncdryrun.html

*3:あれ? loveruby.netにアクセスできない…ruby-listの46278, 46279.ありがたや.