2010-09-25 2 views
4

Hunchentoot/сл-кто Page СоставHunchentoot/сл-кто композиция страницы

Я пытаюсь соединить несколько страниц в 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 "[email protected][~A - ~]Test Site" title))) 
      (:body ,@body))))

Теперь, когда у меня есть чистый текст страницы, или один заполненный HTML литералов как

(define-easy-handler (test-page :uri "/")() 
    (page-template (:title "Splash Page") (:p "Testing testing")))

все это-нормально. Страница выводится правильно, и я могу мгновенно увидеть мои действия. Однако, когда у меня есть страница, состоящая из избыточных элементов, это не так просто. Например, скажем, у меня есть страница, по которой по какой-то причине я хочу отображать три RSS-ленты новостей. Это достаточно сложный компонент, который я хочу, чтобы абстрагировать, так что к моему minnd, я должен быть в состоянии сделать что-то вроде

(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 рассказать мне, каков правильный/канонический способ разложения страницы на компоненты?


РЕДАКТИРОВАТЬ:

Правильная реакция Ramarren ниже; макросы with-html-output работают под разными правилами оценки. Версия публикации-подборке, которая на самом деле работает в этой ситуации на самом деле

(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 (я Schemer, не дают мне слишком много трудное время о душе лямбды , но они были неправильным выбором здесь) и использование htm для блокирования блоков html s-exps (h-exps?), которые иначе не были бы в контексте для with-html-output. Наконец, мне пришлось обернуть текст, но НЕ :href свойств в (str), чтобы они динамически расширялись.

ответ

5

Макрос with-html-output-to-string расширяет свое тело, используя special evaluation rules. В частности, любые недопустимые формы остаются как есть, что означает, что макросы не разворачиваются до генерации кода html, что означает, что к моменту, когда ваш макрос publish-newsfeed будет расширен стандартным компилятором, он больше не находится в контексте with-html-output-to-string. Это понятно при расширении макроса manually, особенно с использованием функции макроэкспонирования слизи.

Чтобы заставить его работать, вы должны сделать publish-newsfeed функцию и использовать with-html-output внутри него с тем же потоком (либо принять `стандартный вывод- везде или передать поток явно).

Смежные вопросы