cl-who прохождение потока в funcalls
Я использую 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, чтобы выполнить... любую подзаработку, которую я хочу сделать, я не могу понять, как заставить контекст HTM CL-WHO выполнять вызов. Например, это прекрасно работает:
(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")))
(Я пробовал различные манипуляции с этим, но безрезультатно.)
Я уверен, что я делаю что-то простое неправильно; Ужасно уродливо возвращаться к формату t в любом subfn, особенно. я не могу использовать удобные HTML-макросы cl-who.
2 ответа
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.
Мне не ясно, что ты пытаешься сделать. Если вы хотите составить веб-страницу по частям, с помощью функций создания частей страницы вы можете использовать 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>"
Из руководства:
Формы, которые выглядят как
(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>"