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
. Строка не печатается (печатаются только формы, которые являются постоянными строками в позиции содержимого).
Вы можете принудительно записать возвращенный сгенерированный 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))
Определим поток, которым мы можем управлять, привязанный по умолчанию к тому, что *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.
person
coredump
schedule
20.12.2018
(defun bar() "This will NOT show up in the html stream")
? Вы не делаете ничего, что требуетhtm
. - person Barmar   schedule 21.12.2018:prologue t
. Это следует использовать только в HTML-документе верхнего уровня, а не внутри других тегов. - person Barmar   schedule 21.12.2018