RSS Feed

Lisp Project of the Day

print-html

You can support this project by donating at:

Donate using PatreonDonate using Liberapay

Or see the list of project sponsors.

print-htmlwebtemplates

Documentation😀
Docstrings😀
Tests 🥺
Examples😀
RepositoryActivity🥺
CI 🥺

This is an HTML generator. This library is similar to cl-who or spinneret, but with two interesting differences.

The first distinction is that print-html is written in the literate programmings style and have very succinct code which can be read in 5-10 minutes.

The second distinction is that print-html is based on generics and you can define a method to print some classes into HTML.

Let's try this!

POFTHEDAY> (print-html:print-html-to-string
            (print-html:html
              (:article
               ((:section :class "intro")
                (:p "Hello Lisp World!")))))
"<article>
<section class=\"intro\">
<p>Hello Lisp World!</p>
</section>
</article>
"

Now I'll create a class of user which can be serialized into HTML:

;; This is our buisiness logic:
POFTHEDAY> (defclass user ()
             ((name :initarg :name
                    :reader get-name)))

POFTHEDAY> (defparameter *user*
             (make-instance 'user
                            :name "Sponge Bob"))

POFTHEDAY> (defun get-profile-uri (user)
             (format nil "/user/~A"
                     (get-name user)))

;; Here is the method to transform business logic
;; object into its HTML representation.
POFTHEDAY> (defmethod print-html:render ((user user))
             (print-html:html
               ((:a :href (get-profile-uri user))
                (get-name user))))

;; Note, it returns HTML nodes, not 
POFTHEDAY> (print-html:render *user*)
(#S(PRINT-HTML::TAG
    :NAME :A
    :ATTRS (:HREF "/user/Sponge Bob")
    :CHILDREN ("Sponge Bob")))

;; Now we can use this kind of objects
;; directly when rendering HTML pages:
POFTHEDAY> (print-html:print-html-to-string
            (print-html:html
              (:article
               ((:section :class "intro")
                (:p "Hello"
                    *user*)))))
"<article>
<section class=\"intro\">
<p>Hello
<a href=\"/user/Sponge Bob\">Sponge Bob</a>
</p>
</section>
</article>
"

Seems this is an interesting approach to render HTML. What do you think?

Update from 2020-09-15

Added performance test to compare with other template renderers. Main post with comparison is here.

POFTHEDAY> (defun render (title items)
             (print-html:print-html-to-string
              (print-html:html
                (:h1 title
                     (:ul
                      (loop for item in items
                            collect (print-html:html
                                      (:li item))))))))

POFTHEDAY> (render "Foo Bar"
                   '("One" "Two" "Tree"))
"<h1>Foo Bar
<ul>
<li>One</li>
<li>Two</li>
<li>Tree</li>
</ul>
</h1>
"

POFTHEDAY> (time
            (loop repeat 1000000
                  do (render "Foo Bar"
                             '("One" "Two" "Three"))))
Evaluation took:
  3.259 seconds of real time
  3.280993 seconds of total run time (3.226489 user, 0.054504 system)
  [ Run times consist of 0.258 seconds GC time, and 3.023 seconds non-GC time. ]
  100.68% CPU
  7,195,206,822 processor cycles
  2,560,006,112 bytes consed

Brought to you by 40Ants under Creative Commons License