Do you Lisp?
"Do you Lisp?" ist ein Framework, das die Entwicklung der serverseitigen Komponente einer AJAX Applikation beschleunigen soll. Das Framework verwendet das dojo Toolkit (Version 0.4.0) auf der Clientseite und Lisp und HTML Templates auf der Serverseite.
Das Framework selbst ist in Java geschrieben. Es ist als Open Source veröffentlicht.
Funktionsweise
Das Framework verwendet das Model View Controller Pattern.
Das Model wird durch das Framework nicht direkt unterstützt, jedoch ist die Einbindung von geeigneten Bibliotheken (siehe z.B. Lisp-Database-Package) einfach.
Als View werden HTML Templates verwendet. Diese werden vom Html-Package bereitgestellt, ihre Funktionsweise ist in dessen Dokumentation erläutert.
Die Control-Komponente wird durch Libraries und Actions realisiert. Es handelt sich dabei um Dateien, die Scriptcode einer Domain Specific Language enthalten.
Actions können per URL vom Browser aufgerufen werden und erzeugen entweder das HTML für eine komplette Webseite oder JavaScript, mit dem die aktuell vom Browser angezeigte Seite verändert werden kann oder JavaScript Objekte als JSON.
Mit Libraries kann der von mehreren Actions gemeinsam verwendete Scriptcode organisiert werden.
Das Framework kann wie folgt gestartet werden:
java -jar lispdojo.jar <HTTP Port> <Webserver Root Verzeichnis> <Verzeichnis für Templates> <Verzeichnis für Actions und Libraries>
Scriptcode
Die Control-Komponente des Frameworks nutzt einen Lisp-Dialekt (siehe Lisp und Lisp-Package), der um folgende Funktionen erweitert ist:
(dojo-link url alist) erzeugt JavaScript, das als href in einen Link eingesetzt werden kann. Wird der Link im Browser geklickt, wird die URL url (eine Zeichenkette) mit den Argumenten aus der Assoziationsliste alist aufgerufen. Die Antwort des Servers muss JavaScript sein. Es wird vom Browser ausgeführt, um die angezeigte Seite zu modifizieren.
(classic-link url alist) erzeugt eine URL, die als href in einen Link eingesetzt werden kann. Wird der Link im Browser geklickt, wird die URL url (eine Zeichenkette) mit den Argumenten aus der Assoziationsliste alist aufgerufen. Die Antwort des Servers muss eine Seite oder Datei sein.
(html-from-template template-name alist) erzeugt HTML, indem das durch den Namen template-name (eine Zeichenkette) referenzierte Template mit den Argumenten aus der Assoziationsliste alist instanziert wird.
(replace-node node-id html) erzeugt JavaScript, das das innerHTML des DOM Nodes mit der Id node-id (ein Symbol) durch das HTML aus html (eine Zeichenkette) ersetzt, wenn es im Browser ausgeführt wird.
(replace-node-from-template node-id template-name alist) ist die Verkettung von replace-node und html-from-template:
(setq replace-node-from-template
(lambda (node-id template-name alist)
(replace-node
node-id
(html-from-template template-name alist))))
(get-valueq name alist) ermittelt den Wert des Arguments mit Namen name (ein Symbol, wird nicht ausgewertet) aus der Assoziationsliste alist. Diese Funktion ist hilfreich, um die Argumente aus einem HTTP Request zu extrahieren.
(get-property-list plist-name) liest die Property List mit dem Namen plist-name.
Libraries und Actions
Der Scriptcode ist in Libraries und Actions organisiert, die wiederum in XML Dateien abgelegt sind.
Das XML einer Library hat folgende Struktur:
<library>
<requires-library>... Name einer Library, die von dieser Library verwendet wird ...</requires-library>*
<provides>... S-Expression, die eine Hilfsfunktion deklariert, die von dieser Library exportiert wird ...</provides>*
</library>
Das XML einer Action hat folgende Struktur:
<action>
<requires-library>... Name einer Library, die von dieser Action verwendet wird ...</requires-library>*
<function>... Funktion, die den Request als Assoziationsliste entgegen nimmt und HTML oder JavaScript erzeugt ...</function>
</action>
Wenn eine Action aktiviert wird, dann wird vom Framework sichergestellt, dass alle benötigten Libraries geladen werden.
Die im HTTP Request enthaltenen Argumente werden als Assoziationsliste an die Action-Funktion übergeben.
Die Action-Funktion wird in einer Umgebung ausgewertet, die exklusiv für die HTTP Session bereitsteht. Wenn die Funktion Variablenwerte in der Umgebung verändert, dann stehen diese Variablenwerte den später in der gleichen Session aktivierten Actions zur Verfügung.
Änderungen an Libraries, Actions und Templates werden unmittelbar beim nächsten Aufruf berücksichtigt (Hot Deployment).
Es gibt zwei Arten von Actions: Pages und Scripts.
Pages
Dateien mit der Endung .page enthalten Actions, die HTML erzeugen. Sie können durch eine URL unter ihrem Dateinamen aufgerufen werden.
<action>
<function>
(lambda (request)
(html-from-template "/index.html" nil))
</function>
</action>
Beispiel 1: die Page-Action index.page
Aufrufen einer Action bedeutet immer, dass die Action-Funktion (eingeschlossen in die function-Tags) mit einer Liste von Name-Wert-Paaren aktiviert wird. Die Liste enthält die Argumente aus dem HTTP-Request.
Von der Action-Funktion wird erwartet, dass sie eine HTML-Seite als Ergebnis liefert. Am einfachsten lässt sich das mit der Framework-Funktion html-from-template erreichen. Diese Framework-Funktion erwartet den Pfad zu einem HTML-Template relativ zum Template-Verzeichnis des Frameworks und eine Liste von Name-Wert-Paaren. Mit der Liste können die im Template vorzunehmenden Ersetzungen festgelegt werden. Im Beispiel 1 wird also einfach der Inhalt des Templates index.html ohne weitere Ersetzungen an den Browser übermittelt.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Hello</title>
<script type="text/javascript" src="/dojo-0.4.0-ajax/dojo.js"></script>
<script language="JavaScript" type="text/javascript">dojo.require("dojo.io.*");</script>
</head>
<body>
<div id="content">
<div id="message">Please enter your name.</div>
<form id="form-1">
Firstname<br />
<input type="text" name="firstname" /><br />
Lastname<br />
<input type="text" name="lastname" /><br/>
<br />
<a href="javascript:void dojo.io.bind({preventCache:true, formNode:dojo.byId("form-1"), url:"submit.script", mimetype:"text/javascript"})">Click here!</a>
</form>
</div>
</body>
</html>
Beispiel 2: das Template index.html
Wenn das Template aus Beispiel 2 als HTML-Seite im Browser angezeigt wird, dann kann der Nutzer seinen Vor- und Nachnamen eingeben. Der Klick auf den Aktionslink "Click here!" ruft die Funktion dojo.io.bind aus dem dojo Toolkit auf. Diese Funktion packt die Werte aus dem Formular mit Vor- und Nachnamen zu einem asynchronen HTTP-Request zusammen und sendet ihn an den Server. Dort wird der Request durch eine Script-Action verarbeitet. Bei der Response der Script-Action handelt es sich um JavaScript, das von dojo.io.bind ausgeführt wird, sobald es beim Browser eintrifft.
Scripts
Dateien mit der Endung .script enthalten Script-Actions, die JavaScript erzeugen. Sie können durch eine URL unter ihrem Dateinamen aufgerufen werden.
<action>
<function>
(lambda (request)
(let
((firstname (get-valueq firstname request))
(lastname (get-valueq lastname request)))
(cond
((or (null? firstname) (less? (string-length firstname) 1))
(list (replace-node (quote message) "Please enter your firstname.")))
((or (null? lastname) (less? (string-length lastname) 1))
(list (replace-node (quote message) "Please enter your lastname.")))
((list (replace-node-from-template (quote content) "/ok.html" request))))))
</function>
</action>
Beispiel 3: die Script-Action sumbit.script
Das oben gezeigte Beispiel für eine Script-Action ermittelt mit get-valueq die Werte in den beiden Eingabefeldern. Die Werte werden anschließend überprüft. Wenn die Werte nicht zulässig sind, sorgt die Script-Action mit replace-node dafür, dass der Node message durch eine passende Meldung ersetzt wird. Sind die Werte akzeptabel, ersetzt das Script mit replace-node-from-template den Node content durch eine Instanz des Templates ok.html.
Hello <!-- insert type="text" name="firstname" /--> <!-- insert type="text" name="lastname" /-->!
Beispiel 4: das Template ok.html
Beim Instanzieren des Templates werden die Lücken firstname und lastname durch die Werte aus den Eingabefeldern ersetzt.
Objects
Dateien mit der Endung .object enthalten Actions, die Objekte in JSON erzeugen. Sie können durch eine URL unter ihrem Dateinamen aufgerufen werden.
Beispiele
Die Anwendung des Frameworks kann an der Demoanwendung Do you Lisp Wiki studiert werden.
Download
Die Quelltexte unterliegen der Modified Artistic License v1 finden sich in der Version v5 in der Jar-Datei unter dieser URL: http://www.qrst.de/downloads/lispdojosrc.jar
Zusätzlich werden noch das Collections-Package, das Html-Package, das Http-Package, das Lisp-Package, das Property-Set-Package, das Threads-Package und das XML-Utils-Package benötigt.