3月26日のOSC2005に行ってきて、Ruby on Rails(以下 RoR) な話をチラリと聞いてきました。実際にその場でアプリケーションを作るというまな板プレゼンと「高橋メソッド」で RoR が生まれた背景や考え方を紹介という内容。とても面白かったです。
Perl の世界でも Maypole をベースにして開発され最近(多分RoRを意識して) 開発が進められている Catalyst という MVC フレームワークがあります。 helper スクリプト、開発用の小さなhttpd等 RoR そっくりです。
今回は Catalyst がどんなものかと、例の 「TinyURL を実装」という形で基本的な部分をご紹介します。 ちなみに、以下のコードは1時間では作成できず、YAPC::Taipei を眺めながら 3時間くらいかかってしまったです。。 でも、 Catalyst の使い方を覚えるのを含めての時間なので大目に見てくださいね。
追記 2005年4月6日
Bundle::Catalyst::Everything をしないと例にしているスクリプトは動作しないそうです。
(んと、また多分後で追記します。)
何は無くとも Catalyst モジュールのインストール。現時点では 4.34 というバージョンになります。
% sudo perl -MCPAN -e 'install Bundle::Catalyst' % sudo perl -MCPAN -e 'install Bundle::Catalyst::Everything'
以下説明は 4.34 の場合です。今後は手順等変更になる可能性があります。
それでは Catalyst でWebアプリケーションを作ってみましょう。 最初の一歩は catalyst.pl コマンドです。 "catalyst.pl アプリケーション名" で新しいセットを作成します。 プロンプトには自動生成したディレクトリやファイル名が出力されます。
% catalyst Peturl created "Peturl" created "Peturl/script" created "Peturl/lib" created "Peturl/root" created "Peturl/t" created "Peturl/t/m" created "Peturl/t/v" created "Peturl/t/c" created "Peturl/lib/Peturl" created "Peturl/lib/Peturl/M" created "Peturl/lib/Peturl/V" created "Peturl/lib/Peturl/C" created "Peturl/lib/Peturl.pm" created "Peturl/Makefile.PL" created "Peturl/README" created "Peturl/Changes" created "Peturl/t/01app.t" created "Peturl/t/02podcoverage.t" created "Peturl/script/cgi.pl" created "Peturl/script/nph-cgi.pl" created "Peturl/script/fcgi.pl" created "Peturl/script/server.pl" created "Peturl/script/test.pl" created "Peturl/script/create.pl"
script/create.pl view MyViewer でViewの部分の実装部分のスケルトンが作成できますが script/create.pl view MyViewer TT とすることで Template-Toolkit をベースとした lib/Peturl/V/MyViewer.pm を作成できます。
% cd Peturl % script/create.pl view TT TT created "/home/sekimura/tmp/Peturl/script/../lib/Peturl/V/TT.pm" created "/home/sekimura/tmp/Peturl/script/../t/v/tt.t"
データベースを作成しテーブルを作成しておきます。
% cat setup.sql
CREATE TABLE tinyurl (
id TEXT PRIMARY KEY,
longurl TEXT
);
% sqlite /tmp/tinyurl.db < setup.sql
model についても Class::DBI をベースにするには % script/create.pl model DataSource CDBI dbi:SQLite:/tmp/data.db のように作成します。 DSN を指定して作成すると、データベースからテーブル名を取り出して lib/Peturl/M/CDBI/テーブル名.pm というファイルを自動生成してくれます。
% script/create.pl model CDBI CDBI dbi:SQLite:/tmp/tinyurl.db created "/home/sekimura/tmp/Peturl/script/../lib/Peturl/M/CDBI.pm created "/home/sekimura/tmp/Peturl/script/../lib/Peturl/M/CDBI/Tinyurl.pm created "/home/sekimura/tmp/Peturl/script/../t/m/cdbi.t created "/home/sekimura/tmp/Peturl/script/../t/m/cdbi_tinyurl.t
次にコントローラの部分を実装します。 TinyURLのロジックをいまさら確認するとこんな感じ。
- http://SERVERNAME/ にアクセスすると TinyURL 生成用のフォームが表示
- http://SERVERNAME/create に longurl というデータをPOSTすると TinyURLを生成して表示。もし longurl がシステムに登録されていたら そのTinyURLを retrieve して表示
- http://SERVERNAME/ue035 とかの生成されたTinyURLでアクセスすると longurl へ redirect
まず、生成用フォームを出力する部分を実装する。 lib/Peturl.pm がデフォルトのコントローラになるのでこのファイルにある !default アクションを以下に変更します。
Peturl->action(
'!default' => sub {
my ( $self, $c ) = @_;
my $key = $c->req->path;
$key =~ s/^\///;
if ($key and $key ne 'create') {
my $t = Peturl::M::CDBI::Tinyurl->retrieve($key) or die $!;
$c->res->redirect($t->longurl);
}
$c->stash->{template} = 'index.tt';
$c->forward('Peturl::V::TT');
},
# XXX: 後述の create アクションの実装はここに挿入される
);
PATH_INFO の値が "/" か "/create" 以外の場合は
たぶん "http://SERVERNAME/ue035" とかのTinyURL だろう
ということで Peturl::M::CDBI::TinyURL から "ue035" 等の値を
持つオブジェクトを取得する。
そのオブジェクトの longurl 属性をURLとして HTTP redirect レスポンスを返す。
PATH_INFO の値が "/" か "/create" の場合は
index.tt のテンプレートを使って Peturl::V::TT に処理を
フォワードし 入力フォームView を生成(レンダリング)してもらう。
index.tt は root/index.tt に以下のように Template-Toolkit の書き方で作成します。
[% IF tinyurl %] <p> LongURL: [% tinyurl.longurl %]<br /> TinyURL: <a href="http://sakura.qootas.org:3000/[% tinyurl.id %]"> http://sakura.qootas.org:3000/[% tinyurl.id %]</a> </p> <p> next? </p> [% END %] <form action="create" method="post"> URL: <input name="longurl" size="30" type="text"> <input name="submit" value="Make TinyURL!" type="submit"> </form>
次に create でTinyURLを保存するところを実装します。 先ほどのコードの XXX の部分に以下のコードを追加します。
'create' => sub {
my ( $self, $c ) = @_;
my $longurl = $c->req->params->{longurl};
$longurl = 'http://' . $longurl unless $longurl =~ /^http(?s):\/\//i;
my $tinyurl = Peturl::M::CDBI::Tinyurl->search_or_generate($longurl);
$c->stash->{tinyurl} = $tinyurl;
$c->stash->{template} = 'index.tt';
$c->forward('Peturl::V::TT');
},
リクエストの longurl パラメータ値が http:// か https:// で始まって いなかったらhttp:// を追加して search_or_generate(後述) でオブジェクトを取得。 stash に tinyurl オブジェクトを入れておいて TT を使って表示させます。
次に search_or_generate の実装。 自動生成された lib/Peturl/M/CDBI/Tinyurl.pm を編集して、 以下を追加します。 search とか create とかはご存知 Class::DBI のメソッドです。
sub search_or_generate {
my ($self, $longurl) = @_;
if (my ($tinyurl) = $self->search(longurl => $longurl)) {
return $tinyurl;
} else {
my $random = _get_random(5, NULL);
return $self->create({longurl => $longurl, id => $random});
}
}
sub _get_random {
my $size = shift || 5;
my $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
. 'abcdefghijklmnopqrstuvwxyz'
. '0123456789';
my $random;
for (1..$size) {
$random .= substr($chars, rand(length($chars)), 1, NULL);
}
return $random;
}
以上、終了。 script/server.pl を実行すると http://localhost:3000/ で作成したWebアプリケーションにアクセスできます。
かなり大雑把に紹介していますが Class::DBI、Template-Toolkit 使いの人であれば rapid に開発できるのは確かです。 私自身まだ使い込んでいないですが、これからも Catalyst に注目していこうかと思います。
質問などありましたらコメントなどにご記入ください。