Mooseを使用したCatalystの実装
Catalystなどでクラスを作っていくと、その継承とは関係のない機能を追加する必要が出てくる場合があります。例えばログ出力や特定の値チェック機能などを追加するために、無理矢理共通の基底クラスに押し込んだり、似た処理をコピーペーストするなどの泥縄的な対応になることもあるかと思います。
このような場合には、Moose::Roleを使用することで、継承関係を越えた機能をPerlのオブジェクトシステムに対して追加することができるようになります。ここでは、Moose::Roleを利用したCatalyst::Controller::ActionRoleについて簡単に紹介します。
ActionRoleコントローラで実現できることは、ある機能を特定のアクションに割り当てることです。例えば、Userコントローラにはユーザーの一覧表示、登録、変更、削除を行うアクションが定義されているとします。この中でユーザーの登録、変更だけでEmailアドレスのチェックを行いたい場合には、次のようなモジュールを定義します。
package MyApp::ActionRole::EmailValidate;
use Moose::Role;
before execute => sub {
my ($self, $controller, $c) = @_;
my $email = $c->req->params->{email};
# Emailアドレスのチェック
};
1;
beforeメソッド修飾子を指定することで、アクションのexecuteメソッドが呼び出される前に、指定された無名関数を実行します。
このRoleを呼び出す場合には、コントローラのアクション定義でDoesアトリビュートの引数としてEmailValidateを指定します。
package MyApp::Controller::User;
use strict;
use warnings;
use parent 'Catalyst::Controller::ActionRole';
# 省略
# ユーザの一覧表示
sub index :Path :Args(0) {...}
# ユーザの登録フォーム表示
sub add :Local {...}
# ユーザの登録実行
sub add_submit :Local :Does('EmailValidate') {...}
# ユーザ情報の変更フォーム表示
sub edit :Local {...}
# ユーザ情報の変更実行
sub edit_submit :Local :Does('EmailValidate') {...}
# 省略
まず、Roleを呼び出せるようにするために、Catalyst::Controller::ActionRoleから継承しています。そして、:Doesアトリビュートで、add_submit、edit_submitアクションに対してEmailValidateを割り当てています。
beginやautoメソッドにチェックコードを記述した場合には、すべてのアクションに対して有効になってしまいますが、Moose::Roleを使用することで、特定のアクションだけに機能を追加することができるようになります。
上記の例では:Doesアトリビュートにはパッケージ名を省略した名前を指定しています。この場合には、”<MyApp>::ActionRole::”または”Catalyst::ActionRole::”がプレフィックスとしてつけられたモジュールを検索しに行きます。
もし”MyApp::Foo::Bar”というモジュールをDoesアトリビュートに指定するには、「:Does('+MyApp::Foo::Bar')」のように、先頭に”+”をつけてパッケージ名を含むモジュール名を記述する方法と、ActionRoleコントローラ側でプレフィックスを_action_role_prefixに登録する方法があります。
package MyApp::Controller::User;
use strict;
use warnings;
use parent 'Catalyst::Controller::ActionRole';
__PACKAGE__->_action_role_prefix([ 'MyApp::Foo::' ]);
# 省略
sub add_submit :Local :Does('Bar') {...}
このようにCatalystではMooseを使用することで、実現したいコードを簡単に実装できるようになりました。
Catalystにおけるモデル
Catalystでは、モデルを実現するためのコンポーネントとして、Catalyst::Modelが用意されています。このCatalyst::Modelを継承したさまざまなモジュールがCPANに登録されており、データベースやRSSなどのフィード、検索エンジンなどへのアクセスをより便利にするための機能が提供されています。
モデルの種類
主なモデルとして、次のようなものがCPANに登録されています。
| モデルモジュール | 説明 |
| Catalyst::Model::DBIC::Schema | DBIx::Class::Schemaを利用してデータベースへのアクセスを行う |
| Catalyst::Model::CDBI | Class::DBIを利用してデータベースへのアクセスを行う |
| Catalyst::Model::WebService::CRUST | WebService::CRUSTを利用してREST呼び出しを行う |
| Catalyst::Model::Adaptor | 通常のPerlオブジェクトをCatalyst::Modelとして扱えるようにする |
| Catalyst::Model::LDAP | Net::LDAPを利用してLDAPへのアクセスを行う |
| Catalyst::Model::File | ファイルベースのストレージへのアクセスを行う |
Webアプリケーションでデータを永続化する場合にはデータベースを使用しますが、現在多くのデータベースアクセスで利用されているモデルモジュールは、Catalyst::Model::DBIC::Schemaです。
このモジュールを使用する例は、次回で紹介する予定です。
最近のCatalystのモデル
一般的なMVCにおけるモデルとは、アプリケーションが扱うデータと、そのデータに対する手続き(ビジネスロジック)を担当するコンポーネントになります。これまでのCatalystでは、ビジネスロジックをコントローラに実装していた方もいらっしゃると思いますが、本来のあり方としてはコントローラはディスパッチに限定し、モデル側にビジネスロジックを実装するべきです。
例えば、ある機能をWebベースのUIで提供する場合と、同時にWebAPIで提供する場合を考えれば同じ処理をコントローラに実装するよりも、モデル側に実装した方がメンテナンスなどの点からメリットがあるのは明らかです。さらに、最近のCatalystでは、データアクセスとビジネスロジックをCatalystから分離してしまうやり方も推奨されています。このやり方では、単体テストやコマンドラインベースの管理ツールなど、Catalystを通さずにロジックを使用できるため、コードの再利用性を高めることができます。
このようなデータアクセスとビジネスロジックを実装した通常のPerlオブジェクト(POPO:Plain Old Perl Object)を、Catalyst::Modelとしてアクセスできるようにするには、Catalyst::Model::Adaptorを使用します。このモジュールの使い方についても次回で紹介する予定です。
まとめ
本記事では、blessとMooseを使用した場合のPerlのオブジェクト指向プログラミングについて、サンプルを交えて紹介しました。また、Mooseを使うことでCatalystの実装方法がどのように変わるか、そしてCatalystのモデルについても概要を紹介しました。
次回では、Catalystのモデルプログラミングについて、サンプルを交えつつ説明していく予定です。
参考資料
- 『Catalyst-Manual-5.8000』
- 『Catalyst::DispatchType::Chained』
- 『モダンPerl入門』 牧大輔 著、翔泳社、2009年2月
