Chienomi

Mail Deliver 0.0.1 release!

有用なユーティリティコード

全面的にコードを書き直す改変を行った。GitHubに反映済みだ。

従来のコードではアドレスの抽出がうまく動いておらず、アドレスに合わせてソートされていなかった。どこが問題なのか見つけ出すよりは全面的に新しいコードにしたほうがよさそうだったため、大幅に修正した。

メインとなるlocaldelivは従来を踏襲する。ただし、ヘッダーの取得・保存に使うスクリプト、アドレス抽出に使うスクリプトはあたらしくなり、またlocaldelivが何を受け取り何を渡すかという動作も変更された。

getheaderスクリプトはメールのヘッダを読み、NKFで変換して適切に結合し、Hashにして、YAMLにして出力する。NKFを使うため、日本語か、ASCIIか、UTF-8のメールでないとうまく動作しない。これはRubyスクリプトだが、Mageiaはnkfパッケージをもっていないし、恐らくRuby経由でNKFを使うほうが確実に動作する。

getaddrはそのHashからFromの値をとった上で正規表現でマッチングを行い、アドレスを取得して出力する。うまく取得できない場合はexit 1だが、それよりもむしろlocaldeliv側でif [[ -n $addr ]]しているほうが重要になる。

localdelivはソートされなかったメールを$MH/inbox/domain/addressに振り分ける。Zshのコードは今回はあまり難しいものはいれていないが、この部分については

box="inbox/address/${addr#*@}/${addr}"

という形でドメインの切りだしを行っている。

savemailはほぼ以前の通りだが、設計がおかしかったので、修正した。

従来、savemailにはフォルダのみを渡していた。この時渡されるのはdomain/addressだけだった。しかし、このためにinboxフォルダを使うようなものが$Mail/inbox/address/inboxになってしまっていた。そこで、$Mail/junkなどを使うためにも、$Mail以下を指定するようにした。これは、localdelivが渡す値の修正と、savemailの受けとった値の取り扱いが変更された。

また、localdelivが呼ぶコマンドにはmaildeliv.というプレフィクスをつけるようになった。これにあわせて名前を変更しながらコピーするインストールスクリプトを書く予定だ。

基本的に修正したのはこの4つだ。とりあえず、目的の動作はするようになったが、メール関連はいくつかユーティリティを書かないとうまく動作しない。また、設定サンプルも書いたほうがいいのだろうか?

このメールユーティリティはZshとRubyの組み合わせとなっている。まさに私らしいユーティリティスクリプトだと言えるだろう。

さて、ここまで書いてから一日がかりの大幅な加筆修正とバグフィックスを行った。コードの詳細はかなり詳しいREADMEもついているのでGitHubを参照して欲しい。ここでは裏話をしよう。

今回最もハマったのは、Rubyでexit 1が書けないことだろう。カッコが省略できない。ちなみに、Kernel.abortも省略できなかった。以前(1.8.6)はできた気がするのだが、やはり1.9以降の変更でハマっている状況だ。

設計は根本的に見直した。まず、今回再認識したのが、設計していないコードや、コメントのないコードは、手が入る時点で投げ捨てるのが正義である、ということだ。

とりあえずでやっつけで作ったこのプログラムは徐々に拡張されてきたが、その過程で入出力のフォーマットの整合性がなかったり、YAMLをつかっているのにわざわざ正規表現で抽出したりということがあった。変数名が統一されていないためにエラーになるような状況もあった。

そのことからそもそもlocaldelivが既存のutilを利用せず新たに書き起こす展開だったのだが、当然ながらlocaldelivに限らずほとんど書き直すことになった。ただ、mail-notify-countに関しては既にYAMLでカウントするようになっていたためそのままとなった。しかしNotify系のコマンドも整合性をとるための編集をしたり、動作モデルを変更したりとなかなか大変だった。

特に大掛かりな変更となったのがmdafilterだろう。なにしろ、今までZshで書いていたものをRubyで書き直し、設定ファイルの形式も変更されてしまったのだから。どれほど変更されたかはgit logで具体的に確認することができるが、結局「これは違うな」と思った時点で挙動を確認せず新たに書き起こしてしまった部分がほとんどだし、修正した部分の中にも最初からやり直すのと大して変わらない労力をかけた部分が多い。

こんな小さなプログラムに6時間もかかってしまったので、だいぶ悔しい。

ソートルールに関しては従来、第二フィールドの値をシェルコマンドとして実行して終了ステータスから判断する仕様だったが、ほとんどの場合Fromから判断するのだから、コマンドで、というのはあまり合理的でない。そこで、Rubyに変更し、ルール自体をRubyで書くことにした。Procに対して渡されるのはメールヘッダのHashオブジェクトだが、さらにアドレスについては#fromメソッドで取得できるほか、#bodyメソッドで本文も取得できる。ちなみに、メールボディについては呼ばれた時にメールを読んでインスタンス変数に格納する仕組みでロードを遅延している。これは、メールボディはSTDIN経由で渡す、という方法で実現している。最新のコードでは次のようになっている

def body @body ||= NKF.nkf("-w -Lu -m", STDIN.read.to_s).split(" ", 2).last end

今回の作業は多岐にわたったが、なんといってもちゃんとしたREADMEにInstallスクリプトまで書いてリリースにこぎつけた。

ぜひGitHubをのぞいてみてほしい。