到目前为止,我正在使用cl-who(通过hunchentoot),它完全成功,但是有一件我不知道的事情,而且我的解决方法很丑,所以我希望有一个简单的解决方法。我的hunchentoot轻松处理程序调用的函数看起来像这样:
(defun foo ()
(with-html-output-to-string
(*standard-output* nil :prologue t)
(:html
(:body (htm :br :hr "foo" :hr ...etc...))))
一切都很好。
但是,当我想从foo内部调用一个辅助函数来执行...我想做的任何子工作时,我都无法弄清楚如何使CL-WHO的HTM上下文能够执行该调用。例如,这可以正常工作:
(defun foo ()
(with-html-output-to-string
(*standard-output* nil :prologue t)
(:html
(:body (htm :br :hr "foo" :hr (bar)))))
(defun bar ()
(format t "This will show up in the html stream"))
但这不起作用:
(defun bar ()
(with-html-output-to-string
(*standard-output* nil :prologue t)
(htm "This will NOT show up in the html stream")))
(我尝试了各种操作,无济于事。)
我确定我在做一些简单的错误;必须在任何subfn中(尤其是)将其还原为t格式,这是非常可怕的。 bcs我不能使用cl-谁方便的html宏。
答案 0 :(得分:3)
CL-WHO基于生成写语句的宏,并且将自动打印所有以关键字开头的表格以及参数值。其他表格仅进行评估(例如产生副作用),不会自动打印。这就是CL-WHO引入str
,fmt
,esc
和htm
宏的原因,这些宏强制(不同地)打印其参数。
您的代码:
(defun bar ()
(with-html-output-to-string
(*standard-output* nil :prologue t)
(htm "This will NOT show up in the html stream")))
返回值是一个字符串,因为您正在使用with-html-output-to-string
。 *standard-output*
暂时绑定到与bar
外部的流不同的流,只是构建了一个返回给调用方(此处为foo
)的字符串。
不打印字符串(仅打印内容位置为恒定字符串的表单)。
您可以使用str
强制编写返回的生成的HTML,但是恕我直言,最好的选择是直接将其与调用方写入同一输出流,而不是构建中间字符串。
基本上,使用with-html-output
:
我不想使用*standard-output*
,而是只使用html的流。这样可以防止其他库向HTML页面编写任何不需要的内容。您还可以将流传递给每个辅助功能,但在这种情况下最好使用特殊变量。
让我们使用简单的宏来简化语法并执行我们自己的约定。
以下内容定义了一个程序包,并将CL-WHO配置为发出HTML5代码。这必须在宏展开之前完成,因为在宏展开期间会使用要设置的特殊变量:
(defpackage :web (:use :cl :cl-who))
(in-package :web)
;; Evaluate before CL-WHO macro are expanded
(eval-when (:compile-toplevel :load-toplevel :execute)
(setf (html-mode) :html5))
定义一个我们可以控制的流,默认情况下绑定到我们打开(而不是定义变量时)时*standard-output*
绑定的任何值:
(defvar *html-output* (make-synonym-stream '*standard-output*)
"Use a dedicated stream variable for html")
还要定义一个通用的缩进级别:
(defvar *indent* 2
"Default indentation")
有两个宏,一个用于嵌入在我们的流中的辅助函数中嵌入的片段,另一个用于顶级html页面,它们返回一个字符串。
(defmacro with-html (&body body)
"Establish an HTML context (intended for auxiliary functions)."
`(with-html-output (*html-output* nil :indent *indent*)
,@body))
(defmacro with-html-page (&body body)
"Return an HTML string (intended for top-level pages)."
`(with-html-output-to-string (*html-output* nil :prologue t :indent *indent*)
,@body))
用法示例:
(defun my-section (title)
(with-html
(:h1 (esc title))
(:p "lorem ipsum")))
(defun my-page ()
(with-html-page
(my-section "title")))
呼叫(my-page)
返回:
"<!DOCTYPE html>
<h1>title
</h1>
<p>lorem ipsum
</p>"
另请参阅鲜为人知的https://github.com/ruricolist/spinneret。
答案 1 :(得分:0)
我不清楚您要做什么。如果要以“片段”形式构成网页,则可以通过函数生成页面的组成部分,可以在调用以下函数之一时使用str
,例如:
(defun f1 ()
(with-html-output-to-string (*output-string*)
(:p "some text")))
(defun f2 ()
(with-html-output-to-string (*output-string*)
(:body (:p "some other text") (str (f1)))))
(f2)
"<body><p>some other text</p><p>some text</p></body>"
来自manual:
看起来像
(str form)
的表单将被(let ((result form)) (when result (princ result s)))
取代
如果您不使用str
,那么结果将不包含在html输出中:
(defun f3 ()
(with-html-output-to-string (*output-string*)
(:body (:p "some other text") (f1))))
(f3)
"<body><p>some other text</p></body>"