2009/01/05

[Common Lisp] UCW で Arc Challenge

UCW で Arc Challenge を書いてみた。

Arc より短かいく書くのは無理なので、なるべく UCW っぽく書いてみたつもり。

UCW として今回必要だったものは次のとおり

  • サーバ(standard-server)
  • アプリケーション(arc-challenge-application)
  • ウィンドウコンポーネント(arc-challenge-window)
  • foo 入力コンポーネント(input-foo-component)
  • リンク表示コンポーネント(click-here-component)
  • foo 表示コンポーネント(you-said-component)
  • エントリポイント(index.ucw)

また UCW はコンポーネント指向なので arc-challenge-window をウィンドウコンポーネントとして定義し、そのボディ部分としてinput-foo-component, click-here-component, you-said-componentの3つのコンポーネントを定義した。

また UCW は継続ベースなのでエントリポイントと言う入口(最初に表示するページ)を定義する。エントリポイントでは (call 'arc-challenge-window) によってarc-challenge-window を表示している。

arc-challenge-window は initialize-instance :after で input-foo-componentを設定しているので foo 入力コンポーネントが表示される。

input-foo-component のフォームの :action で、call-component で arc-challenge-window の body 部分を切り替えることで、リンクの表示と foo の入力の表示を行っている。このあたりが継続っぽいところだと思う。

#|
http://www.paulgraham.com/arcchallenge.html
(defop said req
(aform [w/link (pr "you said: " (arg _ "foo"))
(pr "click here")]
(input "foo")
(submit)))
|#

(eval-when (:compile-toplevel :load-toplevel :execute)
(require :ucw))

(in-package :ucw-user)

;; サーバのインスタンスを生成
(defvar *arc-challenge-server*
(make-instance 'standard-server
:backend (make-backend :httpd :port 9090)))

;; アプリケーションを定義
(defclass arc-challenge-application (standard-application
cookie-session-application-mixin)
()
(:default-initargs
:url-prefix "/arc-challenge/")) ; http://localhost:9090/arc-challenge/xxx

;; アプリケーションのインスタンスを生成
(defvar *arc-challenge-application* (make-instance 'arc-challenge-application))

;; サーバにアプリケーションを登録
(register-application *arc-challenge-server* *arc-challenge-application*)

;; ウィンドウコンポーネント
(defcomponent arc-challenge-window (standard-window-component)
()
(:default-initargs
:title "Arc Challenge"))

;; body の設定
(defmethod initialize-instance :after ((self arc-challenge-window) &rest rest)
(declare (ignore rest))
(with-slots (body) self
(setf body (make-instance 'input-foo-component :window self))))

;; ウィンドウの描画
(defmethod render ((self arc-challenge-window))
(<:h1 "Arc Challenge")
(call-next-method))

;; foo 入力コンポーネント
(defcomponent input-foo-component ()
((window :initarg :window)))

;; その描画
(defmethod render ((self input-foo-component))
(let ((foo ""))
(<ucw:form
:action (flet ((goto (&rest rest)
(with-slots (window) self
;; arc-challenge-window の body 部分を切り替える。
(call-component self (apply #'make-instance rest)))))
;; リンク表示コンポーネント
(goto 'click-here-component)
;; foo 表示コンポーネント
(goto 'you-said-component :foo foo))
"foo "
(<ucw:input :type "text" :accessor foo)
(<:submit))))

;; リンク表示コンポーネント
(defcomponent click-here-component () ())

;; その描画
(defmethod render ((self click-here-component))
(<ucw:a :action (answer)
"click here"))

;; foo 表示コンポーネント
(defcomponent you-said-component ()
((foo :initarg :foo)))

;; その描画
(defmethod render ((self you-said-component))
(<:div "you said: "
(<:as-html (slot-value self 'foo)))
(<ucw:a :action (answer) "ok"))

;; エントリポイントを定義
(defentry-point "index.ucw" (:application *arc-challenge-application*)
()
(call 'arc-challenge-window))

;; エラー発生時にデバッガを起動
(setf ucw-core:*debug-on-error* t)

;; サーバスタート
(defun start-arc-challenge ()
"http://localhost:9090/arc-challenge/index.ucw
にアクセスしてください。"

(startup-server *arc-challenge-server*))

0 件のコメント: