The Yesod Framework

Ein Webrahmenwerk in Haskell

Templates

Templatesysteme und Hamlet

Templatesysteme, auch Template-Engines genannt, verarbeiten Zeichenketten unterschiedlicher Formate, indem sie darin enthaltene Platzhalter mit für den Verarbeitungsprozess übergebenen Werten ersetzen. Auf diese Weise können Programmcode und Markup konzeptuell und praktisch voneinander getrennt werden.[1]

Yesod verwendet zwecks Nutzung von Templates das Paket Hamlet. Dieses Paket umfasst die Templatesprachen Hamlet für HTML, Cassius für CSS und Julius für JavaScript. Für CSS-Auszeichnungen steht zusätzlich eine weitere Templatesprache mit dem Namen Lucius zur Verfügung. In diesem Kapitel werden sowohl die Verwendung dieser Templatesprachen, als auch deren Syntax und Möglichkeiten anhand von praktischen Beispielen erklärt.

Konzept von Hamlet zur Generierung von HTML

Bei Hamlet handelt es sich um das standardmäßig von Yesod verwendete Paket zur Anwendung von Templates. Der Ansatz der darin beschriebenen Templatesprache mit dem gleichen Namen ist, sämtliche Redundanzen aus HTML-Auszeichnungen zu entfernen, gleichzeitig jedoch keine komplett neue Syntax einzuführen, welche erst zu erlernen ist. Grob zusammengefasst lässt sich sagen, dass Hamlet eine zu HTML ähnliche Templatesprache ist, bei der die Struktur des Dokumentbaums durch den Grad der Einrückung bestimmt wird. Ein hervorragender Nebeneffekt dieser Konventionen ist die Tatsache, dass ein erheblicher Anteil von ungültigem HTML auf diese Weise von Hamlet nicht generierbar ist. Beispielsweise wäre die Erzeugung des nicht wohlgeformten HTML-Code-Fragments <p><strong>Text</p></strong> demnach mit Hamlet nicht möglich. Die Gültigkeit von HTML-Bestandteilen wird in diesem Zusammenhang jedoch nicht geprüft. Für einen solchen Mechanismus wäre es notwendig, den gesamten Kontext eines HTML-Dokumentbaums zu erfassen und nicht nur das Fragment.

Es folgt ein Beispiel für Templatecode, welcher von Hamlet interpretiert werden kann.

1<div #an-id style=width:500px;>
2    <a href=@{MyDest}>I'm a link!
3    <ul .a-class>
4        $forall p <- people
5            <li>#{familyName p}, #{firstName p}

Attributwerte müssen innerhalb von Hamlet nicht in Anführungszeichen gesetzt werden. Im generierten HTML-Markup geschieht dies zugunsten wohlgeformter HTML-Dokumente jedoch immer. Die Angabe von ID-Kennungen oder Klassenzugehörigkeiten kann angelehnt an die CSS-Entsprechungen abgekürzt erfolgen. Das Dollarzeichen leitet eine Kontrollstruktur ein.[2]

Interpolationen

Fundamentaler Mechanismus von Hamlet sind die sogenannten Interpolationen. Eine solche wird durch das Interpolationssymbol, gefolgt von einem Parameter, welcher in geschweiften Klammern eingeschlossen ist, gekennzeichnet. Interpolationen sind mit Umwandlungen gleichzusetzen, bei denen Platzhalter typsicher durch entsprechende Inhalte ersetzt werden. Je nach Anwendungszweck stehen drei unterschiedliche Interpolationsarten zur Verfügung. Um Variablen einzubinden, dient die Variableninterpolation, welche mit dem Rautensymbol # eingeleitet wird. Zwei Beispiele für eine solche Umwandlung finden sich im Codeausschnitt in der fünften Zeile. Alle auf diese Art in die Templates eingebundenen Daten werden im Zuge der Verarbeitung automatisch escaped, was Cross-Site-Scripting-Attacken entgegen wirkt.

In Zeile 2 erfolgt zudem eine so genannte URL-Interpolation. Hierbei erfolgt ein Verweis auf eine Adresse, welche beispielsweise im Rahmen des Routingsystems definiert wurde und anhand eines Haskell-Werts und einer Renderfunktion in einen String konvertiert werden kann. Auf diese Weise muss bei einer Änderung der Adresse nicht jeder einzelne Verweis angepasst werden. URL-Umwandlungen werden durch das At-Zeichen @ gekennzeichnet.

Neben diesen beiden Umwandlungsarten existiert zudem noch die Möglichkeit der Einbettung von weiteren Templates, so dass häufig verwendete HTML-Bestandteile entsprechend in eigene Templatedateien ausgelagert werden können. Die Einbettung von Subtemplates wird in Hamlet mithilfe des Einfügezeichens ^ eingeleitet.

Cassius unterstützt sowohl Variablen-, als auch die URL-Interpolation. Julius interpretiert neben diesen beiden Interpolationsarten zudem auch die Einbettung weiterer Templates.[3]

Syntax von Cassius und Julius

Das folgende Beispiel zeigt eine Stylesheet-Definition über die Templatesprache Cassius.

1a
2    color: darkgreen
3h1
4    border-bottom: 1px solid grey

Wie zu sehen ist, erfolgt auch bei Cassius die Definition der Struktur über den Grad der Einrückung. Julius dient im Gegensatz zu Hamlet und Cassius dazu, den unverarbeiteten JavaScript-Code schlicht an Yesod durchzureichen. Somit sind bei Julius beispielsweise auch die Whitespaces nicht signifikant.[4]

Lucius

Mit Lucius existiert zusätzlich noch eine weitere Templatesprache, welche es ermöglicht, CSS-Beschreibungen ineinander zu schachteln. So würde die Lucius-Beschreibung

1foo, bar {
2    baz, bin {
3        color: red;
4    }
5}

in die CSS-Repräsentation

1foo baz, foo bin, bar baz, bar bin {
2    color: red;
3}

umgewandelt werden.[5]

Anwendung von Templates

Bereits im vorigen Kapitel kam Hamlet bei der Betrachtung des einleitenden Beispiels zur Anwendung. An einem weiteren Beispiel soll die Funktionsweise der Templatesysteme verdeutlicht werden.

1    let name = "Michael" :: String
2    let nameId = "name" :: String
3
4    addHamlet [hamlet|
5<h1>Hello World!
6<p>
7    Welcome to my system. Your name is #
8    <span ##{nameId}>#{reverse $ take 2 $ reverse name}
9    . Enjoy your stay!
10<p
11    <a href=@{BlogR "einstein" "relativity"}>general relativity
12|]

Zu Beginn werden zwei Variablen definiert, welche aus dem Template heraus genutzt werden sollen. In Zeile 4 kommt dann eine so genannte Quasi-Quotation zur Anwendung. Dabei wird die Zeichenkette auf der rechten Seite des Trennstriches von dem auf der linken Seite des Trennstriches angegebenen Quoter interpretiert und im Zuge der Kompilierung in Haskell-Code umgesetzt. Die Funktion addHamlet sorgt dann für die entsprechende Speicherung des HTML-Fragments zur späteren Ausgabe an den Client. Der Mechanismus entstammt dem Konzept der Metaprogrammierung mit «Template Haskell», zu welchem ein eigenständiger Seminarbeitrag entstanden ist. Anhand der Quasi-Quotations lassen sich die beschriebenen Templates einbinden. In Zeile 8 werden zusätzlich zwei Variablen interpoliert. Dabei steht die erste Variable für die ID-Kennung des <span>-Tags, wodurch sich das doppelte Rautensymbol erklärt. Bei der zweiten Variable werden mithilfe von Funktionen aus Haskell lediglich die letzten beiden Zeichen ausgegeben. Eine URL-Interpolation findet sich in Zeile 11. In diesem Fall werden zudem zwei Argumente an die Route übergeben. Die entsprechende Definition der Route könnte beispielsweise wie folgt aussehen.

1mkYesod "RouteExample" [parseRoutes|
2/ HomeR GET
3/blog/#Author/#Title BlogR GET
4|]

In der Praxis wird man die Templates bevorzugt auslagern und entsprechend von einer statischen Quelle laden. Dies kann mithilfe der Funktion hamletFile bzw. cassiusFile oder juliusFile unter der Angabe des Dateipfades erfolgen. Zumeist wird der Einbindung eines Templates auch eine Funktion vorangestellt, welche den generierten HTML-Code in ein bestehendes Dokumentgerüst einbettet. In einem für Yesod neu generierten Projekt ist zu diesem Zweck standardmäßig eine Funktion defaultLayout vordefiniert, welche entsprechend der eigenen Vorstellungen angepasst werden kann.[6][7]