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>"
Другие вопросы по тегам