20101206

HTML in Common Lisp: code or fill?

I finally got around to write my very own HTML documentation system. Of course, this may be pointless and useless, but I found the alternatives "out there" lacking this or that (obviously!). So here I am, trying to automate the process of producing documentation like that of CLAZY, which I hand-crafted in the past. In the process, needless to say, I got carried away and got lost in the land of featurism only to contemplate my overall ignorance of most things webbish.

As usual, Edi Weitz came to my rescue by offering his CL-WHO and HTML-TEMPLATE, which raised a question: which of the two approaches is best? Note that the answer should consider how many dependencies my system files can have.

Let's start with the simple problem I have: I need to produce a .html file that contains, say, a "header" for my documentation. I could do it using CL-WHO in the following way (just a snippet, not working...)

(defun produce-header-file (fs header-pathname)
(declare (type frameset fs)
      (type pathname header-pathname))
(let ((fs-order (frameset-order fs))
   (fs-head-title (frameset-head-title fs))
   (fs-body-title (frameset-body-title fs))
   )
(with-open-file (hs header-pathname
                   :direction :output
                   :if-exists :supersede
                   :if-does-not-exist :create)
 (with-html-output (hs hs :indent t)
   (fmt "~A" (cl-fad:pathname-as-file header-pathname))
   (fmt +doctype-frameset-control-string+)
   (:html
    (:head
     (:title (str fs-head-title))
     (:link :rel "stylesheet" :href "clstyle.css"))
    (:body :style "margin: 0pt 0pt 0pt 0pt;"
     (:div
      :class "header"
      :style "padding-left: 2em; padding-top: 5pt; color: #41286f; font-size: 14pt"
      (:strong (str fs-body-title))
      (:div
       :class "navigation"
       :style "right: 2m"
       (:a
        :href "index.html"
        :class "navigation-link-selected"
        :target "_parent"
        (str "Home"))
       )))))
 )))

Or I could use HTML-TEMPLATE and ship around a "template" file like the following (again, this is just a snippet and contains errors):

<!-- header.html -->

<!DOCTYPE HTML PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html>
<head>
<title><!-- TMPL_VAR HEAD_TITLE --></title>
<link rel="stylesheet" href="clstyle.css">
</head>

<body style="margin: 0pt 0pt 0pt 0pt;">

<div class="header"
style="padding-left: 2em; padding-top: 5pt;  ">
<strong><i><!-- TMPL_VAR BODY_TITLE --></i></strong><br/>

<div class="navigation" style="right: 2m">
<!-- TMPL_LOOP frames -->
<!-- TMPL_IF current_frame -->
<a href=<!-- TMPL_VAR frame --> class="navigation-link-selected" target=_parent><!-- TMPL_VAR f_nav_name ->></a>
<!-- TMPL_ELSE -->
<a href=<!-- TMPL_VAR frame --> class="navigation-link" target=_parent><!-- TMPL_VAR f_nav_name ->></a>
<!-- /TMPL_IF -->
<!-- /TMPL_LOOP -->
</div>

</div>

</body>
</html>

<!-- end of file : header.html -->

I am inclined to go the CL-WHO way. I do not have to ship around an extra file and record its location etc etc; yet the HTML-TEMPLATE does have appeal. Any ideas out there?


(cheers)

17 comments:

  1. How about something simple like tags in "Land of Lisp"? Anyways, cl-who has a problem that it doesn't let you use macros. I believe https://github.com/vseloved/cl-who resolves those issues. Good luck.

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. It may also be worth looking at YACLML as described here.

    ReplyDelete
  4. Not directly related to your questions, but: (1) "pt" is a physical unit (1/72 inch), use it only on CSS for printing, not for screen (use px instead). (2) using the STYLE attribute is always a bad idea. (3) CSS are very rarely modified server-side, so it's better to write a separate static CSS file, without enbedding CSS in your lisp code or in your template.

    About templating vs coding, I used to think that templates are better because they give you a representation which is nearer to the final product (the html page); for example, in theory you could have a designer edit a template. However, if one starts for example including sub-templates, conditionals etc. that advantage is lost; in that case the code way is probably easier, provided that you have no design-only figures to deal with.

    ReplyDelete
  5. It depends. If your files are going to be hacked by a lisper, the first is preferable. If they're going to be constructed by a "web guru" who knows HTML better, then the second.

    ReplyDelete
  6. I use Exscribe, and I like it (if not me, the author, who else?). For political reasons, I'm moving away from Bigloo Scribe / Skribe syntax to Racket Exscribe syntax. I think it has the best of both worlds: concise markup syntax and integration with Lisp.

    ReplyDelete
  7. I really don't mean to be a troll saying this, but why use HTML for documentation?

    Why not something like latex, markdown, or even texinfo, which give a wide variety of possible backends? I particularly like the latex and texinfo options because they are not shackled to being rendered in one pass, so you can do intelligent things like indexing and cross referencing, which are at best cumbersome in HTML.

    I worked on a manual that was rewritten from Latex into HTML and it was a real disaster for that reason.

    ReplyDelete
  8. we just mix xml and lisp as much and seamless as we can. then you just reach for the well known tools like functions and macros...

    http://common-lisp.net/project/cl-quasi-quote/present-class.html

    the new codebase is available at: http://dwim.hu/darcsweb/darcsweb.cgi

    ReplyDelete
  9. "(1) "pt" is a physical unit (1/72 inch), use it only on CSS for printing, not for screen (use px instead)."

    This is actually a really bad idea because of high-DPI screens. You should specify the physical font size you want your user to see and let the rendering engine take care of layout (always keeping in mind that the web browser can at anytime override the specified font preferences).

    If you really need to draw graphics in a web page (say, with HTML5 canvas), you should measure the text size you want the user to see (specified in points) as what it renders to in pixels, and then draw the other graphics based on that (except that HTML5 canvas doesn't actually come with any useful text measurement functions).

    The notion of pixel itself is actually rather meaningless (even before PenTile came along, there weren't any color screens with square pixels).

    ReplyDelete
  10. This comment has been removed by the author.

    ReplyDelete
  11. Ok, now to address the on-topic part:

    There's two important parts to HTML rendering: what the output gets generated from, and what it gets generated to. That much is obvious, but the possible design space is actually much larger than CL-WHO vs HTML-TEMPLATE.

    An s-exp based HTML generator should in theory allow you to cons a bunch of s-exp together in various ways (macros and functions returning s-exp snippets) into an assembled s-exp document that is ready to be transformed into HTML.

    CL-WHO is actually one of the weakest s-exp to HTML generators this way because it does everything at macroexpansion time. It's a pain to process s-exp snippets into a larger document with it.

    What it really ends up being in practice is a way of doing (format reply-string-stream "some html produced from s-exp").

    The reason CL-WHO is really popular is that this is actually a great way of generating HTML. But as you said, sometimes you want to do HTML templating directly instead of writing it in s-exp.

    Well, there's no reason to write your templates in separate .html files. There's another great piece of Ediware that lets you have the best of both worlds: CL-INTERPOL.

    Grab the CL-INTERPOL reader function (interpol-reader) and put it under your own reader macro (I use #H) that wraps its result in a (format reply-string-stream ...) or equivalent, and you can do templated HTML inside your Lisp code.

    I've been experimenting with this and I really like it. It's much more concise than CL-WHO. I can post the actual reader macro code and some examples later if you want.

    ReplyDelete
  12. To answer a related question, take a look at ANTLR and StringTemplate. They are rather good toolkits to borrow ideas from (if not use directly).

    ReplyDelete
  13. Depends on how dynamic your page is and whether you don't mind page-flash. I do, which is why I do quite a bit of processing client-side.

    If I have a very dynamic page I use javascript client-side templates, ajax, jquery, and json, which talks to a hunchentoot web service. There are lots of client-side templating systems available. I use the one that comes with the uize framework. I also do a lot of DOM-building using jquery based on data that comes from a lisp server via json ajax calls.

    For small, highly-reusable pages that are largely static, I use server-side templates. I wrote the templating system.

    For all remaining dynamic pages that don't fit into the above categories I use cl-who, but to a limited extent.

    Lastly, for static pages/images lisp shouldn't be used at all except for prototyping.

    ReplyDelete
  14. Thanks guys for the great advice. As I said, I was contemplating my ignorance of web thingies :)

    Back to work.

    ReplyDelete
  15. I recently found a third alternative, which (I think) falls between the CL-WHO and HTML-TEMPLATE approaches.

    You use a template, but it's just html. No LOOP, IF, etc. stuff. And from the code you use HTML parser and CSS selectors to get to the relevant parts and modify them. What I like with this approach is, that you can use any HTML and the "interface" between you and the designer is only CSS ids/classes. (Well, almost).

    I don't know if there is a Common Lisp implementation though, but there is one for clejure called enlive[1] and there is a nice tutorial[2], which describes the concept better than me.

    [1] https://github.com/cgrand/enlive
    [2] https://github.com/swannodette/enlive-tutorial

    ReplyDelete
  16. Last non-trivial site[1] I worked on, I ended up with a mixture of code and templates. It depends on the content.

    [1] http://www.international-lisp-conference.org/2007/

    ReplyDelete
  17. I keep receiving questions about the cl-interpol trick, so I'm going to reply here as well. It's really simple:

    http://paste.lisp.org/display/118522

    One thing I don't like is having the cl-interpol delimiters there (I used '[' and ']' in the example, but cl-interpol will let you use other ones). There's not really any general way around it that I can think of.

    If you're thinking of using a templating engine, consider https://github.com/archimag/cl-closure-template which might be appropriate if the people making templates are familiar with Google's Closure Template tool.

    ReplyDelete