5

Hunchentoot/cl-who 页面构成

我正在尝试将 hunchentoot 中的几页放在一起作为实验,但我遇到了意想不到的墙壁。例如,我有以下模板宏。

(defmacro page-template ((&key title) &body body)
  `(with-html-output-to-string
    (*标准输出* 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") ()
  (页面模板(:标题“启动页面”)
                 (发布新闻源“http://nf-one.html”)
                 (发布新闻源“http://nf-two.html”)
                 (发布新闻源“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)))))
    (让 ((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宏在不同的评估规则下工作。在这种情况下实际工作的发布新闻源版本实际上是

(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)))
           (项目 (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)))
      (带有-html-输出
       (*标准输出* 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")))))))))))))

请注意删除mapcarfor dolist(我是一名计划者,不要给我太多关于喜欢 lambdas 的困难,但在这里它们不是正确的选择),以及使用htmto 转义 html s-exps 块(h-exps?)否则不会在with-html-output. 最后,我不得不包装文本而不是:href属性,(str )以使它们动态扩展。

4

1 回答 1

6

宏使用特殊的评估规则with-html-output-to-string扩展它的主体。特别是,任何无法识别的表单都保持原样,这意味着宏在生成 html 生成代码之前不会展开,这意味着当您的宏被标准编译器展开时,它不再位于. 这在手动扩展宏时很明显,尤其是使用 slime 宏扩展功能。publish-newsfeedwith-html-output-to-string

为了使它工作,你应该创建publish-newsfeed一个函数并with-html-output在它内部使用相同的流(或者在任何地方假设`标准输出或显式传递流)。

于 2010-09-25T19:18:18.813 回答