cl-who прохождение потока в funcalls - PullRequest
0 голосов
/ 20 декабря 2018

Я использую 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, особенно.bcs Я не могу использовать удобные HTML-макросы cl-who.

Ответы [ 2 ]

0 голосов
/ 21 декабря 2018

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*, когда мы open поток (не тогда, когда мы определяем переменную):

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

0 голосов
/ 21 декабря 2018

Мне не понятно, что вы пытаетесь сделать.Если вы хотите составить веб-страницу «по частям», с помощью функций, создающих части страницы, вы можете использовать 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>"
...