jac*_*cal 1 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...))))
Run Code Online (Sandbox Code Playgroud)
一切都很好。
然而,当我想从 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"))
Run Code Online (Sandbox Code Playgroud)
但这不起作用:
(defun bar ()
(with-html-output-to-string
(*standard-output* nil :prologue t)
(htm "This will NOT show up in the html stream")))
Run Code Online (Sandbox Code Playgroud)
(我已经尝试过各种操作,但无济于事。)
我确信我正在做一些简单的错误;必须在任何 subfn 中恢复为格式 t 是非常丑陋的,尤其是。bcs 我无法使用 cl-who 方便的 html 宏。
CL-WHO 基于生成 write 语句的宏,并且自动打印以关键字开头的所有形式以及参数值。其他表格仅进行评估(例如,副作用),并且不会自动打印。这就是 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")))
Run Code Online (Sandbox Code Playgroud)
返回值是一个字符串,因为您正在使用with-html-output-to-string. 暂时绑定到一个流*standard-output*,与外面的不同bar,只是为了构建一个返回给调用者的字符串,这里是foo。不打印该字符串(仅打印内容位置为常量字符串的形式)。
您可以使用 强制写入返回的生成的 HTML str,但恕我直言,最好的选择是直接写入与调用者相同的输出流,而不是构建中间字符串。
基本上,使用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))
Run Code Online (Sandbox Code Playgroud)
定义一个我们可以控制的流,默认绑定到打开*standard-output*流时(而不是定义变量时)绑定的任何内容:
(defvar *html-output* (make-synonym-stream '*standard-output*)
"Use a dedicated stream variable for html")
Run Code Online (Sandbox Code Playgroud)
另外,定义一个通用的缩进级别:
(defvar *indent* 2
"Default indentation")
Run Code Online (Sandbox Code Playgroud)
有两个宏,一个用于嵌入辅助函数中的片段,这些函数写入我们的流中,另一个用于顶级 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))
Run Code Online (Sandbox Code Playgroud)
用法示例:
(defun my-section (title)
(with-html
(:h1 (esc title))
(:p "lorem ipsum")))
(defun my-page ()
(with-html-page
(my-section "title")))
Run Code Online (Sandbox Code Playgroud)
调用(my-page)返回:
"<!DOCTYPE html>
<h1>title
</h1>
<p>lorem ipsum
</p>"
Run Code Online (Sandbox Code Playgroud)
另请参阅鲜为人知的https://github.com/ruricolist/spinneret。