[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: