TwitterGoogle

Vorstellung der Diff, Match and Patch Bibliothek

Bisher habe ich auf wikihost.org das diff-Tool der Linux-Shell verwendet, um den Anwendern eines Wikis die Möglichkeit zu geben, zwei unterschiedliche Versionen einer Wiki-Seite hinsichtlich ihrer Unterschiede zu vergleichen. Dabei wurden einfach die beiden Wiki-Sources temporär als Datei weggeschrieben, das externe diff aufgerufen und der Output wieder eingelesen und interpretiert.
Dieser Ansatz hatte einige Nachteile, wie z.B. das Unvermögen mit Unicode-Zeichen umzugehen, die Verarbeitungsgeschwindigkeit und eine unschöne Darstellung.

Aus genannten Gründen war ich daher auf der Suche nach einer vernünftigen, integrierten Lösung. Gefunden habe ich eine sehr schöne für mehrere Sprachen entwickelte Bibliothek.

Google Diff, Match and Patch

Die diff, match and patch-Bibliothek von Neil Fraser stellt  identische Funktionen für Java,  JavaScript, Dart, C++, C#, Objective C, Lua und Python bereit. Sie wird auf code.google.com gehostet und unter der Apache License 2.0 bereitgestellt.

Für wikihost.org ist die Java-Bibliothek relevant, daher beziehe ich mich hier ausschließlich darauf. Wikihost.org ist in Grails/Groovy entwickelt, daher zeige ich auch kurz, wie die Java-Bibliothek dort einzubinden ist.

 

Bibliothek ins Grails-Projekt aufnehmen

Damit die Java-Klassen der diff-Bibliothek in der Grails-Anwendung genutzt werden können, muss das Java-File einfach in das Verzeichnis <PROJECT>/src/java kopiert werden.

Hier die Projekt-Struktur des wikihost.org Grails-Projekts mit eingebundener diff-Bibliothek:

 

  Diff Project-157x300 in

 

Ist das erledigt, kann man auch schon beginnen, die diff-Funktionalität in einem der Controller zu nutzen.

Diff in Grails ermitteln

Hier der Code, wie ich ihn zum Vergleich der Revisionen einer Wiki-Seite nutze:

//Initialisiere diff-Bibliothek
def dmp = new diffy.diff_match_patch()
//Vergleiche alte und neue Version
def diffed = dmp.diff_main(oldRevText,newRevText)
//mache für Menschen lesbar (optimierte Darstellung)
dmp.diff_cleanupSemantic(diffed)
//als HTML ausgeben (färbe gelöschte Teile rot, Hinzugefügte grün)
def TextHtml = dmp.diff_prettyHtml(diffed)

 

Zuerst wird eine Instanz der Klasse diff_match_patch initiiert. Den Package-Namen diffy habe ich in der Klasse definiert, damit ich leichter erkenne, wo diese Klasse herkommt. Der eigentliche Vergleich zwischen oldRevText und newRevText findet dann in der zweiten Zeile durch Aufruf der Methode diff_main() statt. Da die Differenz zwischen den beiden Versionen durch Veränderungsschritte (was muss gelöscht und hinzugefügt werden, um vom alten auf den neuen Zustand zu kommen aka Levenshtein-Distanz) dargestellt werden, wäre in vielen Fällen ein kaum entzifferbares Ergebnis, wie in diesem Beispiel der Fall:

Diff Demo1 in

Durch den Aufruf der Methode diff_cleanupSemantic() wird dies lesbarer gemacht, indem die einzelnen diffs zu größeren Blöcken zusammengefasst werden. Hier der selbe Vergleich mit nachträglicher Optimierung:

Diff Demo2 in

Mit der Methode diff_prettyHtml() kann eine simple HTML-Ausgabe erzeugt werden. Möchte man das Ergebnis nicht für eine HTML-Seite

Das Ergebnis liegt zum Schluss in der Variable TextHtml und wird an die GSP-Seite zur Ausgabe im Browser durchgereicht.

 

Selbst ausprobieren kann man die Funktionen der Bibliothek unter http://neil.fraser.name/software/diff_match_patch/svn/trunk/demos/demo_diff.html direkt im Browser.

Grails 1.3.7 released

CXmUZIAv28XIiNgkRiz4RRl21TsGZ5HoGpZw1UITNyV in Graeme Rocher hat die Version 1.3.7 des Web-Frameworks Grails veröffentlicht. Bei Grails handelt es sich um eine Zusammenstellung von Frameworks, wie Spring, Hibernate und SiteMesh.
Programmiert wird Grails mit Groovy nach dem Prinzip “Convention over configuration” und ermöglicht so schnelle Ergebnisse bei der Programmierung.
Um noch ein wenig mehr zu Grails zu erfahren, empfehle ich 5 Gründe warum Grails einfach groovy ist zu lesen.

Die wesentlichen Neuerungen von Version 1.3.7 sind

  • Grails verwendet Groovy 1.7.8
  • Request-Parameter können von Stacktrace-Logs ausgeschlossen werden (gezielt einzelne Parameter, oder komplett alle)
  • rund 30 Bugfixes, sowie
  • einige kleinere Verbesserungen (Details im Changelog)

Bisher habe ich noch keine Probleme mit Version 1.3.7 festgestellt; die Migration des Projekts zu wikihost.org ging reibungslos über die Bühne und läuft nun seit knapp 24 Stunden unter normaler Last produktiv.

Grails 1.3.7 Download
Grails 1.3.7 Dokumentation

5 Gründe warum Grails einfach groovy ist

Seit nun fast einem Jahr schreibe ich meine Wiki-Hosting-Plattform wikihost.org in Grails neu. Die bisher zugrundeliegende Technologie, die tdbengine, erschien mir für ein solches Portal einfach nicht mehr zeitgemäß.
Am Anfang stand die Entscheidung, in welcher Programmiersprache bzw. mit welchem Framework ich das (Mammut)-Projekt angehe. PHP und Perl mag ich nicht, Java-Servlets erschienen mir sehr umständlich und aufwändig und an mir völlig fremde Sprachen, wie Ruby oder Python traute ich mich nicht. Mit Grails und Groovy fand ich einen Mittelweg aus Java- und JavaScript-ähnlicher Programmierung, welche ich mir, mit ein wenig Lektüre, schnell aneignen konnte.

Und ich muss heute sagen, dass dies eine gute Entscheidung war und ich bis heute nicht von Grails enttäuscht wurde.

Hier nun meine 5 Gründe, warum ich finde, dass Grails einfach groovy ist:

1. Die zugrundeliegende Sprache Groovy

Mit Groovy zu arbeiten ist einfach … groovy. Während Java starr und restriktiv jede noch so kleine Abweichung vom Schema F abstraft, ist Groovy, welches Grails zu Grunde liegt, wesentlich gelassener. Ob man nun eine Funktion bzw. Methode mit oder ohne Klammern aufruft, um Parameter zu übergeben ist egal. Datentypen? Kann bzw. sollte man dort benutzen, wo es notwendig und sinnvoll ist. Darf man aber getrost ignorieren, wo es zulässig erscheint.

Groovy ist ausserdem eine funktionale Sprache und löst sich vom Paradigma Javas, welches extrem Objekt-bezogen ist. Mit sogenannten Closures bieten sich ganz neue Ansätze beim Code-Schreiben: Einer Funktion als Parameter eine Funktion zu übergeben, das gibt es in Java so nicht.

Kleines Beispiel in Java und Groovy:

//Java
char[] arr = {'a','b','c'};
for(int i = 0; i < arr.length; i++) {
  System.out.println( arr[i] );
}
//Groovy
['a','b','c'].each { println it }

2. Man muss sich an vielen Stellen einfach keine Gedanken über die zugrundeliegende Datenbank machen

Der O/R-Mapper Hibernate ist fast völlig transparent in Grails integriert. Hat man seine Datenbank in der Konfiguration korrekt hinterlegt und sogenannte Domain Classes definiert, sorgt dieser dafür, zu jeder Klasse eine entsprechende Datenbank-Tabelle zu erzeugen bzw. diese entsprechend anzupassen. Um korrekte SQL-Syntax, Normalisierung oder Indexierung der Daten muss (kann aber trotzdem) man sich keine Gedanken machen.

// Beispiel Domain-Klasse
package org.example
class Poem {
    String title
    String author

    static constraints = {
        title(blank: false)
        author(blank: false)
    }
}

3. Convention over Configuration bringt schnelle Ergebnisse

Wer sich die Website zu Hibernate oder dem Spring-Framework mal etwas genauer durchliest, wird immer wieder auf XML-Strukturen stoßen, mit denen diese Frameworks konfiguriert werden. Das ist aufwendig, fehleranfällig und mit Grails auch gar nicht notwendig. Grails setzt gewisse Conventions voraus, wie z.B. Class-Names wie FlugzeugController, um dahinter eine Controller-Klasse (im Sinne des MVC-Patterns) zu definieren, die bestimmte Methoden bereitstellt. Darum, dass es sich letztlich um Servlets handelt, die im Container gemappt werden müssen, braucht der Entwickler sich keine Gedanken zu machen.

4. Plugins für viele unterschiedliche Aufgaben existieren bereits

Egal ob eine Anbindung an PayPal erfolgen soll, ob die eigenen Inhalte per Volltextsuche via Lucene durchstöbert werden sollen oder ob einfach hübsche Formular-Widgets im Browser der Besucher erscheinen sollen, es gibt für vieles ein passendes Plugin. Diese Plugins können dank der verwendeten IDEs (Netbeans oder Eclipse) mit wenigen Klicks eingebunden werden und stehen danach transparent zur Verfügung.

Dadurch kann man sich für viele Aufgabenstellungen die grundlegende Arbeit sparen und nutzt (meist) ausgereifte Bibliotheken.

5. Im schlimmsten Fall steht einen die ganze Welt von Java zur Verfügung

Weil Groovy letztlich wie ein Aufsatz auf Java funktioniert und letztendlich ganz normale .class-Dateien als Kompilat herauskommen steht einem die ganze Welt von Java zur Verfügung. D.h. alle Taglibs oder Bibliotheken, die man in einem normalen Java-Programm nutzen kann, lassen sich auch in einer Grails-Applikation verwenden. Daher mache ich mir so schnell auch keine Sorgen, dass ich mich mit wikihost.org bzw. der darunterliegenden Umgebung wieder in eine Sackgasse manövriere.

Es gibt sicherlich noch viele weitere Gründe, warum das Arbeiten mit Grails mir groovy erscheint. Vielleicht liefere ich ja demnächst noch weitere nach.