(define html-response/c
(or/c string?
(or/c (cons/c symbol? (listof html-response/c))
(cons/c symbol?
(cons/c (listof (list/c symbol? string?))
(listof html-response/c))))))
例:
"hello"はHTMLでは
hello
と表示されます。<p>This is an example</p>は
'(p "This is an example")
で生成されます。
<a href="link.html">Past</a>は
'(a ((href "link.html")) "Past")
で生成されます。
<p>This is <div class="emph">another</div> example.</p>は
'(p "This is " (div ((class "emph")) "another") " example.")
で生成されます。
これら
html-response
は直接cons
やlist
で作成できます。しかしながら、それでは厳しい表記となるでしょう。比較してみてください:
(list 'html (list 'head (list 'title "Some title"))
(list 'body (list 'p "This is a simple static page.")))
対:
'(html (head (title "Some title"))
(body (p "This is a simple static page.")))
両者とも同じ
html-response
を生成しますが、後者の方が記述も解読もはるかに簡単です。ここでは、How to Design Program:13章で解説された拡張リスト省略表記を使っています。先頭の引用符はリスト構造を表していて、これを使えば自信を持って静的なhtml
レスポンスを作る事が出来るのです。しかしながら、動的コンテンツでは、単純なリスト省略表記を使うと問題が生じます。
html-response
構造に式を挿し入れたい場合、単純なリスト省略表記のアプローチは使えません。と言うのも、それらはリテラルなリスト構造の一部として扱われてしまうからです!欲しいのは、構造の一部だけは普通の式として扱えるオプション付きの、クオートされたリスト省略記法の簡易性を備えた表記法、です。つまり、簡易表記可能な、動的に埋め込めるプレースホルダー付きのテンプレートを定義したいわけです。
Schemeはこのテンプレート機能を逆引用符として提供しています。逆引用とは、全体構造の直前にバッククオートを使う事です。普通のクオートされたリスト省略記法のように、殆どのリスト構造はネストされていてもリテラルに保護されます。一部の式の評価値を挿入したい場所で、その式の直前にクオート解除の為のコンマを挿し入れます。例えば:
; render-greeting: string -> html-response
; name を受け取り、動的 html-response を生成する。
(define (render-greeting a-name)
`(html (head (title "Welcome"))
(body (p ,(string-append "Hello " a-name)))))
Exercise.
(listof post?)
を受け取り、そのコンテンツのhtml-response
を生成する機能、render-posts
を書いてください。
render-posts : ((listof post?) . -> . html-response/c)
例えば:
(render-posts empty)
は次を生成します:
'(div ((class "posts")))
一方、
(render-posts (list (make-post "Post 1" "Body 1")
(make-post "Post 2" "Body 2")))
は次を生成します:
'(div ((class "posts"))
(div ((class "post")) "Post 1" "Body 1")
(div ((class "post")) "Post 2" "Body 2"))
render-posts
機能を入手したので、webアプリに戻ってhtml-response
を返すstart
機能を実装しましょう。
#lang web-server/insta
; ブログは (listof post)
; 投稿は (make-post title body)
(define-struct post (title body))
; BLOG: blog
; 静的ブログ
(define BLOG
(list (make-post "First Post" "This is my first post")
(make-post "Second Post" "This is another post")))
; start: request -> html-response
; リクエストを受け取り、webコンテンツの全てを
; 表示するページを生成する
(define (start request)
(render-blog-page BLOG request))
; render-blog-page: blog request -> html-response
; ブログとリクエストを受け取り、ブログのコンテンツの
; html-response を生成する
(define (render-blog-page a-blog request)
`(html (head (title "My Blog"))
(body (h1 "My Blog")
,(render-posts a-blog))))
; render-post: post -> html-response
; 投稿を受け取り、投稿のhtml-response要素を生成する
(define (render-post a-post)
`(div ((class "post"))
,(post-title a-post)
(p ,(post-body a-post))))
; render-posts: blog -> html-response
; ブログを受け取り、全投稿のhtml-response要素を
; 生成する
(define (render-posts a-blog)
`(div ((class "posts"))
,@(map render-post a-blog)))
Runを押せば、webブラウザがブログの投稿を表示します。
0 件のコメント:
コメントを投稿