このブログは、旧・はてなダイアリー「檜山正幸のキマイラ飼育記 メモ編」(http://d.hatena.ne.jp/m-hiyama-memo/)のデータを移行・保存したものであり、今後(2019年1月以降)更新の予定はありません。

今後の更新は、新しいブログ http://m-hiyama-memo.hatenablog.com/ で行います。

ニセWebの作り方

Catyのドキュメンテーションに含めるつもりの記事(の下書き)。

CatyのWeb処理: Webフィーチャとメタプログラミング」の続き。

Catyにとって、マゾ・テストは大きな目標だった*1。マゾ・テストを想定しないと、Catyの設計判断は理解できないだろう。例えば、goto制御の導入; マゾ・テストは無限走行(実際は長時間走行)する。そのためには、どうしてもgoto制御が必要だった。なんと言われようとgotoなのだ。今、Catyには「おとなしいgoto」としてforwardとrepeatが入っている。

もうひとつ、マゾ・テストに必要なことは、現実世界の内部模型化だ。具体的には、インターネットをまったく使わないでWebアプリケーションを実行すること。localhost(ループバックインターフェイス)さえ使わない。ネットワーク機能とはまったく無縁に、Catyの内部世界機構だけでWebアプリケーションを動かす。

Catyの内部世界にニセのWebを作り出すことになる。スノーグローブのWeb、箱庭のWeb、ミニチュアのWeb、内部Web、ジオラマのWebとか … 色々な呼び方をしてきたが、ともかくも“本物ではないWeb”だ。

ニセWebには3つの特徴がある。

  1. ネットワーク機能を一切使わない
  2. サーバーサイドプログラムには、本物との区別が付かない
  3. Webクライアントを含む

「本物との区別が付かない」ことは極めて重要である。本物との差があれば、「ニセWebは所詮ニセ物だから、役に立たない」となってしまう。マゾ・テストは無人全自動で行うが、本物のWeb上で人間が手動で行うテストと同じ効果がなくてはならない。

アクションの構造:Webラッパー部

Webアプリケーションの実体は、アクションの集合体である。アクションは、概念的に2つの部分に別れる。

  1. 本来のセマンティックな処理を行う部分
  2. Webとの入出力の面倒をみる部分

「Webとの入出力」で、特にWebへの出力のほうに注目する(Webからの入力(リクエスト)の偽装(faking)は簡単なのでたいした問題ではない)。「本来のセマンティックな処理」の後で行うことを、さらに次の二種に分ける。

  1. 装飾(decoration)処理 : 出力コンテンツを、人やクライアント側アプリケーションが見やすい形に飾ったり整えたり。
  2. 転送(transfer)処理 : クライアントまでデータを送り届ける。

場合により、装飾処理は省略されることがある(JSONを使ったWebAPIのときなど)。一方、転送処理は必ず行われる。そうでないとクライアント/サーバー型システムにならない。

注意:現状のexpandコマンドをrenderコマンドにリネーム予定。renderを使って書く。renderの日本語は「描出」かな。

装飾処理と転送処理には、それぞれ次のコマンドが使われる。

  1. render : テンプレートパスを引数にして、そのテンプレートによりデータを装飾する。
  2. responce : 入力データにHTTPヘッダを付けてレスポンスデータを作る。

ここで、responseは、単に「レスポンスデータを作る」だけでなく、そのデータをクライアントまで届ける機能も内在していると考える。

responseコマンドの置き換え

responseを、「データをクライアントまで届ける機能を内在している」と考えると、アクションの終りでは必ずresponseを呼ばなくてはならないことになる。関数の終りにreturnが必要なのと同じだ。アクション(の実装コード)がresponseで終わっているかどうか? はフロー解析で分かるので、そうなってないときはエラーにできる。つまり、アクションは必ずresponseで終わっていると仮定してよい。(例外/シグナルは許容する。)

実際のWebサーバー環境下でアクションが動くとき、responseは実際に「データをクライアントまで届ける」ように機能する。メカニズム的には、アクションの外側のWebサーバー/Webアプリケーションコンテナーが転送を実現しているのだが、responseが「転送の指示を出している」と言って差し支えない。

ニセWebを作るためには、responseが「ニセの転送」を指示するか/実行してしまえばよい。ニセであっても、データの送り先はクライアントである。クライアント? ニセの世界にクライアント(通常はブラウザと人間)なんているのか? いるんです! They exist within the fake world. ニセWebの特徴の三番目に挙げた「Webクライアントを含む」がポイントになってくる。

ニセのWebクライアントは、コマンド(スクリプトコード)で実現される。このスクリプトコードはクライアントエミュレーション・スクリプトと呼ばれたりしてる。単にニセクライアントと呼んでもいいだろう。ニセWebの世界で「データをクライアントまで届ける」とは、「ニセクライアントであるコマンドを呼ぶ」ことに対応する。ニセのresponseは、ニセのクライアントに制御を移すのだ。制御の移し方の“トポロジー”は、ハイパーリンクグラフの情報から得られる。

ここで問題が生じる。サーバーとクライアント間の制御の移動は(概念的には)無限に続く。アクション(と呼ばれるコマンド)とニセクライアント(と呼ばれるコマンド)のあいだで、無限の相互呼び出しが発生する。通常はスタックを使い潰し破綻する。Catyにgoto制御が必要な本当の理由はここにある。forward制御は、スタックを消費しないで制御移譲ができるので、サーバー/クライアントのあいだの無限回の相互呼び出しを実現できる。

http://d.hatena.ne.jp/m-hiyama/20120828/1346114644 の最後の(短い)サンプルである「制御をピンポンして無限に Hello, world. を印字し続けるスクリプト」を参照のこと。

renderコマンドの置き換え

クライアントが「ブラウザ+人間」である場合、HTMLによる表示、しかも凝った表示が必要になる。ニセのクライアントであるクライアントエミュレーション・スクリプトは、凝ったHTMLを受け取っても困惑するだけだ。

renderコマンドを置き換えることにより、クライアントエミュレーション・スクリプトにとって都合のよいデータを作り出すことができる。

その他

アクションが正常終了すれば、データはrender(省略可能)とresponseを通ってWebに出ていく。アクション内でシグナル/例外が発生すると、アクション内に書かれたresponseは通らない。シグナル/例外のハンドラーに制御が移り、そのなかで再びresponseされる。シグナル/例外のハンドラー内でも置き換えたresponseが使われれば、ニセWebは正しく機能するが、必要ならシグナル/例外のハンドラーをゴッソリ入れ替えてしまうこともできる。

本物のシグナル/例外ハンドラーでは、スタックトレースなどの詳細情報を潰してしまうことが多いので、テストやデバッグの目的では、ハンドラー(マッパー)を置き換えることになるだろう。

というわけで、response, render, map-signal, map-exceptionの置き換えでニセWebが出来る。最小の置き換えならresponseだけでもいけるはずだ。

*1:僕の感覚としては、マゾ・テストが一番大きな目標かな。あと、ハイパーバリデーションも同程度の重さがある。