継続を使ったコントローラを作る:Gaucheでメタプログラミング(5)(2/3 ページ)
Lispの一種であるScheme。いくつかある処理系の中でも気軽にスクリプトを書けるGaucheでLispの世界を体験してみよう(編集部)
継続ベースのWebアプリケーションを作る
ここでは、独自の継続ベースのコントローラと簡単なWebサーバを作ります。前回、説明したORマッパーとテンプレートエンジンを組み合わせ、テーブルの一覧表示や編集ができる簡単なWebアプリケーションを作ってみます。
モジュール
継続を使ったWebアプリケーションのプログラムの説明をする前に、Gaucheのモジュールについて少し説明します。ほとんどの言語は、関数名などの衝突を回避したり、コードの再利用性を高めたりするためのモジュール機能を持っています。Javaではパッケージと呼ばれるものがそれに当たります。
Gaucheのモジュールは、下のテンプレートエンジンモジュールのようにdefine-moduleでモジュールを定義します。define-moduleの中には、そのモジュールで利用するモジュール名を指定するuse、そのモジュールで公開する関数などを指定するexportなどを記述します。
その下のselect-moduleは、以降の関数定義などの所属するモジュールを指定しています。最後のprovideは、このモジュールをuseするモジュールが複数あっても再読み込みしないようにするための設定です。
通常はモジュール単位でファイルを分けます。
(define-module template
(use file.util)
(export render rendering-text rendering-file))
(select-module template)
(define (template->s-exp templ)
(define (quote-display m)
(format "(display ~s *p*)" (m 1)))
(if (not (#/<%/ templ))
templ
(let* ((s (regexp-replace-all* templ
#/<%=(.*?)%>/ "<% (display \\1 *p*) %>"
#/%>(.*?)<%/ quote-display))
(str-s-exp (regexp-replace* s
#/^(.*?)<%/ quote-display
#/%>(.*?)$/ quote-display)))
(read-from-string (string-append "(call-with-output-string
(lambda(*p*)"
str-s-exp
"))")))))
(define (rendering-text templ vars args)
(apply (eval `(lambda ,vars ,(template->s-exp templ))
(interaction-environment)) args))
(define (rendering-file templ-file vars args)
(rendering-text (file->string templ-file) vars args))
(define-macro (render tmpl-file . args)
`(rendering-file ,tmpl-file (quote ,args) (list ,@args)))
(provide "template")
これから各モジュールを簡単に解説します。関数などの詳細な説明しませんので適宜ユーザーリファレンスを調べてください。
templateモジュール
templateモジュールは、前回作ったテンプレートエンジンを少し変更しました。変更点は次のとおりです。
- 生成されるS式を(call-with-output-string (lambda(*p*) .... )とし、テンプレート全体を文字列として戻すようにしました
- 上に伴い、内部で使っているdisplay関数を文字列ポートに出力するようにしました
- rendering-text関数は、テンプレート評価の際にテンプレート内の変数に値を渡せるようlambda式にしました
- ファイルからテンプレートを読み込むrendering-file関数を追加しました
- テンプレートに渡す変数のリストを指定するだけでrendering-fileを利用できるようにするrenderマクロを追加しました
Gaucheで大量の文字列を生成する際に、string-appendのような文字列操作関数を多用するとメモリを大量に消費し効率的ではありません。このような用途には、文字列ポートを使います。
serverプログラム
このファイルは、モジュールではなくメインプログラムです。8080番のポートにソケットを作成し、Webブラウザからのリクエストを待ち、GETリクエストがあれば処理部分であるdispatch関数を呼び出し、その結果をWebブラウザに送れる形にします。
このサーバはGETリクエストにしか対応していません。また、出力はHTMLのみです。何かエラーが起きた場合は、guardマクロで例外処理を行います。ここではエラー情報を表示し、終了します。
ちなみに、report-error関数はドキュメント化されてない関数ですが、便利なので使っています。ただし、ドキュメント化されてない関数は将来、仕様が変更されるかもしれないので注意してください。
(add-load-path "./")
(use gauche.net)
(use controller)
(define (web-server)
(let1 server-socket (make-server-socket 'inet 8080 :reuse-addr? #t)
(guard (e (else (socket-close server-socket) (report-error e)))
(while #t
(let* ((client-socket (socket-accept server-socket))
(request (get-request (socket-input-port client-socket))))
(put-response
(dispatch request)
(socket-output-port client-socket))
(socket-close client-socket))))))
(define (get-request in-port)
(cond ((rxmatch #/^GET\s+(\S+)\s/ (read-line in-port)) => (lambda (m) (m 1)))
(else "")))
(define (put-response content out-port)
(display "HTTP/1.1 200 OK\r\n" out-port)
(display "Content-Type: text/html; charset=utf-8\r\n" out-port)
(display #`"Content-Length: ,(string-size content)\r\n" out-port)
(display "\r\n" out-port)
(display content out-port))
(web-server)
Copyright © ITmedia, Inc. All Rights Reserved.