CatyのWeb処理: Webフィーチャとメタプログラミング
Catyのドキュメンテーションに含めるつもりの記事。
CatyのモットーはできるだけWeb処理をやらないことだ。Webフレームワークとか言いながらも、一般的な言語処理の機能内でついでにWeb処理をしてしまう形態を理想としている。「Web特有の機能を付けない」「Web特有の処理をしない」と言ってもいい。Webに適合するための機能性は「アクション」という概念だけに閉じ込めている。で、以下、アクション=Web処理の話。
[追記 date="2012-10-01"]「フィーチャ」って言葉を、ここで定義した意味で使うのはちょっと問題アリかもしれない。本来の用語では「モデル」。モデルってのも意味が広すぎてイヤなんだけど、フィーチャは「システムにインストール済みの機能」という意味で使いたい時がある。[/追記]
アクションの遂行処理
アクションも単なるコマンドに過ぎない、と僕は強調している。今後さらにアクションの特殊性を薄めていくつもりだ。アクションの特徴は次の3つ。
- プロファイルが固定されている。
- リモート起動が出来る。
- 遂行(perform)出来る。
アクションのプロファイルは、arg0も含めてお約束で決められている。これによりすべてのアクションを一律に扱える。だが、この特徴には何らの特殊性もない。どんなコマンドでもプロファイルを持つ。そのプロファイルがたまたま制約されているだけだ。
リモート起動は、外部からのリクエストにより実行(exec)できることだ。これが出来なきゃWeb処理に使えない。ただし、リモート起動(remote invocation)と直接起動(direct invocation)の違いはまったくない。アクション自体は、自分がリモート起動されたか直接起動されたかを一切区別することができない。環境変数REQUEST_METHODなどがリモート起動時の情報を伝えるが、unclose構文で環境変数をセットできるので決め手にならない。これは、直接起動を使ってリモート起動を100%エミュレート出来ることを意味する。
遂行(perform)は、実行(exec)とは違う。その違いは次の絵で示せる。この絵全体でやっていることが「アクションの遂行」である。
遂行をCatyScriptのコードで書けば(catch {*=> ...} が今動かないが):
try --wall=superhard {
some-action
} |
catch {
normal => pass,
signal =>
try --wall=superhard {
map-signal
} |
catch {
normal => pass,
* => internal-server-error,
},
except =>
try --wall=superhard {
map-exception
} |
catch {
normal => pass,
* => internal-server-error,
},
}
単に実行するだけでなく、シグナルと例外を map-signal, map-exception に渡して面倒を見ることが遂行(perform)である。アクションを遂行するのはフレームワーク(Webシェル)の仕事だが、アクションを表現するクロージャ(closure-like object)を入力としてアクションを遂行するコマンド perform も提供される。また、ディスパッチャーコマンドとして lookup-and-perform ってのもある(単にexecするのは lookup-and-exec)。
Webフィーチャ
Webフィーチャとは、Web特有の機能性を実現するための、基本となる型とコマンドのセットである。
ちなみに、フィーチャとは、型、コマンド、ファシリティ、アセット(静的HTLM、画像、JavaScriptファイルなど)の一式をまとめた概念である。モジュールという単位でうまくマトマリがつかないときフィーチャという単位を使う。Webフィーチャも、必ずしもモジュールと1:1対応はしない。
[追記 date="2012-10-01"]「フィーチャ」の意味は上の説明のとおりだが、「Webフィーチャ」は不適切な気がしきた。冒頭の追記も参照。[/追記]
Webフィーチャの実体は2つの型と4つのコマンドである。フィーチャを構成する型/コマンドの名前とコマンドのプロファイルは完全に決まっている。
- 型 WebRaw
- 型 Response
- コマンド expand
- コマンド response
- コマンド map-signal
- コマンド map-exception
オプションと引数は省略して、入出力プロファイルだけを書くと:
- expand :: any -> WebRaw
- response :: WebRaw -> Response
- map-signal :: any -> Response
- map-exception :: Exception -> Response
anyとExceptionはCaty標準の型である。map-signal と map-exception は、先ほどのアクション遂行で出てきたアレである。歴史的な事情から、WebRawの別名としてWebInput、Responseの別名としてWebOutputが使われる。これらの型/コマンドの典型的な使用例は次のようになる。
上の絵の説明は節を改めてする。
Webフィーチャの使われ方
アクションは、その本質的な処理を行うセマンティック処理部と、Webへの出力の仲介をするWeb出力ラッパー(アダプター)の部分に別れる。Web出力ラッパーとして使ってよい基本コマンドは2つだけ、関係する型も2つである。
- 型 WebRaw : Webメッセージ(HTTPリクエストとレスポンス)のエンティティボディとなりうるデータの型
- 型 Response : エンティティボディをヘッダで包んだデータの型
- コマンド expand : テンプレートのパスを引数に取り、入力をWebRawデータに変換するコマンド
- コマンド response : エンティティボディからレスポンスを作り出すコマンド
頻繁に使われるprintコマンドは、expand | response と定義されるので基本コマンドではない。
アクション内で発生したシグナルと例外は、map-signal, map-exception に渡される。上の絵に描いてないが、Web出力ラッパー内の expand, responsも例外を引き起こすリスクがある。map-signal, map-exception の例外に関しては最終的にフレームワーク(Webシェル)が面倒を見る。
アクション内で発生するシグナル/例外を予測することはまったく不可能だが、Web(HTTP仕様)で定義されたリダイレクトとクライアントエラー/サーバーエラーに関しては、事前に定義しておくことができる。
exception HttpError = {
"status": integer(minimum=400, maximum=599),
*: any?
};type uri = common:uri;
@[register-public]
type HttpRedirect = @& {
"status": integer(minimum=300, maximum=399),
"location": uri,
*: any?
};
ステータス番号300番代のリダイレクトはシグナルで、ステータス番号400, 500番代のエラーは例外で知らせる。常識的に作られた map-signal, map-exception なら、これらのシグナル/例外を適切に処理する(HTTP仕様に従ったレスポンスを作る)だろう。
Webフィーチャの切り替え
デフォルトでは、Catyに組み込まれているWebフィーチャが使われる。これを標準Webフィーチャ(standard Web feature)と呼ぶ。標準以外のWebフィーチャは、非標準Webフィーチャ(non-standard Web feature)、または代替Webフィーチャ(alternative Web feature)と呼ばれる。
最近のCatyは、Webフィーチャを切り替える機能を持っている。これは、汎用のモジュールローディング機能を利用しているのだが、その汎用機能はWebフィーチャの切り替えを目的に開発されたものである。メカニズムはどうでもよくて(つうか、あまり詮索して欲しくない^^;)、重要なことは、前もって準備された複数のWebフィーチャを切り替えて動作させることが出来るってこと。
Webフィーチャの切り替え機能を使うと、標準ではないWebフィーチャにより、Webの偽装(faking)が出来るのだ。アクションのコードは一切変えない。アクションは本物のWeb上で動作していると思っている。本番稼働と何一つ変わらない挙動を示す。この状態で我々は、好きなだけシステムを監視/調査/記録/テストできる。ニセ物のWebなので、普通では起こらないような現象を人工的に作り出すこともできる。高負荷状態やクラッキングのシミュレーションも出来る。もう、やりたい放題だ。ムフフ。
「自分で自分を苛め抜く」という意味のマゾ・テストも実行できる。あー、あこがれのマゾ・テスト。あー、虐めたい/虐められたーい。
代替Webフィーチャにより非標準のWebを作り出すにしても、システム自身のメタ情報(主に設計情報)にアクセスできないと全自動テストはできない。が、CatyのレイフィケーションAPIはかなり揃ってきたし、今後さらに精密なレイフィケーションが出来るようになる。メタプログラミングを駆使して代替Webフィーチャを作ることにより、今までとは次元が違うテストができだろうと期待している。
最後に一言注意しておくと; フィーチャを切り替えたり、コードトランスフォーム/コードシンセサイズを使ってテストをすることは、何もアクションに限らない。一般のコマンドセットに対しても同様の手法を使うことができる。関連するコマンド達を一連の“シナリオ”に沿って網羅的に実行する。全自動、無人、長時間の実行をする。