Unixでスライドショー: Continuationスタイルウェブサーバ編

通常のアプリケーションを使わないで画像処理をする偏屈シリーズ、第4弾。こんどはContinuityモジュールによるperlの継続スタイルウェブサーバでやってみる。今回は画像を見るだけじゃなくて左右に回す編集機能付きだ。

このツールはデスクトップユーザ権限でコマンドを走らせて画像ファイル名のストリームを読み込み、ブラウザから操作する。ウェブアプリケーションというよりは、コマンドラインツールのUIにブラウザを使ったようなものだ。

こんな使い方:
find DCIM/ -type f | ./pic-viewer.pl

良きUnixツールのように処理する画像ファイル名を標準入力から読み取るんで、ファイルの選択が自由自在。こんなことだって出来て楽しいと思う。
locate jpg | grep '\.jpg$' | ./pic-viewer.pl

Mooseを使って画像倉庫を管理するクラスを作ったらメインのスクリプトは1ページ半に収まった。前からこのようなツールを作ろうとして挫折したが、Continuityを使ったら簡単に書けてしまった。また、セッションの流れも追いやすい。継続ベースのウェブプログラミングの楽しさがちょっと分った気がする。


#!/usr/local/bin/perl -w
use strict;
use Carp;
use Data::Dumper;
# これがperlでcontinuationスタイルのサーバを可能にする魔法のおまじない
use Continuity;
# このプログラムのための画像ファイルのパスを管理するモジュール
use Tengu::Img;

# 画像処理の定義。
my %action=(
rotate_90=>"gm convert -rotate 90 %s %s",
rotate_270=>"gm convert -rotate 270 %s %s",
rotate_180=>"gm convert -rotate 180 %s %s",
);

# 画像倉庫。諸々のサイズとか編集履歴を置いておくディレクトリ階層を管理するもの。
my $repo=Repo->new(dir=>'pv.d', var_def=>{md=>700, tn=>200})->setup();

# サーバの初期化と稼働。
my $server = new Continuity(
port=>3000,
# 静的なファイルはパターンを指定すると勝手に送り返してくれる。
# 大文字のJPG対応のためにオーバーライドしている。
# 詳しくはContinuationのマニュアルを。
staticp=>sub { $_[0]->url =~m/\.(jpg|jpeg|gif|png|css|ico|js)$/i },
);

# ブラウザをサバーに向ける。コマンド打つだけで勝手にブラウザがページを開いてくれる。
# xx 競合状態の恐れあり。
`firefox -a firefox -remote "openurl(http://localhost:3000/)"`;

# サーバの仕事場。
$server->loop;

# セッションの実装。
# ハンドラによって断片化されたロジックでなく、ユーザとのインタラクションが
# 一つの関数の中のブロックと対応している。セッションの流れが一目瞭然。
sub main
{
# この関数はリクエストがあるとサーバから呼ばれる。
my ($request)=@_;
# サーバを通してクライアントと対話していると同時に
# 標準入力から吸い込んだファイルを処理していく。
# インタラクティブバッチ処理を両方こなす。
while(<STDIN>) {
chomp;
my $file=$_;

# オリジナルから画像庫のエントリーを作る。
my $img=Img->new(repo=>$repo, original=>$file);
$img->setup();
# 対話は画像処理画面を描くことから始まる。
$request->print(img_edit($img));
# 編集ループ。Nextが押されるまで回る。
while(1) {
# ユーザの入力を待ちブロック。
$request->next;
my %qp=$request->param;
my $op=$qp{op} or die "need op";
# ループからの脱出コマンドの処理。
last if $op eq 'next';
exit(0) if $op eq 'quit';
# 動画処理コマンド。
my $action=$action{$op} or die "bogus op: $op";
$img->edit($action);
# 再度、編集画面を送る。
$request->print(img_edit($img));
}
}
# 編集ループから落ちて出た。ってことは標準入力が終ったか
# ユーザが終了コマンドを選択したか。
$request->print('<html><body><h2>All Done!</h2>'
.'<a href=".?op=quit">bye</a>');
# ここでブラウザのタブが閉じれるといい、、
$request->next;
exit(0);
}

# 簡単な画像処理画面。
sub img_edit {
my ($img)=@_;

my $md=$img->md;
my $html=<<__END__;
<html>
<body>
<center>
<div style="float: right;">
<img src="$md">
</div>

<div>
<a href=".?op=rotate_270"><img src="img/rotate_270.png"></a>
<a href=".?op=rotate_180"><img src="img/rotate_180.png"></a>
<a href=".?op=rotate_90"><img src="img/rotate_90.png"></a>
</div>
<div>
<a href=".?op=next"><img src="img/next.png"></a>
</div>

__END__
}