wiki:skripte-als-webservice

Version 25 (modified by Wolfgang Schmidle, 13 years ago) (diff)

--

XML-Workflow-Skripte auf Webservice vorbereiten

Die Skripte für den XML-Workflow sollen in mehreren Kontexten funktionieren:

  • als Textfilter
  • auf der Commandline: mindestens einzeln, möglichst auch in einer Unix-pipe
  • mit einem in Java geschriebenen Wrapper als Webservice

Im Webservice sollen die Skripte ohne Expertenwissen verwendbar sein.

1. Schnittstelle zwischen Skripten und Webservice-Wrapper

Input und Output

Input der Skripte ist eine Datei oder STDIN. Output geht nach STDOUT bzw. STDERR (bei Fehlern) für textverändernde Skripte und STDERR für informierende Skripte.

Falls zusammenfassende Skripte, die intern eine Unix-pipe verwenden, nicht direkt in einen Webservice übertragbar sind, brauchen wir einen Mechanismus, der Unix-Pipes nachbildet.

Parameter-Typen

Die Skript-Parameter sollen direkt auf den Webservice abbildbar sein. Offensichtliche Parameter-Typen:

  • ja/nein, entspricht einer Checkbox: Beispiel --verbose (dieser Parameter-Typ ist bei den XML-Workflow-Skripten selten)
  • Wert aus einer festen Liste, die sich selten ändert: Beispiel --DESpecs=1.1.2
  • Wert aus einer Liste von Dateinamen: Beispiel --wordlist=datei1,datei2 (die Reihenfolge der Dateien ist wichtig)

Etwas esoterischere Parameter-Typen, die in den Skripten zwar geplant, aber noch nicht implementiert sind:

  • Meta-Parameter, d.h. Zusammenfassungen von Parametern in einem einzelnen Parameter: Beispiel --language=LA oder --timeperiod=RENAISSANCE oder --defaults=file, bewirkt u.a. die Vorauswahl bestimmter Wortlisten
  • einzelne Regelergänzungen wie --addWordlistEntry="opꝫ = opportet", --removeWordlistEntry="opꝫ" ? (eventuell ersetzbar durch eine kurze zusätzliche Datei)
  • Änderungen von Zeichenlisten, zum Beispiel --addWordseparator="/", --removeWordseparator="/" ? Ein vergleichbarer Parameter könnte auch im Anzeigesystem verwendbar sein, siehe #176.

Woher weiß der Webservice, welche Parameter ein Skript hat?

Das reg-Skript hat zum Beispiel zurzeit die Parameter

  • dir (1x, String)
  • wordlist, simple (jeweils mehrmals, aus Liste von Dateien, Reihenfolge ist wichtig)
  • Standard-Parameter man, help, ?
  • hypothetisch auch noch: DESpecs, verbose

Zuerst könnte das beim Webservice hardcoded sein. Später wäre ein Übergabeformat sinnvoll, das eingelesen wird, wenn eine neue Skriptversion hochgeladen wird. Daraus wird dann automatisch die passende GUI zusammengebaut.

Bei Aufruf mit einem weiteren Standard-Parameter --parameterformat könnte das Skript in etwa ausgeben (angelehnt an das Format von Getopt::Long, siehe unten):

verbose
DESpecs=1.1.2,default=2.0
dir=s,$
wordlist=s,@,reg-wordlist
simple=s,@,reg-simple

Dabei bedeutet

  • s: erwartet wird ein String
  • $: einmal, @: mehrmal (Alternative: Notation wie bei regexes)
  • reg-wordlist: akzeptiert alle Hilfsdateien , die mit "reg-wordlist" anfangen

Der letzte Punkt bezieht sich auf die Hilfsdateien im Server. Der Benutzer kann natürlich weitere Dateien mit beliebigen URLs hinzufügen.

Der Parameter --dir ist nur eine Bequemlichkeit für die Verwendung des Skriptes auf der Commandline. Der Webservice wird ihn nicht verwenden. Gleich in der Liste weglassen?

Wie werden Meta-Parameter kommuniziert?

2. Zusätzlich im Webservice

Nachbilden von Texteditor-Funktionen:

  • zufrieden? anwenden oder nicht
  • nur markierten Ausschnitt bearbeiten

Interaktives Arbeiten:

  • zum Beispiel minimale Interaktivität: diff anzeigen, einzeln durchgehen.
  • Mechanismus, dass sich das System die nachträglichen Korrekturen allgemein merkt: Benutzer kann weitere Liste anlegen mit Positiv-Formen und Negativ-Formen (d.h. "nein, diese Form soll nicht verändert werden")

Sonstiges:

  • Service für PND, zusätzlich zu unserer eigenen Liste?

3. Anpassungen in den Skripten

BBEdit 10.0

In BBEdit 10.0 gibt es eine Änderung für Textfilter: der input ist nicht mehr eine temporäre Datei, sondern kommt via STDIN. Konkret hat das wenig Auswirkungen für die Programmierung der Skripte.

In BBEdit 9.6.3 gab es das Problem, dass UTF-8-output nach STDERR von BBEdit als MacRoman interpretiert wurde und deshalb unleserlich wurde. Das Problem ist in 10.0 gelöst. Der Workaround, das Ergebnis in eine Datei zu schicken, ist daher nicht mehr nötig, deshalb werde ich ihn nicht mehr verwenden. Übergangsweise gibt es das Problem, dass man BBEdit braucht, um den XML-Workflow mit Textfiltern zu verwenden, denn Textwrangler wird den Fehler noch haben. (Alternative ist die Commandline.)

Bei BBEdit 10.0 sind Textfilter jetzt in ~/Library/Application Support/BBEdit/Text Filters. Das alte Textfilter-Verzeichnis von BBEdit 9 wird automatisch an die neue Stelle kopiert.

BBedit 10.0 ist immer noch kein reines Cocoa, aber sie haben sich viel Mühe gegeben, dass man das praktisch nicht mehr merkt.

Getopt

Ich verwende jetzt das Perl-Modul Getopt::Long für das Einlesen der Optionen (und Pod::Usage für die Dokumentation der Optionen). Dadurch ergibt sich recht natürlich folgendes Format, das auch für Textfilter funktioniert:

perl script.pl [options] [file.xml]

bzw. [file.txt] für die ersten Skripte im XML-Workflow. Wahrscheinlich ist dieses Format auch pipe-tauglich, aber das habe ich bisher nur sehr oberflächlich getestet.

Bei den Optionen werden dadurch automatisch mehrere Formate akzeptiert, zum Beispiel

  • --wordlist=datei1 --wordlist=datei2
  • --wordlist=datei1,datei2
  • -wordlist=datei1,datei2
  • --wordl datei1,datei2
  • --w datei1,datei2

Beispiel (mit Zeilenumbrüchen, um es übersichtlicher zu machen):

perl "/Users/ich/Library/Application Support/BBEdit/Text Filters/Filter_5_01_insert_reg.pl"
--dir=/Pfad/zum/Repository/trunk/trunk/texts/aux
--wordlist=reg-wordlist-lat.txt --wordlist=reg-wordlist-lat-hentisberi.txt --wordlist=reg-wordlist-lat-alvarus.txt
--simple=reg-simple-lat.txt,reg-simple-lat-alvarus.txt
Alvarus-Zeile.txt

Die Reihenfolge der Parameter ist egal. Man kann also auch nicht --dir angeben, dann ein paar Dateien, dann ein neues --dir und weitere Dateien. Innerhalb eines Parameters wie wordlist ist die Reihenfolge der Dateien wichtig.

Beim Webservice wird es eine Liste von Standard-Hilfsdateien geben. Zusätzlich muss der Benutzer die Möglichkeit haben, weitere Hilfsdateien in das System hochzuladen oder mit beliebigen URLs auch Dateien einzubinden, die nicht im System sind.

Der Parameter --dir kann am Ende ein / haben, es darf aber auch fehlen.

Input und output sind immer UTF-8. Der input wird im Skript wie bisher mit "while(<>)" ausgelesen. Das funktioniert mit STDIN und Dateinamen. Ich verwende jetzt doch nicht "input=", weil alles, was nicht eine Option ist, als Dateiname interpretiert wird. (Mehr als ein Dateiname ist eigentlich bei keinem Skript sinnvoll, aber es ist nicht verboten). Ich verwende auch kein "output=", weil die defaults STDOUT für Text und STDERR für Fehlermeldungen eigentlich gut genug sind. Umleitungen kann man dann auf der Commandline mit Unix-Mitteln machen.

Error conditions: Wenn ein Skript nach STDERR schreibt, soll die Verarbeitung des Skripts oder der pipe abgebrochen werden. Das Skript bricht mit "die" ab, das automatisch einen exit code ungleich 0 bewirkt.

Wo nach Optionen gesucht wird

Das Skript versucht, Optionen an folgenden Stellen zu finden:

  1. Commandline

(nach Schritt 1 wird der Text eingelesen)

  1. <metadata> <parameters>: wenn nur ein Textausschnitt übergeben wird, muss das aber nicht beim Textfilter ankommen
  1. falls das Skript als Textfilter läuft, eine zusätzliche Datei in Filter_parameters/$name.txt. Schritt 3 ist nötig, weil man einem Textfilter keine Parameter übergeben kann. Beispiel: Text Filters/Filter_5_01_insert_reg.pl sucht in Text Filters/Filter_parameters/Filter_5_01_insert_reg.txt.

Ich verwende jetzt, im Gegensatz zu vorher, einen relativen Pfad, weil es sonst nicht ohne Änderungen funktioniert, nachdem man das Verzeichnis trunk/schema/scripts/workflow aus dem Repository in das Textfilter-Verzeichnis kopiert hat. Siehe workflow_tutorial, insbesondere den Abschnitt für BBEdit.

Die Dateien reg-wordlist-lat-alvarus.txt etc. würde man dann wohl nicht mehr in trunk/texts/aux, sondern in Filter_parameters haben.

Unterschiedliche Parameter-Formate

Der Parameter "wordlist" heißt "reg.wordlist" in <parameters>, etc. Wenn man das nicht will, muss man auf namespaces verzichten und hoffen, dass die Skript-Parameter-Namen sich nicht beißen. Und es gibt (wenige) Parameter, die zu mehr als einem Skript gehören. Unterelemente von <parameters> finde ich übertrieben. Oder <parameters skript="Filter_insert-reg">, etc.? Dann kann man die namespaces weglassen.

Und alle Parameter in einem <parameters> ohne @skript würden sich auf alle Skripte beziehen. Außerdem könnte man den Inhalt von <parameters> ebenfalls mit Getopt::Long einlesen, um ein einheitliches Parameter-Format zu haben. Ein einzelnes Skript würde dann den Inhalt von <parameters> und <parameters script="name"> übergeben. Oder vielleicht doch kein <parameters> ohne @script, denn in Filter_parameters gibt es auch keine Datei, die für alle Skripte gilt. Dann muss man allgemeine Parameter eben für alle relevanten Skripte wiederholen. Kein großer Aufwand.

Vorschlag also: <parameters> kann mehrfach verwendet werden, mit obligatorischem @script. Darin das gleiche Optionen-Format wie auf der Commandline, einlesen mit GetOptionsFromString.

Commandline versus Textfilter

Ob das Skript als Textfilter als Textfilter aufgerufen wird, erkennt es daran, dass die runtime environment variable $ENV{'BB_DOC_NAME'} existiert. Das ist BBEdit-spezifisch. Für Textwrangler oder andere Editoren wäre es anders.

Wenn man bereits Parameter in <parameters> angegeben hat, werden deshalb trotzdem auch noch die Parameter aus der zusätzlichen Datei eingelesen. Der Aufruf auf der Commandline ohne Parameter

perl script.pl file.xml

wäre dann nicht identisch mit dem Aufruf als Textfilter.

Alternative wäre, zum Beispiel zu prüfen, ob in Schritt 1 und 2 irgendwelche Optionen gesetzt wurden. Dann würden beim Aufruf auf der Commandline ohne jeden Parameter wie bei der Verwendung als Textfilter die Standard-Parameter aus der zusätzlichen Datei verwendet werden, aber sobald man irgendeinen Parameter setzt, wird die zusätzliche Datei nicht mehr eingelesen. Welche Variante führt zu möglichst wenig Verwirrung?

Nachbilden der Möglichkeit, den Textfilter mit einem Ausschnitt einer Datei aufzurufen: Optionen wie "startzeile" und "endzeile" oder gar die genauen Spalten haben wir ja erstmal verschoben. Vielleicht könnte man das auch sowieso besser im Webservice-Wrapper machen. Dann muss es nicht in jedem Skript einzeln stehen. (BBEDIT übergibt übrigens runtime environment variables für Startzeile und -spalte etc. an den Textfilter. Diese Information wird jedoch zurzeit nicht verwendet.)