31. Aug 2011

DE_flag GB_flag

for english readers

Aus dem richtigen Leben

In der Projektarbeit wird die Erst-Installation einer Client/Server-4D Anwendung vor Ort gemacht. Entweder bin ich selber dort oder der Verantwortliche beim Kunden erledigt für mich die Aufgabe.

PoM_whereisdata

Ganz anders ist das, wenn ich eine App zum Download anbiete und der Anwender keine Idee über 4D hat.

Das erste was 4D erfragt nach einem Doppelklick auf die App: "Wo ist die Datendatei?" Der Anwender versteht Bahnhof, ruft hoffentlich an … oder wirft die App in den Papierkorb und ward nimmer gesehen.

Findet der Anwender keine Datendatei und versucht es mit Erzeugen, wird es brenzlig.

PoM_newData

4D bietet ihm an, die Daten ins Paket der Anwendung zu legen. Eigentlich nicht dumm, alles zusammenhalten, keine Unordnung erzeugen. Also wird die Datendatei angelegt.

Meine App reagiert auf die neue Datendatei: hilft dem Anwender, liest das Adressbuch aus, weiß wer Ich bin, wenn es ein Me im Adressbuch gibt und leitet von Schritt zu Schritt zu einer funktionierenden Anwendung. So weit, so gut!

Wir kennen das: "traue keiner .0" – wird mir nicht besser gehen. Meine App informiert den Anwender: "Hey, es gibt eine neue Version. Willst'de die laden?" Gemacht, getan!

PoM_whereisdata

Die neue Version wird gestartet und 4D fragt nach dem Datenfile, weil das zuletzt verwendete ist auf der Platte des Anwenders nicht zu finden, die liegt auf meinem Entwickler-Rechner.

Und da haben wir den Salat. Das Datafile ist weg! Die war im Paket der letzten Version der App und die ist durch die neue ersetzt worden.

Der Kunde kann glücklich sein, wenn die vorhergehende noch im Papierkorb liegt und ich am Telefon helfen kann. Ich bin nicht glücklich! Den Ärger und den Aufwand würde ich mir gerne sparen.

Daten und Struktur zu trennen ist eine der guten Eigenschaften von 4D. FileMaker-Entwickler wissen ein Lied davon zu singen. Die Daten ins Paket der Anwendung zu sichern ist die falscheste der Möglichkeiten. Das muß ich unterbinden, das muß anders laufen.

Da ich nicht auf die V13 warten kann und möchte, muß ich selber buchführen, welche Daten zu benutzen sind. Das stellt sich für mich so dar:

  • meine App frisch geladen und zum ersten Mal gestartet
    • eine leere Datendatei aus dem Paket der App verwenden
    • den Anwender um einen Ordner fragen, der die Daten aufnehmen soll
    • die leere Datendatei aus dem Paket dorthin kopieren
    • mit OPEN DATA FILE die frisch erstelle Kopie öffnen
    • den Pfad des nun verwendeten Datenfiles beim Beenden an sicherer Stelle auf diesem Mac aufheben: System folder(User Preferences_User)
  • neue Version meiner App geladen und zum ersten Mal gestartet
    • aus System folder(User Preferences_User) den Pfad des zuletzt verwendeten Datafiles auslesen und dieses
    • mit OPEN DATA FILE($pfadZumLetzenDatafile) öffnen
Es ist komplizierter aber so ist das Grundsätzliche. Damit das funktioniert brauche ich

Eigene Präferenzen

PoM

für meine Anwendung. Solche die nicht in der Struktur (Entwickler-Einstellungen) oder in den Daten (Anwender-Einstellungen) aufgehoben werden sondern auf dem Rechner meines Kunden liegen. Also eine Datei mit dem Namen nach dem Schema de.mettre.prefs.dddd im Preferences-Ordner auf dem Rechner.

Ich habe mich entschieden, meine Präferenzen als Name-Wert-Paare anzulegen und alle Werte als Text. Das Bild zu diesem Absatz zeigt die aktuelle Liste:

  • DataFile: sind die Daten des Anwenders
  • BaseData: sind die Daten im Paket
  • TestData: ist ein Hilfsmittel während der Entwicklung
  • LastExitDateTime: steht hier kein Zeitstempel drin, ist meine App nicht ordnungsgemäß beendet worden*.
Die Reihenfolge der Einträge ist egal.

Funktionalitäten wie den Preferences_Manager lege ich komponenten-fähig an. Ich schreibe eine Präferenz mit
Prefs_On_Mac ("PrefsWert_Write";->$dataFile;0;Get localized string("PoM_DataFile"))
und lese einen Präferenz-Wert mit
Prefs_On_Mac ("PrefsWert_Read";->$lastExitInfo;0;Get localized string("PoM_StandardExit"))
Brauche ich einen neuen Wert, vergebe ich einen neuen Namen und schicke den Wert. Das reicht.

Für meine Kontrolle oder um mal dazwischen zu funken kann ich Prefs_On_Mac ohne Parameter verwenden und erhalte dann das abgebildete Fenster.

OPEN DATA FILE ist ein unduldsamer 4D-Befehl. Dauert die On Exit zu lange, beendet sich 4D und startet nicht mehr. Das sieht aus wie ein Crash, doch es fehlt das Crash-Log. Gelöst!

Dazu auch der feature-request:
New Database Event--On Before Opening Database

* während der Laufzeit ist dieser Eintrag leer. Wird 4D korrekt beendet, schreibt Prefs_On_Mac $lastExitInfo:=String(Current date;Date RFC 1123;Current time) in den Wert.

Sie möchten den Sourcecode haben? Kein Problem! Überweisen Sie, was es Ihnen wert ist auf mein PayPal-Konto: info@mettre.de. Was ist denn üblich?

DE_flag GB_flag

lieber auf deutsch

From real life

Doing project work means installing myself a Client/Server-app at the customers site, or having somebody doing that for me.

PoM_whereisdata

This changes, when offering an app for download and not being sure the user has any idea about 4D.

While starting up, before my code runs, 4D asks for the datafile. The user understands nothing. Hopefully he'll call, but she might just trash the app and leave a bad comment.

It might get worse, if the user decides to Create a datafile.

PoM_newData

4D offers to save the datafile inside the app-package. Nice idea, keeping the disk tidy.

My app is polite. Starting with a fresh datafile it reads the addressbook, finds the me-record and prepares the new datafile for an efficient usage. Really nice!

We all know: "never trust a .0" – it won't be better with my app. It's a nice app: "hey, there is a new version. How abouts download now?" Easy doing!

PoM_whereisdata

The new version starts up and 4D asks for the datafile. The last one used isn't found, that was the one on the developers-machine.

Shit! The datafile is gone! It's that one in the app-package and that was replaced with the new version of the app.

Happy customers didn't empty the trash. They call me and I might be helpful enough to them open the package and move *.4dd and *.4dindx and *.4dsomething to a save folder. I'm not happy!

Keeping structure and data apart is a major advantage of 4D, FileMaker-developers will tell. Putting the data inside the app-package is the worst option. I need to avoid that.

I'm not going to wait for V13, nobody pays for waiting. I need to track the last used datafile myself. This is the procedure I installed for my app:

  • my app is fresh and starts the first time
    • use an empty datafile from inside the app-package
    • ask the user for a folder to put the data in
    • copy the empty datafile to the selected folder
    • run OPEN DATA FILE to open the fresh copy
    • save the now valid datafile-path to: System folder(User Preferences_User)
  • a new version of my app starts up the first time
    • read from System folder(User Preferences_User) the path to the last used datafile and
    • OPEN DATA FILE($pathToLastUsedDatafile)
It's more complex than that but this is the concept. To get that working, I need to have

my own preferences

PoM

for my app on the users Mac. These prefs are different from those inside the structure (developers prefs) and those stored in the datafile (users prefs) and need to be stored on the machine of my customer. On Mac this is a file named like de.mettre.prefs.dddd in ~/Library/Preferences. 4D will handle that appropriately on Windows for me.

I've decided to store those prefs in name-value-pairs and every value is of type text. This picture illustrates a current state:

  • DataFile: path to the current datafile
  • BaseData: path to the data inside the app-package
  • TestData: needed during development
  • LastExitDateTime: this contains a timestamp when my app quits properly*.
No dependency on sort-order.

Nowadays I implement functionality like the preferences-manager in component-style or api-like. To write a preference I call
Prefs_On_Mac ("PrefsValue_Write";->$dataFile;0;Get localized string("PoM_DataFile"))
to read a preference works accordingly
Prefs_On_Mac ("PrefsValue_Read";->$lastExitInfo;0;Get localized string("PoM_StandardExit"))
Do I need a new value, I send the new name and a value. Prefs_On_Mac takes care of that.

For my convenience and manual adjustements calling Prefs_On_Mac without parameters opens the shown window.

OPEN DATA FILE is a very intolerant 4D-command. Does On Exit take too long, 4D quits and does no restart with the named datafile as expected. It looks like a crash without crash-log. Solved!

Check the feature-request:
New Database Event--On Before Opening Database

* while my app is running this entry is empty. This value is filled when shutting down and cleared when starting up.

You want to have the source-code? No problem! Send a descend amount of $ or € to my PayPal-account: info@mettre.de. Finding out how much?