cl-谁在funcall中传递流

时间:2018-12-20 20:57:16

标签: lisp common-lisp hunchentoot cl-who

到目前为止,我正在使用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宏。

2 个答案:

答案 0 :(得分:3)

CL-WHO基于生成写语句的宏,并且将自动打印所有以关键字开头的表格以及参数值。其他表格仅进行评估(例如产生副作用),不会自动打印。这就是CL-WHO引入strfmteschtm宏的原因,这些宏强制(不同地)打印其参数。

您的代码:

(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>"