Catalystのデータクラスについて
Catalyst::Base, Class::DBI ↑ Catalyst::Model::CDBI ↑ MyApp::M::CDBI
あるとき僕はこのデータクラスをコマンドラインで実行するスクリプトから読み込んで使おうとしたときに困ってしまいました。なぜなら、CatalystはWebで使うのを前提に設計されていて、コマンドラインからの実装は想定されていないさそうだからです。
生成されたMyApp::M::CDBIに下記のようなコードを付け加えて、その下にあるスクリプトのようにやればCatalyst関係のモジュールを使用しないで読み込めるので、むりくり実行させられなくもないのですが。。。
sub new_from_command { my $class = shift; my $self = bless $class->_config, $class; $self->{namespace} ||= ref $self; $self->{additional_base_classes} ||= (); push @{ $self->{additional_base_classes} }, ref $self; eval { $self->loader( Class::DBI::Loader->new(%$self) ) }; for my $class ( $self->loader->classes ) { $class->autoupdate(1); no strict 'refs'; *{"$class\::new"} = sub { bless {%$self}, $class }; } return $self; }
my $cdbi = MyApp::M::CDBI->new_from_command; my $film = $cdbi->loader->find_class('film'); my @rec = $film->search(foo => 'bar');
この解決法として1つ思いついたのは、データクラスの親クラスからCatalyst::Baseを除くということです。これでCatalystとの依存がなくなるしすっきりだ!とか思ったのですが、
他のCatalystユーザは、データクラスをコマンドラインから使ったりしないのだろうか…。たぶんそんな使い方している人はあまりいないんだろうなぁ…。
そんなことを考えてるときに、ふとこんなモジュールはどうだろうと思って、Catalyst-Helper-Model-CDBI-AnyDBD-0.01.tar.gzというモジュールを作ってみました。これは上で書いたことを解決するためのモジュールではないのですが…。自分はClass::DBI::mysqlなどのモジュールを使うのに慣れているせいか、Class::DBI::Loader使うのはなんかなぁと思ったのでこのモジュールを作ってみました。
これはClass::DBI::Loaderを使わずに、Class::DBI::mysqlやClass::DBI::Pgなどを使用するデータクラスを生成するためのヘルパーです。生成されるデータクラスの構造は下記のようになります。
package MyApp::M::CDBI; use base qw(Class::DBI::mysql Catalyst::Base); __PACKAGE__->connect('dbi:mysql:db', 'user', 'password'); package MyApp::M::CDBI::Film; use base qw(MyApp::M::CDBI); __PACKAGE__->set_up_table('film');
でも結局Catalyst::Baseが親クラスなんですけどね…。
2005-11-15 03:24追記:
※1で書いたんですが、MyApp::M::CDBIは必ずしも親クラスがCatalyst::Baseではなくてもよいようです。Catalyst::Baseを親クラスにする必要があるとしたら、config()、process()などのクラスメソッドを使う時か、controllerから$c->forward()などで呼び出したい時かなぁと思います。自分はあまりそのようなことを行なわないのですが、そのようなニーズってあるのでしょうか。あまり多そうでなければ、上で書いたモジュールで生成されるMyApp::M::CDBIの親クラスからCatalyst::Baseをはずしたものを作成して、CatalystのMLに投げてみようかと思います。