Композиция страницы Hunchentoot / cl-who
Я пытаюсь собрать несколько страниц в hunchentoot в качестве эксперимента, и я сталкиваюсь с неожиданной стеной. В качестве примера у меня есть следующий шаблон макроса.
(defmacro page-template ((&key title) &body body)
`(with-html-output-to-string
(*standard-output* nil :prologue t :indent t)
(:html :xmlns "http://www.w3.org/1999/xhtml" :xml\:lang "en" :lang "en"
(:head (:meta :http-equiv "Content-Type" :content "text/html;charset=utf-8")
(:title ,(format nil "~@[~A - ~]Test Site" title)))
(:body ,@body))))
Теперь, когда у меня есть чисто текстовая страница или страница, заполненная HTML-литералами, такими как
(define-easy-handler (test-page :uri "/") ()
(page-template (:title "Splash Page") (:p "Testing testing")))
все в порядке. Страница выводится правильно, и я сразу вижу усилия моего кода. Однако, когда у меня есть страница, которая состоит из избыточных элементов, это не так просто. Например, допустим, у меня есть страница, на которой по какой-либо причине я хочу отобразить три RSS-канала. Это достаточно сложный компонент, который я хочу абстрагировать, поэтому, на мой взгляд, я смогу сделать что-то вроде
(define-easy-handler (test-feed :uri "/feeds") ()
(page-template (:title "Splash Page")
(publish-newsfeed "http://nf-one.html")
(publish-newsfeed "http://nf-two.html")
(publish-newsfeed "http://nf-three.html")))
(defmacro publish-newsfeed (url &optional (item-limit 5))
(flet ((get-text (s-tree node-path) (car (last (xmls-tools:find-subtree s-tree node-path)))))
(let ((rss-feed (xmls:parse (drakma:http-request url))))
`(:div :class "rss-feed"
(:a :href ,(get-text rss-feed '("channel" "link")) :target "_top" (:h1 ,(get-text rss-feed '("channel" "title"))))
(:ul ,@(mapcar #'(lambda (item)
`(:li (:a :href ,(get-text item '("link")) :target "_top" (:h2 ,(get-text item '("title"))))
(:p :class "date" ,(get-text item '("pubDate")))
(:p ,(get-text item '("description")))))
(let ((items (xmls-tools:find-all-children (xmls-tools:find-subtree rss-feed '("channel")) "item")))
(if (> (length items) item-limit) (subseq items 0 item-limit) items))))))))
Но результатом вышеописанного является страница «Ошибка сервера». Я не совсем уверен, почему; page-template
- это макрос, поэтому вызовы publish-newsfeed
не следует расширять до тех пор, пока они не попадут в контекст with-html-output-to-string
. Может кто-нибудь сказать мне, что я делаю не так?
Кроме того, при ближайшем рассмотрении различных учебных пособий Hunchentoot / cl-who, ни один из них, по-видимому, не выполняет такую композицию страниц. Может кто-нибудь с некоторым опытом Hunchentoot сказать мне, каков правильный / канонический способ разложения страницы на компоненты?
РЕДАКТИРОВАТЬ:
Правильный ответ Рамаррена ниже; макросы with-html-output
работают по разным правилам оценки. Версия publish-newsfeed, которая действительно работала бы в этой ситуации, на самом деле
(defun publish-newsfeed (url &optional (item-limit 5))
(flet ((get-text (s-tree node-path) (car (last (xmls-tools:find-subtree s-tree node-path)))))
(let* ((rss-feed (xmls:parse (drakma:http-request url)))
(items (xmls-tools:find-all-children (xmls-tools:find-subtree rss-feed '("channel")) "item"))
(ltd-items (if (> (length items) item-limit) (subseq items 0 item-limit) items)))
(with-html-output
(*standard-output* nil :indent t)
(:div :class "rss-feed"
(:a :href (get-text rss-feed '("channel" "link")) :target "_top" (:h1 (str (get-text rss-feed '("channel" "title")))))
(:ul (dolist (item ltd-items)
(htm (:li (:h2 (:a :href (get-text item '("link")) :target "_top" (str (get-text item '("title")))))
(:p :class "date" (str (get-text item '("pubDate"))))
(:p (str (get-text item '("description")))))))))))))
Обратите внимание на удаление mapcar
для dolist
(я - Схемер, не беспокойтесь о том, чтобы мне нравились лямбды, но здесь они не были правильным выбором), и использование htm
для экранирования блоков html s-exps (h-exps?), которые иначе не были бы в контексте with-html-output
. Наконец, мне пришлось обернуть текст, но НЕ :href
свойства в (str )
, чтобы заставить их динамически расширяться.