アプリケーションの枠組みを越えた再利用
前回は,Catalyst 5.7で登場したチェーンドアクションを利用して適切なベースコントローラをつくれば,CRUDのような定型処理は再利用できるようになる,という話をしました。これはコンテントマネジメントシステム(CMS)のように同じようなインタフェースを持つ画面が多いシステムをつくるときには特に効果的なのですが,その再利用を,ひとつのアプリケーション内だけで完結させてしまうのはもったいない話。自社でつくるアプリケーションにはどんどん使い回していきたいものですし,コピー&ペーストを避けるためには,なんらかの名前空間上にその共通コードをまとめていく必要があります。
もちろんそのコードが小さく,十分に一般化できるものなら,Catalyst,あるいはCatalystXという名前空間に入れてもかまいませんが,コントローラの部品だけでなく,モデルやビューまで巻き込んで独自路線を突き進むのであれば,別の名前空間を用意したほうが賢明です。
今回はそのような例のひとつとして,mstことマット・トラウト(Matt S. Trout)氏を中心に開発が続けられているReactionというベタープラクティス集を取り上げてみます。
Reactionは,当時のデファクトスタンダードであったCatalyst+DBIx::Class+Template::Toolkitという組み合わせにおそらくはじめてMooseを持ち込んだ事例であり(CPANに最初のプレビュー版がリリースされた2006年5月当時,Mooseはまだ誕生から2ヶ月しかたっていませんでした),そのコードや考え方は,のちのCatalystの世界に大きな影響を与えてきました。
Reactionはかなり大きなコードなので全貌を把握するのは大変ですが,ここでは付属のテストアプリケーションやチュートリアルを参考に,大事なポイントをいくつか紹介していきます。
環境を構築する
ReactionはCPANに上がっていますので,単に使いたいだけであれば,いつもの手順でCPANからインストールできます(※1)。
ただし,今回はReactionに付属しているテストファイルを利用したいので,CPANからtarballをダウンロードして適当なディレクトリに解凍することにしましょう(このような場合は,CPANシェルを起動して「look Reaction」を実行するか,CPANPLUSを使っているのであればシェル(コマンドライン)から直接「cpanp -z Reaction」するのが手軽です。もちろんhttp://code2.0beta.co.uk/reaction/svn/以下のリポジトリから最新版をチェックアウト/エクスポートしてもかまいません)。
tarballの解凍が済んだら,おなじみの「perl Makefile.PL && make test」を実行してください(これでテストとともにサンプルアプリケーションの環境設定が行われます)。それが済んだら,下記のコマンドを実行してサーバを起動しましょう。環境によってはいくつか警告が表示されますが,深刻なエラー以外はひとまず無視していただいて結構です。ブラウザで http://localhost:3000/ を開くと,Reactionベースのアプリケーションを試すことができます。
> perl script/componentui_server.pl
- ※1
残念ながら最新版はいまのところ依存モジュールの都合でWindows環境では正しくインストールできません。
また,過去に初期のバージョンをインストールしたことがある方は,内部構造が大きく変わっているので一度古いReactionを削除する必要があるかもしれません。ReactionやReactionが利用しているファイルがどこにインストールされているかは,シェルから下記のコマンドを実行すれば調べられます。
> perldoc -l Reaction > perl -MFile::ShareDir=dist_dir -e 'print dist_dir "Reaction"'
ルートコントローラをのぞいてみよう
動作を確認したところで,実際のコードを見てみましょう。まずはエディタでlib/ComponentUI/Controller/Root.pmを開いてみてください。見慣れないもの,見かけないもの,それぞれにありますが,順不同で説明していきます。
まずは,前回やり方を紹介したように,ここでもすべてのチェーンの起点となるアクションが用意されています(Reactionではbaseという名前が使われています)。push_viewportというのは見慣れませんが,いまはアクションごとにテンプレートの部品を順次登録しているものと思っておいてください(baseアクションではサイトのレイアウトの大枠を,rootアクションではindexというより具体的な部品を登録しています)。
sub base :Chained('/') :PathPart('') :CaptureArgs(0) {
my ($self, $c) = @_;
$self->push_viewport(SiteLayout,
title => 'ComponentUI test title',
static_base_uri => "${\$c->uri_for('/static')}",
meta_info => {
http_header => {
'Content-Type' => 'text/html;charset=utf-8',
},
},
);
}
sub root :Chained('base') :PathPart('') :Args(0) {
my ($self, $c) = @_;
$self->push_viewport(ViewPort, layout => 'index');
}
push_viewportのなかではSiteLayoutやViewPortという裸のワードが使われていますが,これを実現しているのがaliasedというプラグマ。細かな使い方はPODを見ていただくとして,長い,決まり切ったパッケージ名が頻出する場合はこのプラグマを使うと短くすっきり書けるようになります。
use aliased 'Reaction::UI::ViewPort';
use aliased 'Reaction::UI::ViewPort::SiteLayout';
また,コードは転載しませんが,このルートコントローラでは静的ファイルやエラーの処理も行っています(エラー処理についてはベースクラスの方にもあるので省略可能です)。よく見かけるRenderViewを使ったendアクションの姿もありませんが,これはベースコントローラのほうで似たような処理を行っているためです(ルートのベースコントローラに登録されているbeginでビュー(ビューポート)まわりの基本設定を行い,そこから各コントローラのチェーンドアクションなどでURLごとの処理をし,ふたたびルートのendで登録済みのビューポートを合成する,というのがReactionの基本的な流れになります)。

