Step4

English page

Kahua Release

kahua-web Release

Security Advisory

Event Log

Documentation

For developers

Site info

Related Site

ソーシャルブックマークを作る(2)

さて、いちいちkahua-shellからブックマークを登録するわけにもいかないので、 ブックマークを追加するための画面とエントリを追加してみましょう。

登録画面を作る

まずはブックマークを登録するフォームを生成する手続きを書いてみましょう。

(define (bookmark-form/)
  (form/cont/
   (@@/ (cont (entry-lambda (:keyword title url)
                (make <bookmark> :title title :url url)
                (redirect/cont (cont all)))))
   (table/
    (tr/ (th/ "Title: ")
         (td/ (input/ (@/ (type "text") (name "title") (value "")))))
    (tr/ (th/ "URL: ")
         (td/ (input/ (@/ (type "text") (name "url") (value "")))))
    (tr/ (th/) (td/ (input/ (@/ (type "submit") (value "Register"))))))))

一見ややこしく見えますが、form要素の中でテーブルを組んで体裁を整えている だけです。ここでキモになるのは、 form/cont/ という見慣れない高階タグ関数です。

form/cont/ は、エントリをactionに指定した form 高階タグを生成する特殊な 高階タグ関数です。

(@@/ (cont (entry-lambda (:keyword title url)
                (make <bookmark> :title title :url url)
                (redirect/cont (cont all)))))

という部分が、実際にエントリをactionに結びつけているところです。@/ では なく @@/ であることに注意しましょう。これは、SXMLで言うところの補助属性 で、エントリ(継続)は、この補助属性として form/cont/ や a/cont/ などに渡 されます。

この例では、entry-lambda で作った無名のエントリ(継続)を渡しています。 このエントリは「title」と「url」という2つのパラメータを受け取り、 <bookmark> のインスタンスを作ってから、一覧のエントリにリダイレクト しています。:keyword をつけるのをお忘れなく。これをつけないと、 title と url をフォームのパラメータではなくリクエストURIのパス要素として 受け取ることになってしまいます。well knownエントリとは違い、無名エントリ はこのページにアクセスしない限り作られません。ブックマークの追加は 必ずこのページを経由して欲しいので、ここでは無名エントリを使いました。

redirect/cont は、エントリにリダイレクトを行う手続きです。最後に / は つきません。これは高階タグを生成する手続きではないからです。

ではこれを呼び出すエントリを書きましょう。名前は「new」にします。

(define-entry (new)
  (kahua:xml-template->sxml
   page-template
   :title (title/ (@/ (id "title"))
                  "New Bookmark")
   :body (div/ (@/ (id "body"))
               (h1/ "New Bookmark")
               (bookmark-form/))))

ページの体裁を整えている以外は、本当に呼び出すだけですね。では、 http://localhost:8088/bookmarks/new にブラウザでアクセスして、実際にブッ クマークを追加してみましょう。

ちょっとリファクタリング

さて、ここまでのコード全体を眺めてみましょう。

(use gauche.collection)

(load "bookmarks/version.kahua")

(define page-template
  (kahua:make-xml-template
   (kahua-template-path "bookmarks/page.xml")))

(define-class <bookmark> (<kahua-persistent-base>)
  ((title :allocation :persistent :init-keyword :title :init-value "")
   (url :allocation :persistent :init-keyword :url :init-value "")))

(define (bookmark-entry/ bm)
  (li/ (a/ (@/ (href (slot-ref bm 'url))) (slot-ref bm 'title))))

(define (bookmark-list/ bm-collection)
  (ul/ (map/ bookmark-entry/ bm-collection)))

(define-entry (all)
  (kahua:xml-template->sxml
   page-template
   :title (title/ (@/ (id "title")) "All Bookmarks")
   :body (div/ (@/ (id "body"))
               (h1/ "All Bookmarks")
               (bookmark-list/ (make-kahua-collection <bookmark>)))))

(define (bookmark-form/)
  (form/cont/
   (@@/ (cont (entry-lambda (:keyword title url)
                (make <bookmark> :title title :url url)
                (redirect/cont (cont all)))))
   (table/
    (tr/ (th/ "Title: ")
         (td/ (input/ (@/ (type "text") (name "title") (value "")))))
    (tr/ (th/ "URL: ")
         (td/ (input/ (@/ (type "text") (name "url") (value "")))))
    (tr/ (th/) (td/ (input/ (@/ (type "submit") (value "Register"))))))))

(define-entry (new)
  (kahua:xml-template->sxml
   page-template
   :title (title/ (@/ (id "title"))
                  "New Bookmark")
   :body (div/ (@/ (id "body"))
               (h1/ "New Bookmark")
               (bookmark-form/))))

(define-entry (version)
  (kahua:xml-template->sxml
   page-template
   :title (title/ (@/ (id "title"))
                 "bookmarks")
   :body (div/ (@/ (id "body"))
               (h1/ (format "bookmarks: version ~a"
                            *bookmarks-version*))
               (a/cont/ (@@/ (cont greeting))
                        "greeting"))))

;
; initialization
;
   
(initialize-main-proc all)

何だか似たようなコードがたくさん並んでいます。リファクタリングなんて 言葉を持ち出すまでもなく、そろそろ整理しときたいですよね。

ということでざっと整理してみました。

(use gauche.collection)

(load "bookmarks/version.kahua")

(define page-template
  (kahua:make-xml-template
   (kahua-template-path "bookmarks/page.xml")))

(define (standard-page title body)
  (kahua:xml-template->sxml
   page-template
   :title (title/ (@/ (id "title")) title)
   :body (div/ (@/ (id "body")) (h1/ title) body)))

(define (make-link/ entry text)
  (p/ (a/cont/ (@@/ (cont entry)) text)))

(define-class <bookmark> (<kahua-persistent-base>)
  ((title :allocation :persistent :init-keyword :title :init-value "")
   (url :allocation :persistent :init-keyword :url :init-value "")))

(define (bookmark-entry/ bm)
  (li/ (a/ (@/ (href (slot-ref bm 'url))) (slot-ref bm 'title))))

(define (bookmark-list/ bm-collection)
  (ul/ (map/ bookmark-entry/ bm-collection)))

(define-entry (all)
  (standard-page "All Bookmarks"
                 (node-set/
                  (new-bookmark-link/)
                  (bookmark-list/ (make-kahua-collection <bookmark>))
                  (version-link/))))
(define all-bookmarks-link/ (cut make-link/ all "[All Bookmarks]"))

(define (textbox/ name value . maybe-size)
  (input/ (@/ (type "text") (name name) (value value)
              (size (get-optional maybe-size #f)))))
(define (submit/ value)
  (input/ (@/ (type "submit") (value value))))

(define (bookmark-form/ title url)
  (form/cont/
   (@@/ (cont (entry-lambda (:keyword title url)
                (make <bookmark> :title title :url url)
                (redirect/cont (cont all)))))
   (table/
    (tr/ (th/ "Title: ") (td/ (textbox/ "title" title 50)))
    (tr/ (th/ "URL: ")   (td/ (textbox/ "url"   url   50)))
    (tr/ (th/) (td/ (submit/ "Register"))))))

(define-entry (new)
  (standard-page "New Bookmark"
                 (node-set/
                  (all-bookmarks-link/)
                  (bookmark-form/ "" ""))))
(define new-bookmark-link/ (cut make-link/ new "[New Bookmark]"))

(define-entry (version)
  (standard-page "bookmarks version"
                 (node-set/
                  (h2/ *bookmarks-version*)
                  (all-bookmarks-link/))))
(define version-link/ (cut make-link/ version "[Version]"))

;
; initialization
;
   
(initialize-main-proc all)

基本的には input/ で生成されるテキストボックスやsubmitボタンを部品化す るために関数としてくくりだした他、ページ全体の生成もエントリごとにでは なく、standard-page という関数を使って行うようにしました。さらに、各 well knownエントリ間をリンクで結んであります。a/cont/ は form/cont/ と 同様、リンク先としてエントリ(継続)を埋め込むための特殊な高階タグ関数です。

もう1点。属性値として#fを渡すと、最終的に生成される(X)HTMLにその属性自 体が出力されません。条件によってある属性を出したり出さなかったりしたい 時に使えるでしょう。

Copyright (c) 2003-2007 Kahua Project Contact | About Us