Discussion:
Generating html tree with dynamic structure
Dmitry V'yal
2009-11-23 16:37:04 UTC
Permalink
Hello anyone,

I have a simple problem, but I can't find an elegant solution.

Basically I have a message and two flags, the first one signals message
should be rendered in <strong> tag and the second - in <italic>. So
there are four possibilities.

Here is the actual code:

(defmethod p-render-html ((s-tree tree) (p text-piece) s)
(with-html-output (s nil :prologue nil)
(let ((style (style-of p))
(txt (text-of p)))
(if style
(if (tree-find-prop s-tree style #'bold)
(htm (:strong (if (tree-find-prop s-tree style #'italic)
(htm (:italic (str txt)))
(str txt))))
(if (tree-find-prop s-tree style #'italic)
(htm (:italic (str txt)))
(str txt)))
(str txt)))))

I had to test for each case and it resulted in code duplication. Can it
be avoided? What if I had 5 flags or so?
Ken Harris
2009-11-23 18:49:01 UTC
Permalink
Hi Dmitry,

I had the same problem about a year ago. After looking at cl-who, my
conclusion was that it's really designed for efficiently writing fixed
structure with variable content, not variable structure.

My first attempt was simply string concatenation on the result of
multiple with-html-output-to-string calls. It worked fine, but with
bigger inputs it became unusably slow due to all the consing.

A second option I considered was to slap a big ol' EVAL (or
MACROEXPAND? I can't remember, offhand) in there. This is a little
weird, since cl-who transforms the input sexp into code to efficiently
write it, but then I'm only using it once. Also, due to the weird
call structure (to-macro-and-back), I recall it being somewhat hard to
debug.

The thing I finally settled on was writing my own function that took
as input something that looked like cl-who's input, and wrote it out
to a stream. It was surprisingly simple to write (I think it was
10-15 lines, but I don't have it in front of me right now), though it
obviously doesn't handle every case cl-who does. It wouldn't handle
htm/str/txt or prologues or inline code or whatever -- I just pass in
a complete sexp, and it walks the tree and writes to a stream.

In your case, you'd just need to generate a value like (:strong
(:italic "text")) to pass in -- and Lisp tends to be pretty good at
letting you build lists. :-)

(I hooked it up to write to my web server's response stream directly,
and it screamed. Well, at least compared to the old "cons a few
gigabytes every request" method.)

I can dig up that function tonight if it sounds useful to you.


- Ken
Dmitry V'yal
2009-11-24 08:47:22 UTC
Permalink
Post by Ken Harris
Hi Dmitry,
I had the same problem about a year ago. After looking at cl-who, my
conclusion was that it's really designed for efficiently writing fixed
structure with variable content, not variable structure.
I have the same feeling now
Post by Ken Harris
My first attempt was simply string concatenation on the result of
multiple with-html-output-to-string calls. It worked fine, but with
bigger inputs it became unusably slow due to all the consing.
A second option I considered was to slap a big ol' EVAL (or
MACROEXPAND? I can't remember, offhand) in there. This is a little
weird, since cl-who transforms the input sexp into code to efficiently
write it, but then I'm only using it once. Also, due to the weird
call structure (to-macro-and-back), I recall it being somewhat hard to
debug.
The thing I finally settled on was writing my own function that took
as input something that looked like cl-who's input, and wrote it out
to a stream. It was surprisingly simple to write (I think it was
10-15 lines, but I don't have it in front of me right now), though it
obviously doesn't handle every case cl-who does. It wouldn't handle
htm/str/txt or prologues or inline code or whatever -- I just pass in
a complete sexp, and it walks the tree and writes to a stream.
In your case, you'd just need to generate a value like (:strong
(:italic "text")) to pass in -- and Lisp tends to be pretty good at
letting you build lists. :-)
(I hooked it up to write to my web server's response stream directly,
and it screamed. Well, at least compared to the old "cons a few
gigabytes every request" method.)
I can dig up that function tonight if it sounds useful to you.
Yes, please, share it. I thought about such approach and was sure
someone already faced that problem and has a solution.

P.S. After some search I found htmlgen library. It seems to have built
in functions for generate html from s-expressions. Probably it better
suited for my use case.

Ala'a (cmo-0)
2009-11-23 19:26:06 UTC
Permalink
this is a my first shot. I do not know what is an s-tree, thus the
implementation of 'text-style'

(defmethod p-render-html ((s-tree tree) (p text-piece) stream)
(with-html-output (stream nil :prologue nil)
(let ((text (text-of p)))
(if (styled-p text-piece)
(styled-str text )
(str text)))))

(defun styled-p (text-piece)
(if (style-of text-piece) t nil))

(defun styled-str (text)
(dolist (style (text-styles text))
(setf text (apply-style style text)))
text) ;; or you can use recursive style

;; this is only for light testing
(defun text-styles (text)
(list 'italic 'bold))

;; you may change the internals to a macro (aka defstyle ) based for
example on a hastable of lambdas
;; rather than using and maintaining/updating ecase form manually
(defun apply-style (style text)
(with-output-to-string (out)
(with-html-output (out nil :prologue nil)
(ecase style
('italic (htm (:italic (str text))))
('bold (htm (:strong (str text))))))))

Regards,

Ala'a (cmo-0)
Post by Dmitry V'yal
Hello anyone,
I have a simple problem, but I can't find an elegant solution.
Basically I have a message and two flags, the first one signals message
should be rendered in <strong> tag and the second - in <italic>. So
there are four possibilities.
(defmethod p-render-html ((s-tree tree) (p text-piece) s)
  (with-html-output (s nil :prologue nil)
    (let ((style (style-of p))
          (txt (text-of p)))
      (if style
          (if (tree-find-prop s-tree style #'bold)
              (htm (:strong (if (tree-find-prop s-tree style #'italic)
                                (htm (:italic (str txt)))
                                (str txt))))
              (if (tree-find-prop s-tree style #'italic)
                  (htm (:italic (str txt)))
                  (str txt)))
          (str txt)))))
I had to test for each case and it resulted in code duplication. Can it
be avoided? What if I had 5 flags or so?
_______________________________________________
cl-who-devel site list
http://common-lisp.net/mailman/listinfo/cl-who-devel
--
It does not matter how fast your code is, if it does not work!
Loading...