Semi-Automatische Folien mit Groovy
Letzte Woche habe ich eine dreitägige Grails-Schulung gegeben. Ziel dieser Schulung war, dass die Teilnehmer einfache Webanwendungen nach der Schulung selbst schreiben können. Es ist wichtig, dass die Teilnehmer zum einen die Grundlagen von Grails verstehen und zum anderen wissen, wo und wie sie den Rest selbst herausfinden können. Zu den Grundlagen zählt z.B. das Wissen um das Model-View-Controller-Pattern (MVC) und wie man es in Grails benutzt, aber auch natürlich Groovy, denn in Grails macht man in der Regel häufig Gebrauch davon. Spätestens, wenn man mehr Informationen über Grails haben möchte, hilft die Sourcecodeexegese, wobei man hier an Groovy nicht mehr vorbeikommt. Daher gliedert sich die Schulung auch in einen Teil Groovy (der erste Tag) und einen Teil Grails (die verbleibenden zwei Tage).
Natürlich kann man an einem Tag nur einen kleinen Teil von Groovy vermitteln, etwa die häufigsten Sprachfeatures wie Closures und Operator-Overloading sowie Teile des GDKs, also der Groovy-API. In der Vorbereitung dieser Schulung habe ich also Grails und Grailsanwendungen durchforstet nach Groovy-Eigenschaften, die der gemeine Grailsentwickler kennen sollte. Und das ist eine ganze Menge - zumindest, wenn man mehr machen möchte als Scaffolding ;-)
Wie vermittelt man das notwendige Wissen um Groovy, wenn man als Teilnehmer Tag 2 und 3 der Schulung überstehen möchte? Ein Weg wäre, einen reinen Theorievortrag zu halten und die Groovy-Spec quasi in Folien zu gießen - aber das wäre langweilig, für mich als Trainer, beim Vorbereiten der Schulung und dem Vortragen der Folien, und für die Teilnehmer sowieso. Außerdem hat das wenig Praxisbezug, was die Transferleistung erhöht, das Gelernte in Grails umzusetzen.
Dierk König und Co-Autoren machen gute Wissensvermittlung in GinA vor: Beispiele noch und nöcher! Daran habe ich mir ein Beispiel genommen und die Präsentation für Tag 1 fast ausschließlich über kleine und überschaubare Codeschnipsel gestaltet. Dabei steht auf den Folien außer der Folie kein weiterer Text, nur Code. Was es zu dem jeweiligen Beispiel zu sagen gab, habe ich gesagt. So sind etwa 90 Folien mit Code zustandegekommen, die zusammen mit Übungen den ersten Tag der Schulung in einen Workshop verwandelten. So sieht eine Beispielfolie aus:
Der Haken ist der, dass der Code sich ändern kann! So aus dem Bauch heraus würde ich schätzen, dass ich Foliencode pro Folie etwa drei Mal ändere im Laufe der Vorbereitung einer Präsentation. Bald kommt Groovy 1.1 raus, dann gibt's auch wieder haufenweise Verbesserungen, die ich in den Folien ändern muss. Und schließlich mache ich Fehler, und ein Reviewer (oder aufmerksamer Schulungs-Teilnehmer) hat immer Korrektur- und Verbesserungsvorschläge.
Änderungen ziehen mit jenem manuellen Verfahren Probleme nach sich: Wie halte ich die Folien synchron zu den Dateien mit den Codeschnipseln? Nur manuell, und damit wird's stupide und fehleranfällig: Ich möchte an einer Folie etwas im Code ändern, also suche ich mir die entsprechende Datei raus, ändere und überprüfe den Code in der Datei und dann copy-paste ich den Code wieder in die Folie.
Nee, das macht keinen Spaß, ist unökonomisch (die vielen manuellen Schritte, immer und immer wieder), fehleranfällig (was dabei alles schief gehen kann) und überhaupt total ungroovy. Ich möchte die Synchronisation automatisieren. Außerdem möchte ich die Schnipsel automatisiert testen können. Und schließlich möchte ich die Schnipsel klein halten: Als brauchbar haben sich maximal 55 Zeichen pro Zeile und maximal 15 Zeilen pro Folie erwiesen. Da läuft man ohne Hilfe schnell aus dem Folienrahmen, aber merkt das erst nach dem Copy-Paste-Vorgang, also zu spät. Das ist vielleicht ein Gefummel, kann ich Euch sagen!
Wie automatisiere ich das nun? Naiver erster Versuch: Textdatei mit Groovy-Skript in Kraftpunkt als Verknüpfung referenzieren. Geht aber nicht, denn Kraftpunkt kann den Text nur importieren, was den Bezug zur Datei verloren gehen läßt. OpenOffice ist da keinen Deut besser. Wenn's doch gehen sollte, hab ich's nicht hinbekommen. Und außerdem gibt's mit diesem Verfahren kein Syntaxhighlighting, und ich will niemandem 90 Folien voll Code ohne Farben zumuten.
Ich hab's dann doch geschafft, und so sieht der Weg aus:
- Ich erstelle ein normales Groovyscript mit einem Codeschnipsel drin, z.B. für die Fälle bei der Groovy-Truth.
- Dann lasse ich ein Magic-Build-Groovy-Skript laufen und erhalte eine Grafik im PNG-Format, die den gleichen Namen hat wie das entsprechende Skript.
- Diese Grafik verknüpfe ich in Kraftpunkt. Fertig.
Wenn ich jetzt den Codeschnipsel ändern möchte, dann ändere ich das entsprechende Groovy-Skript, werfe den Magic-Build-Vorgang an und mehr nicht: Kraftpunkt merkt die Veränderung und aktualisiert die Folien entsprechend.
Aber so richtig interessant wird's, wenn man bedenkt, was da eigentlich passiert: Das Magic-Build-Groovy-Skript durchforstet ein Verzeichnis nach Groovy-Dateien, den Codeschnipseln aus der Folie. Diese werden gelesen, volumengeprüft, übersetzt und evaluiert, also intern übersetzt. Hätten meine Schnipsel zu lange Zeilen oder zuviele davon, dann würde ich das bei der Methodenprüfung merken. Gäbe es Compile-Fehler, dann würde ich das beim Übersetzen merken. Wären meine Inlinetests (die vielen
assert
s) kaputt, dann würde ich das beim Evaluieren merken. Rundumsorglos abgesichert :-) Schließlich werden aus den Textdateien png-Grafiken generiert und diese in ein anderes Verzeichnis geschrieben.Wie's funktioniert zeigt die Power von Groovy: das Lesen der Dateien ist ein simples
new File('pfad/zum/skript.groovy').text
. Die Volumenprüfung erfolgt ebenfalls im Skript mit Groovy (ein paar mehr Codezeilen, aber sehr überschaubar). Das übersetzen und evaluieren schafft man wieder mit einem Einzeiler:evaluate(skriptAlsString)
Den Teil mit dem Bildgenerieren habe ich dann wieder in Java geschrieben und vom Magic-Build-Groovy-Skript aus aufgerufen. Beim Bildermalen gibt's fast keine Logik, sondern nur viele API-Calls gegen Javas AWT, wobei Codecompletion doch recht praktisch ist, was in Java eben noch besser geht als in Groovy. Toll ist, dass ich einfach wechseln kann, wenn ich das möchte, dank Groovy und dessen grandioser Interaktion mit Java.
Das Syntaxhighlighting erfolgt auch noch in Java (selbstgeschrieben), aber das möchte ich nach Groovy portieren vor der nächsten Schulung - es wäre dort einfacher zu schreiben und weiterzuentwickeln. Da ich von Java aus auch Groovy aufrufen kann, mache ich mir auch keine Sorgen, dass ich damit Probleme bekommen könnte :-)
Der aufmerksame Leser wird auf der Folie weiter oben diese Zeile bemerkt haben:
assert new Bar() && !null
Wo kommt denn da das
Bar
her? Wie kann denn das übersetzt werden? Das passiert mit einem kleinen Trick. Das Skript hierzu sieht so aus:assert new Bar() && !null
/* ### */
class Bar {}
Bei der Evaluierung wird das komplette Skript betrachtet. Daher gibt's keine Fehler aufgrund desssen, dass die Klasse
Bar
nicht hätte gefunden werden können. Die drei Rauten (###
) geben dem Skript allerdings an, diesen Kommentar und alles Nachfolgende nicht in das Bild zu schreiben. Ähnliches gibt's auch mit drei Pfeilen nach oben (^^^
), was mir Code vor dem Skript aus Bildern ausblendet. So einfach ist ein SetUp und TearDown selbstgeschrieben.Der Nachteil bei diesem Verfahren ist, dass die Teilnehmer keinen Code aus den Folien kopieren können, um ihn mal eben in der GroovyConsole auszuprobieren. Da die Codeschnipsel aber auch nicht groß sind, ist das nicht so schlimm. Schlimmer finde ich, dass die Teilnehmer nicht digital suchen können in den Folien: es sind eben Bilder, kein Text.
Trotzdem, diese Nachteile überwiegen bei Weitem nicht die Vorteile, die sich mit diesem Verfahren ergeben: automatische Synchronisation, automatischer Test (Übersetzen und Inline-Tests), Syntaxhighlighting und Volumenprüfung.
Ausbaufähig wäre das ganze allerdings noch: Ich muss immer noch nach dem entsprechenden Groovy-Skript für eine Folie suchen. Dabei behelfe ich mir zwar momentan mit einer Namenskonvention, aber das Wahre ist das auch noch nicht (es sei denn, man hätte ein automatisches Rename-Refactoring für Skripte und Folien - ich bin aber auch verwöhnt... ;-) ) Und eine neue Folie muss ich immer noch selbst anlegen mit einem Bild und einer Überschrift.
Ich hab mich in Groovys Scriptom immer noch nicht eingearbeitet (siehe auch GinA S. 546ff.), aber ich hoffe, das Folgende damit tun zu können:
- Zu einem Verzeichnis mit png-Bildern wird pro Bild eine Folie mit einem bestimmten Layout angelegt. In diese Folie wird das Bild eingetragen als Verknüpfung und der Name des Bildes als Überschrift (CamelCase.png wird dann zur Überschrift CAMEL CASE).
- Gibt es eine Überschrift, die nicht zu einer Bilddatei passt, dann wird die Folie gelöscht.
- Gibt es eine Bilddatei, die nicht zu einer Überschrift gehört, dann wird eine neue Folie angelegt.
- Gibt es genau eine Folie, zu der es keine Bilddatei gibt, und gibt es genau eine Bilddatei, zu der es keine Folie gibt, dann wird an der Stelle der bilddateilosen Folie eine neue Folie mit dem folienlosen Bild erzeugt. (Rename-Refactoring!!).
Bin mal gespannt, ob das mit Scriptom zu schaffen ist. Bin dankbar für jeden hilfreichen Hinweis, wie man mit Scriptom Kraftpunkt-Präsentationen manipuliert.