今回はtDiaryのプラグインの仕組みを読んでみたいと思います。いっても今までtDiaryを読んだことがないので1から読んでいきたいと思います。今回読んだtDiaryのバージョンは2.1.4です。何はなくともindex.rbを見てみましょう。興味深いコードがあります。'tdiary'$:はファイルのロードパスです。そこにindex.rb*1が置かれているディレクトリを登録することでカレントディレクトリがどこから実行されてもindex.rbと同じディレクトリにあるtdiary.rbをロードできるようにしています。*2次にTDiary::Configオブジェクトを作ることでtdiary.confのロードを行っています。流そうと思ったのですがまた興味深いコードがありました。TDiary::Config.initializeメソッドにおいて次のコードがあります。pendSRC)endインスタンス変数からgetterメソッドとsetterメソッドを動的に生成しています。う〜ん、Rubyならではですね。その後、渡された引数に応じてTDiary::TDiaryBaseから派生したクラスのオブジェクトを作成、eval_rhtmlメソッドを呼ぶことで出力を作っているようです。†さて、eval_rhtmメソッドl(実体はprotectedなdo_eval_rhtmlメソッド)ですが初めにプラグインをロードしているようです。いうわけでその部分を見てみましょう。10spamfilter.rbという「数字2桁+名前.rb」でファイルが置かれているわけですがsortをかけることで必ず番号の小さいファイルから読まれるようにしています。globが番号の小さい順にしてくれる保証はありませんからね。個々のプラグインは次のコードで読み込まれています。)endend興味深いところは2つあります。言語名ディレクトリにあるプラグインファイル名と同名のリソースファイルを読み込んでいるところとプラグインファイルの内容をinstance_evalしているところです。表示メッセージなどの文字列を別ファイルにすることで言語を切り替えやすくなります。foo...endとすればよいことになります。†さて、上のコードではtdiaryディレクトリ直下のpluginディレクトリにあるプラグインしか読み込まれません。現在のtDiaryにはmisc/pluginディレクトリにあるプラグインから使いたいプラグインを選択するという機能があります。これはどのように実現されているのでしょうか?どうやらその仕事をしているのは50sp.rbのようです。50sp.rbの最後に以下のコードがあります。)endbreakendendendend@data_path/tdiary.confを見るとoptions2配列にsp.selectedというキー名で選択したプラグインが列挙されていることがわかると思います。@sp_pathは通常[misc/plugin]なので、これで選択したプラグインが読み込まれることになります。ところで、いつ@data_path/tdiary.confを読んでいるんだろう?と疑問に思っていたのですが、カレントディレクトリのtdiary.confの最終行に、load_cgi_confという行があります。これにより、カレントディレクトリのtdiary.confを読むと同時に@data_path/tdiary.confも読むということを行ってるようです。†それではdo_eval_rhtmlメソッドに戻りましょう。do_eval_htmlメソッドはプラグインを読み込んだ後、HTMLを生成するrhtmlをERBで処理しています。).srcとしています。2回ERBにかけているのでしょう?サンプルとしてデフォルトで表示されるlatest.rhtmlを見てみましょう。本物とは若干違いますがわかりやすさのために加工しています。%>またeval_rhtmlメソッドが呼ばれています。メソッド名が同じなので混乱してしまったのですがどうやらこのeval_rhtmlはTDiary::DiaryBaseモジュールのeval_rhtmlのようです。DiaryBaseモジュールをincludeしているのは誰なのでしょうか?tdiary.rb内を探しても見つからないのでgrepしたところ、各記述スタイルを提供するファイルで定義されているXXXDiaryがincludeしているようです。いうことは上のdiary.eval_htmlメッセージはXXXDiaryオブジェクトが受け取りそうです。DiaryBase.eval_rhtmlメソッドを見てみましょう、すると、diary.rhtmlがERBにかけられていることがわかります。diary.rhtmlで気になる部分は以下のところです。%>to_htmlで日記本文が出力されそうです。body_enter_procとbody_leave_procはHOWTO-make-plugin.htmlに説明されているコールバック系プラグインですね。to_htmlと違って<%%=となっています。それでは<%%=の謎解きをしましょう。erb.rbを参照すると以下のように書かれています。%>となるようです(本文中にfooプラグインを呼び出していたとします)。何かからくりがわかってきた気がします。二回目のERBですがresultメソッドではなくsrcメソッドを呼び出しています。†さて、do_eval_rhtmlメソッドに戻ります。@pluginという処理をしています。Plugin.eval_srcでは(セキュリティのためのコードを除くと)単純に以下のことを行っています。)プラグインの読み込みの段階でPluginクラスにfooメソッドが定義されているため、evalによりfooメソッドの返値が日記に埋め込まれます。)endr.joinendと登録されているprocを順次呼び出すことで本文の前に出力したい文字列を出力しています。procの登録はadd_body_enter_procで行います。endというprocを追加しています。ところで、"日記本文"をevalしたらそんなメソッドなんてありませんとエラーになりますよね。そこら辺はmethod_missingの仕組みを使って回避しているようです。今回はtDiaryのソースをプラグインの実現という部分に注目して見てみました。プラグインを実現するための重要なポイントは次の2点です。%>とrhtmlに書き、ERBを1回目は普通に処理、2回目はタグを取り除いた形のソースを得てPluginオブジェクト内でeval率直な感想として、「たださんすげぇ!」の一言です。それではみなさんもよいコードリーディングを。