モダンPerlの世界へようこそ

第22回 Mojolicious::Lite:本当に簡単なウェブアプリがあればいいときは

この記事を読むのに必要な時間:およそ 5 分

あれから1年

Mojoについては2009年1月1日から4回にわたって特集記事を連載しました。ちょうど執筆を開始した直後に作者リーデル氏が不幸な医療事故にあい,一時はどうなることかと思いましたが,連載を終了する直前に開発続行の宣言が出て,ほっとしたのをよく覚えています。

あれから1年。Mojoを取り巻く環境はずいぶん変わりましたが,いま,Mojoはいったいどうなっているのでしょうか。今回は今年最後の記事として,Mojo界隈の近況をお届けすることにします。

大きく変わったといわれていますが……

昨年12月にバージョン0.9に到達したMojoは,途中事故の後遺症で開発が停滞した時期はあったものの,この1年でかれこれ30回以上のリリースが行われたことからもわかるように,いまもなお着実に開発が続けられています。この「ベータテスト」期間中にいくつか後方互換性が失われる変更があったため批判を浴びたこともありましたが,これはおもに実装が進んだことによるファイルの再配置や,不要と判断された機能の除去によるものなので,巷で言われているほど大きな変更だったわけではありません。実際にどのような変更があったのかについてはこれから説明しますが,少なくともCatalystMoose化されていく過程で起こった騒動に比べればよほど軽微なものですので,まずはご安心くださいと申し上げておきましょう※1)⁠

※1

実際,この記事の執筆にあわせて「まったく動かなくなっている」と批判されていた特集記事のコード(や,筆者が運用しているいくつかのMojoアプリケーションのコード)も最近の実装にあわせて書き直したのですが,ソースコードを読まなければ修正できないような箇所はほとんどありませんでした(もっとも,この原稿向けのサンプルを書いている最中に2ヶ月ほど前の機能強化で紛れ込んだバグを踏んでしまったせいで先々週は原稿を落としてしまったのですが)⁠

HTTPパイプラインの実装による修正点

Mojoが当初からインターネットの標準を強く意識して書かれてきたことは前回の特集記事でも触れた通りですが,Mojoが後方互換性を失ったひとつの原因は,これまで多くのPerl製HTTPサーバが見て見ぬ振りをしていたHTTPパイプラインの仕様を愚直に実装したからでした。この機能が実装されていると,コネクションをいくつも張り巡らせなくても,ひとつのコネクションのなかで複数のリクエストをサーバ側からのレスポンスを待つことなく連続して送信できるようになるのですが,当初はMojoもほかのPerl製HTTPサーバの実装と同じように一組のリクエスト/レスポンスにしか対応しておらず,パイプライン処理はあとからMojo::Pipelineという別の名前のモジュールを追加して行っていました。そのため,モジュール名が実態とずれてきたり,重複したコードが出たりと,全体的な見通しが悪くなってきたので,既存のMojo::TransactionはMojo::Transaction::Singleに,既存のMojo::PipelineはMojo::Transaction::Pipelineに移動したうえで,あらためてMojo::Transactionというクラスを共通のベースクラスにして関連性をより明確にした,というのがその後方互換性喪失(というか,過去の不完全な実装からの決別)の顛末です。

一般的なエンドユーザのレベルでは,テストなどでトランザクションをロードするときにuseするモジュールの名前が変わった(Mojo::Transactionではなく,ふつうはMojo::Transaction::Singleを呼ばなければならなくなった)ことと,従来テストに利用していたMojo::ClientのAPIががらっと変更になったこと,テスト用にはあらたにTest::Mojoというモジュールが用意されるようになったことを覚えておけば事足りる話ではありますが,フレームワークやサーバの実装を書いている方は,実際にHTTPパイプラインに対応するかどうかはさておき,Mojoがこのような道をたどった(そして気をつけないと自分も二の轍を踏むことになる)ということも覚えておいていただければと思います。

Mojolicious::Lite

Mojoが後方互換性を捨てたもうひとつの理由は,デフォルトのサンプルフレームワークがCatalystを強く意識していたMojoliciousから,よりシンプルな(RubyのSinatraによく似た)Mojolicious::Liteに変わったことから説明できます。

もともとリーデル氏はMojoをCatalystのエンジンに,という希望を持っていたため,当初はいささか冗長ながらも拡張性を重視した(Catalystの流儀にもあわせられるようにした)設計をしていましたが,受け入れられる芽がすっかりなくなってしまったのであれば,無闇に拡張性を持たせるよりは,健全なデフォルトを与えるほうがコードがきれいになりますし,初心者にもやさしくなります。そのため,たとえば当初はCatalystと同じように「my ($self, $c) = @_;」のような形でたらい回しにしていたコンテキストオブジェクトは(前回特集記事を連載していた時にもすでにその傾向が見えていたように)いまでは完全に(コントローラの)$selfのなかに統合されてしまいましたし(stashやreq,paramといったメソッドはコントローラから直接アクセスできるようになっています)⁠アトリビュートへのアクセサ/ミューテータも整理され,アトリビュート名とデフォルト値のみ書けばよいようになりました(遅延評価させたい場合はsub { ... } のなかに入れておきます)⁠

__PACKAGE__->attr(name => 'default value');
__PACKAGE__->attr(name => sub { Some::Module->new });

テンプレートの拡張子も当初の.phtmlから,.html.epliteなどを経て,いまは.html.epへと変更になっています。テンプレートそのものの表記はあまり変わっていませんが,従来の.eplテンプレートなどに比べると,.epテンプレートでは$selfなどをいちいち引き渡さなくてもよくなり,より簡潔に書けるようになったという違いがあります。

CGI.pmのようなMojolicious::Lite

このあたりの違いは言葉で説明するだけではわかりづらいでしょうから,実際にひとつサンプルアプリケーションをつくってみましょう。最新のMojoをインストールしたら,適当なディレクトリにnopasteというファイルをつくって,エディタでこのようなコードを書いてください。

#!/usr/bin/env perl

use Mojolicious::Lite;
use Mojo::Asset::File;
use Encode;

our $datadir = app->home->rel_dir('data');
mkdir $datadir unless -d $datadir;

app->log->level('error');
app->types->type(html => 'text/html; charset=utf-8');

get '/' => 'form';

post '/' => sub {
    my $self = shift;
    my $id   = time;
    my $file = Mojo::Asset::File->new(path => "$datadir/$id");
    $file->add_chunk(encode_utf8($self->param('code')));
    $self->redirect_to('page', id => $id);
};

get '/:id' => [ id => qr/\d+/ ] => sub {
    my $self = shift;
    my $id   = $self->stash('id');
    my $path = "$datadir/$id";
    return $self->render('not_found') unless -f $path;

    my $file = Mojo::Asset::File->new(path => $path);
    $self->stash(code => decode_utf8($file->slurp));
} => 'page';

shagadelic;

__DATA__

@@ form.html.ep
% layout 'default';
<form method="POST">
<textarea name="code"></textarea>
<input type="submit" value="post">
</form>

@@ page.html.ep
% layout 'default';
<div><pre>
<%= $self->stash('code') %>
</pre></div>

@@ not_found.html.ep
% layout 'default';
<h1>not found</h1>

@@ layouts/default.html.ep
<!doctype html><html>
<head><title>nopaste</title></head>
<body>
<%== content %>
<p><a href="<%= url_for 'form' %>">home</a></p>
</body>
</html>

書き終わったら(コピー&ペーストしたら)⁠コマンドラインから「perl nopaste daemon」とタイプして,スタンドアロンサーバを立ち上げてみましょう。ブラウザでhttp://localhost:3000/にアクセスして,適当なコードを投稿してみてください。専用のページに移動して,投稿したコードが表示されれば成功です。

※2

なお,お手元にApacheなどのCGI環境がある方は,途中の「shagadelic;」という行を「shagadelic('cgi');」のように変えると,CGI用のアプリケーションとしても利用できます。また,ここでは素のEncodeを利用していますが,Mojoの純粋主義者ならMojo::ByteStreamというモジュールを使って「b(...)->encode(app->renderer->encoding)->to_string」のような書き方をすることでしょう。

著者プロフィール

石垣憲一(いしがきけんいち)

あるときは翻訳家。あるときはPerlプログラマ。先日『カクテルホントのうんちく話』(柴田書店)を上梓。最新刊は『ガリア戦記』(平凡社ライブラリー)。

URLhttp://d.hatena.ne.jp/charsbar/

コメント

コメントの記入