[Prev][Next][Index][Thread]

Re: Generating HTML with Dylan




Hello Chris,
   I've not played around with dylan and html for some time as the lack of
updates at dylanworld.com show, but at one point I worked on a few
different approaches of producing HTML with Dylan and recently with many
other languages. Personally, the best approach I came up with was to not
produce a DOM inside my code, but to keep all of the HTML in html files
along with dylan DOM objects. So, I'd have something like

<HTML>
<HEAD><TITLE>Hello</TITLE></HEAD>
<BODY>

<DYLAN>
//dylan code here
write-html("Hello World");


</DYLAN>
</BODY>
</HTML>

and let my dylan app parse this file and interpret the <DYLAN>
object. This was only done for very simple cases a few years ago, so it
never really saw much daylight. In some ways, I think XML might provide a
nice bridge for dylan. You could create a dylan environment with something
like

<DYLAN>
<LIBRARY name="myWebAPP">
<USE name="Dylan"\>
<USE name="Format"\>
</LIBRARY>
<MODULE name"myWebAppModule">
<USE name="Common-Dylan"\>
<USE name="Format"\>
</MODULE>


format(*standard-output*, "Hello");

</DYLAN>

Kinda funky, I know, but in some ways I think this approach allows for
easier dropping in of programming code into pre-made html documents, which
is pretty essential in a team environment.


Just some quick thoughts,
Brad Dominy



On 14 Jun 2001, Chris Double wrote:

> I've been playing around with ways of generating HTML and serving web
> pages from Dylan. 
> 
> I've always liked the HTMLGEN approach of AllegroServe (a Common Lisp
> web server) when it comes to generating HTML from Lisp. It's a simple
> s-expression based method. where your lisp html looks something like:
> 
>   (html-stream p 
>     (:html
>       (:head (:title "Test Table"))
>       (:body 
>         ((:table border 2)
>           (dotimes (i count)
>             (html (:tr (:td (:princ i))
>                   (:td (:princ (* i i))))))))))))
> 
> HTML is generated from this and parsed to a stream. 
> 
> With Dylan I played around with things like:
> 
>   with-html()
>     with-head()
>       with-title()
>          .. etc ..
> 
> But things get verbose and a bit tedious. I also tried:
> 
>   #(#"html",
>      #(#"head",
>        #(#"title, "Test Table"),
>        #(... etc...)
> 
> This was still a tad harder to read (for me) than the lisp version and
> I preferred the generation of some sort of DOM (Document Object Model)
> structure which could then be translated into HTML, XML or
> whatever. The tidiest approach (for a definition of 'tidy' that suits
> me of course) ended up with code that looks like:
>  
>    with-dom-builder ()
>        (html
>          (head (title ["Test Page"])),
>          (body
>            (p ["The sum of 1 and 2 is "], [1 + 2]),
>            (p 
>               ["Test this link: "],
>               ((a href: "http://www.double.co.nz") ["My website"]))))
>    end;
> 
> with-dom-builder being a macro. All expressions in square brackets
> being evaluated, converted to a string. This macro results in a DOM
> being created with objects like <node-element>, <text-element>,
> <attribute>, etc. This is passed to a print-html method which
> traverses the DOM and sends HTML to a stream.
> 
> It seems to do most of what the lisp version does. For example, nested
> invocation and closing over method arguments:
> 
> -----------------8<------------------
>  define method simple-table(count, border-width, backg-color, border-color)
>   with-dom-builder()
>     (html
>       (head (title ["Test Table"])),
>       (body
>         ((table border: border-width,
>                 bordercolor: border-color,
>                 bgcolor: backg-color,
>                 cellpadding: 3)
>           (tr ((td bgcolor: "blue")
>                ((font color: "white", size: "+1")
>                 ["Value"])),
>               ((td bgcolor: "blue")
>                 ((font color: "white", size: "+1")
>                  ["Square"]))),
>           [for(n from 0 below count)
>              with-dom-builder(*current-dom-element*)
>                (tr (td [n]), (td [n * n]))             
>              end;
>            end for])))
>   end with-dom-builder;
>  end method simple-table;   
> 
>   with-open-file(fs = "d:\\test-table.html", direction: #"output")
>     print-html(simple-table(10, 3, "silver", "blue"), fs);
>   end;
> -----------------8<------------------
> 
> I'm interested in approaches others have come up with and any comments
> on the above...too ugly or lispy for Dylan maybe? The advantages are
> it's nice and concise.
> 
> The code for this is in dylanlibs CVS repository (not in a release
> yet, you'll have to check it out of CVS as I haven't finalised it yet)
> in the dom-builder library. This library has the macro for the above,
> a simple DOM and a HTML generator for the DOM.
> 
> Ideally I'd like to use a full featured DOM like Scott McKay's Dylan
> DOM and HTML stuff. I'll probably move to that if I keep going down
> this path and they become available in some manner.
> 
> An example of use is in the beginnings of an http-server,also
> available in Dylanlibs CVS, as http-server. This is a bit of a hack of
> an http-server I threw together a couple of years ago - but it least
> it serves simple dynamic pages and I hope to expand on it later. It
> demonstrates some of using the macro with-dom-builder anyway:
> 
> -------------------8<-----------------
> define method display-header-handler(server :: <simple-http-server>,
>                            request-type :: <symbol>,
>                            requested-path :: <string>,
>                            headers :: <string-table>,
>                            stream :: <stream>)
>  => ()
>   with-standard-http-result(stream)
>     print-html(with-dom-builder()
>                  (html
>                    (head (title ["Headers"])),
>                     (body
>                       ((table border: 1)
>                         (tr (td ["Header"]), (td ["Value"])),
>                         [for(v keyed-by k in headers)
>                            with-dom-builder(*current-dom-element*)
>                              (tr (td [k]), (td [v]))
>                            end with-dom-builder;
>                          end for])))
>                end, stream);
>     write(stream, "\r\n");
>   end;
> end method;
> 
> define method handler-not-found(server :: <simple-http-server>,
>                                 request-type :: <symbol>,
>                                 requested-path :: <string>,
>                                 headers :: <string-table>,
>                                 stream :: <stream>)
>  => ()
>   let dom = 
>     with-dom-builder()
>       (html
>         (head (title ["File not found"])),
>         (body 
>           (p ["I could not find the file "],
>              [requested-path],
>              [" on this server."])))          
>     end with-dom-builder;
>   print-html(dom, stream);
> end method handler-not-found;
> -------------------8<-----------------
> 
> Since there is no example of using the http-server yet, here is some
> sample code to do it if any brave souls want to try. It's pretty
> incomplete at this stage though.
> 
> -------------8<------------------------
> define method index(server :: <simple-http-server>,
>                     request-type :: <symbol>,
>                     requested-path :: <string>,
>                     headers :: <string-table>,
>                     stream :: <stream>)
>  => ()
>   with-standard-http-result(stream)
>     let dom = 
>       with-dom-builder()
>         (html
>           (head
>             (title ["Test HTTP Server"])),
>           (body
>             (p ["Testing the Dylan HTTP Server."]),
>             (p ["One, two three."])))
>       end;
>     print-html(dom, stream);
>   end;
> end method index;
> 
> define method main () => ()
>   format-out("Starting...\n");
>   block()
>     initialize-http-server();
>     let server = make(<threaded-http-server>);
>     publish-dynamic-handler(server, "/quit", quit-handler);
>     publish-dynamic-handler(server, "/headers", display-header-handler);
>     publish-dynamic-handler(server, "/", index);
>     start-http-server(server, port: 8000);
>   cleanup
>     format-out("Stopping...\n");
>   end;
> end method main;
> 
> begin
>   main();
> end;
> -------------8<------------------------
> 
> The libraries are functional developer only at this stage btw. There
> is no real reason that dom-builder can't work under Gwydion Dylan asit
> uses common-dylan. But it does use dynamic-bind and a thread
> variable. Taking these out and it almost compiled with GD so there is
> hope and I'll eventually head that way.
> 
> Chris.
> -- 
> http://www.double.co.nz/dylan
> 
> 




References: