Hunchentoot / cl-页面组成

时间:2010-09-25 14:37:46

标签: common-lisp hunchentoot

Hunchentoot / cl-who Page Composition

我正试图在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经验的人都可以告诉我将页面分解为组件的正确/规范方法是什么?


编辑:

下面是Ramarren的正确回应; 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(我是Schemer,不要给我太多关于喜欢lambdas的困难时间,但这里不是正确的选择) ,以及使用htm来转义html s-exps(h-exps?)的块,否则这些块不会出现在with-html-output的上下文中。最后,我必须在:href中包装文本但不包含(str )属性,以使它们动态扩展。

1 个答案:

答案 0 :(得分:5)

with-html-output-to-string使用special evaluation rules扩展其正文。特别是,任何未识别的表单都保留原样,这意味着在生成html生成代码之前不会展开宏,这意味着当标准编译器扩展您的publish-newsfeed宏时,它不再处于上下文中的with-html-output-to-string。扩展宏manually时很明显,特别是使用slime宏扩展功能。

要使其正常工作,您应该使publish-newsfeed成为一个函数,并在其中使用with-html-output使用相同的流(或者假设` standard-output 到处或者明确地传递流)。