[Catalyst] Catalyst::Model::Adaptorを使ってみた
Catalyst::Model::Adaptor - use a plain class as a Catalyst model
要はどんなクラスでもCatalystのモデルにしてくれるクラスです。
たとえばこんなクラスを作って
lib/MyApp/DateTime.pm
package MyApp::DateTime;
use strict;
use warnings;
use base qw/DateTime/;
use DateTime::Format::W3CDTF;
sub now {
my ($class, $args) = @_;
$args->{time_zone} = 'local' unless $args->{time_zone};
return $class->SUPER::now(%$args);
}
sub parse {
my ( $class, $str ) = @_;
my $dt = DateTime::Format::W3CDTF->parse_datetime($str);
bless $dt, $class;
}
sub format {
my $self = shift;
return DateTime::Format::W3CDTF->format_datetime($self);
}
で、ヘルパーを使うと
script/myapp_create.pl model DateTime Adaptor MyApp::DateTime now
こんなクラスを作ってくれます。
lib/MyApp/Model/DateTime.pm
MyApp::Model::DateTime
package Test::Model::DateTime;
use strict;
use warnings;
use base 'Catalyst::Model::Adaptor';
__PACKAGE__->config(
class => 'Test::DateTime',
constructor => 'now',
);
1;
ヘルパーの使い方は
script/myapp_create.pl model [Modelクラスの名前] Adaptor [使うクラス名] [コンストラクタ名]
みたいな感じ
で、コントローラにこんな感じのを書いて
lib/MyApp/Controller/Root.pm
-snip-
sub now : Local {
my ($self, $c) = @_;
my $model = $c->model('DateTime');
$c->res->body($model->format);
}
-snip-
サーバーをあげて/nowにアクセスすると現在の時間をW3CDTFな書式で表示してくれます。
便利!
・・・ですがリロードしても時間が変わりません。
で、M::DateTimeを変更します。
MyApp::Model::DateTime - snip - use base 'Catalyst::Model::Factory'; - snip -
で、あげなおしてリロードしたらちゃんと現在の日時を返してくれます。
Catalyst::Model::Adaptorのパッケージには
Catalyst::Model::Adaptor - アプリケーションの起動時にインスタンスを生成
Catalyst::Model::Factory::PerRequest - リクエストごとにインスタンスを生成
Catalyst::Model::Factory - $c->model()で呼ばれるたびにインスタンスを生成
が入ってるのでそれぞれの用途で使い分けるといいです。
また、コンストラクタの引数を設定ファイルに指定する場合は
---
name: Test
Model::DateTime:
args:
time_zone: UTC
な感じでargs:の下に書くとよろし。
ただし、注意しなければいけないのはC::M::Adaptorからクラスのコンストラクタに渡る引数はハッシュリファレンスになります。今回はクラスのほうでDateTimeクラスに渡すときに変換していますが、C::M::Adaptorのprepare_argumentsとmangle_argumentsをオーバーライドすることで行うこともできます。
MyApp::DateTimeからnow()を削って、MyApp::Model::DateTimeに
sub prepare_arguments {
my ( $self, $app ) = @_;
my $args = $self->{args};
$args->{time_zone} = 'local' unless $args->{time_zone};
return $args;
}
sub mangle_arguments {
my ( $self, $args ) = @_;
return %$args;
}
を追加する感じ。
ドキュメントにありますが要は内部的に
my $args = $self->prepare_arguments($app); $adapted_class->$constructor($self->mangle_args($args));
みたいな処理をしてアプリケーションからクラスのコンストラクタに渡してるのでそれぞれフックしてやる感じです。
