[Munich-pm] Suche ein noch besserer Data::Dumper :-)

Harald Jörg Harald.Joerg at arcor.de
Fri Jan 20 15:29:39 PST 2012


Stephen Riehm schreibt:

> OK, ihr kennt schon Data::Dumper, YAML, Data::Printer etc. usw. - 
> kenne ich auch... und bin von allen enttäuscht :-)

Oh ja... das kenn' ich...

Bei kleinen Projekten braucht man's nicht und bei großen Projekten
schafft man's nicht...

> Wir erweitern unser Objekt-Orientierung immer mehr aus - was zu Folge
> hat, das unser Objekt-Netzwerke immer komplexer werden.

Oder, um bei einem der Themen vom letzten Meeting zu bleiben: Die
Objekte verwenden unterschiedliche Implementierungen.  In einem großen
Objekt sind dann Unterobjekte als blessed hash reference, solche aus
Class::Std::Fast und solche aus XS... wie bekommt man da einen Dump, der
die "wichtigen" Informationen enthält?

> Noch dazu, die Objekte werden immer besser mit einander vernetzt, und
> das heisst das "mal ein Dump()" auf ein Objekt zum Debuggen
> leider nicht mehr funktioniert - weil das Ganze Speicher wird
> ausgegeben.
>
> Was ich suche ist ein Modul, der:
> 	ein Human-Readable format wie YAML beherrscht (muss nicht
> wieder geparst werden)
> 	die gefiltert werden kann (gibt nicht alle keys aus einem Hash aus)
> 	und noch besser, Objekte die Freiheit geben, sich selbst zu dumpen
> 	wenn möglich sollte es pur-perl sein und wenige (bis gar
> keine) Abhängigkeiten mit sich bringen.
>
> Kennt ihr so-was?

Leider nein.

> Für mich habe ich ein wenig experimentiert und mir einen Modul
> geschrieben die dies mehr-oder-weniger macht.
> Wenn es ein blessed object findet - schaut es nach ob die Methoden
> dump(), dumpKeyNames(), dumpKeyStyles() oder dumpKeyComments()
> definiert sind.
>
> dump() gibt das ganze Objekt als mehr-zeilige String zurück - das
> Objekt kann dabei machen was es will.

Das habe ich auch schon manchmal gemacht: Eine dump()-Methode für meine
Klassen geschrieben.  Geht aber nur "bottom up", der dump() muss ja
wieder wissen, was er mit allen Objekten macht, die er enthält.  Noch
ein Fallstrick: Bei Vererbung (jaja, sollte man vermeiden) erbt man auch
dump() aus der Basisklasse.  Das schreibt aber vielleicht nicht alles,
was man braucht.

> dumpKeyNames() gibt einfach eine Liste alle keys die ausgegeben werden
> darf, und in welchen Reihenfolge (default ist Lexogrphisch).
> Wenn andere Keys im Objekt existieren, werde sie nicht gedumpt.

Meinst Du "Keys" (wie in hash keys) oder doch eher "Attributes" (wie in
OO-Terminologie)?  Keys verstehe ich dann, wenn Du riesige Hashes mit
tausenden von Keys hast, aber nur eine Auswahl brauchst.  Bei einem tied
hash aus einer Datenbank kann sowas tödlich sein.

Bei Objekten ist für mich eher die tiefe Verschachtelung ein Problem,
die Anzahl der Namen pro Objekt ist bei mir meistens übersichtlich.  Da
hilft mir so etwas wie Data::Dumper::Maxdepth mehr als mir dumpKeyNames
helfen würde.  Auch in Deinen Beispielen unten würde das ausreichen!

> dumpKeyStyles() gibt vor, wie jedes Attribute ausgegeben werden
> sollte. Der return Wert ist eine Liste key => value Paare.
> Der Wert kann entweder 'asString' (ein-Zeilige Representation) und
> dump' um das ganze Objekt aus zu geben (eigentlich, könnte der Wert
> irgendwelchen Methodennamen sein)

Gefällt mir nicht so gut.  Denn wenn das Attribut selbst ein Objekt ist,
weiß nur dieses Objekt, wie es dargestellt werden kann.  Hier mußt Du
vorher für jedes Unterobjekt wissen, was es ist.

> dumpKeyComments() gibt ebenfalls vor, welche Attribute mit eine
> Kommentar versehen werden sollten. Der return Wert ist ebenfalls eine
> Liste key => value Paare.
> Der Wert wird als Kommentar in der Zeile vor der Key ausgegeben.
>
> Falls eine der Methoden entdeckt wird - dann wird das Objekt
> entsprechend ausgegeben.

Das geht aber schlecht nach unten zu delegieren.  Ein Dump() kann
versuchen, für jedes Attribut-Objekt auch Dump() aufzurufen, ok.  Ein
dumpKeyNames() kann das aber nicht wirklich, weil es nicht weiß, welche
Keys aus dem Attribut-Objekt Du haben willst.

> Zum Beispiel, ein Adressbuch, mit Gruppen und natürlich viele Leute.
> Eine Gruppe-Objekt trägt eine Liste alle Mitglieder, eine Gruppen
> Email, vielleicht noch ein Kalendar Objekt usw.
> Jeder Person Objekt kann auch mit seine Gruppen verbunden sein usw.
>
> z.B.:
>
> # Beispiel 1:
> #       das Person Objekt gibt nur die Namen der verlinkten Gruppen
> aus...
> my @people = $address_book->people();
> print Dump( \@people );
> ---
> - <Person object>
>  name:   Adam
>  email:
>          # hash-keys in order provided! (and with comments!)
>          work: adam at megacorp.com
>          home: adam at home.de
>          oma:  bub at oma.de
>  groups:
>          - <Group object> sport freaks
>          - <Group object> extreme-couchers
>          - <Group object> shatten-parker
> - <Person object>
>  name:      Frank
>  email:
>  telephone:
>             home:   (089) 123123
>             work:   (089) 321 321
>  groups:
>             - <Group object> shatten-parker
> - <Person object>
>  name: Joe
> ...
>
> # Beispiel 2:
> #       aber das Group Objekt macht es genau so umgekehrt
> #       - hier werden nur die Personen-Namen ausgegeben - statt das
> ganze Objekt
> my @tiny_groups = grep { scalar( $_->members() ) < 10 } $address_book-
>>groups();
> print Dump( \@tiny_groups );
> ---
> - <Group object>
>  name: schatten-parker
>  email:
>    group:  extrem-schatten at parkhaus.de
>  # note that the Person objects are only displayed as 'name'
>  members:
>    - <Person object> Adam
>    - <Person object> Frank
> ...
>
> Habt ihr irgendwelchen Ideen?

Eher ein paar Erfahrungen mit Code-Erbschaften.

 * Anstelle von "Dump()" würde ich lieber das Zielformat in der Methode
   haben, z.B.  "asYAML()", wenn's YAML werden soll, oder "asDebug()",
   wenn's vor allem lesbar sein soll.  Sonst findest Du im perldoc
   Dump(), rufst es auf, und bekommst eine bunte Mischung aus
   Data::Dumper, YAML, XML-Serialisierung, stringify.... was auch immer
   dem Programmierer grade eingefallen ist.

 * BTDT: Dump() braucht man selten, also schreibt's $CLEVER_GUY in die
   AUTOLOAD-Methode, damit es nur bei Bedarf mit require() nachgeladen
   wird.  $OTHER_CLEVER_GUY stellt fest, dass nicht jedes Objekt aus dem
   Projekt Dump() implementiert und fragt erst nach, ob das Ding, das er
   dumpen will, auch Dump() kann: if UNIVERSAL::can($object,'Dump')...
   Er bekommt immer false, weil can() nichts von AUTOLOAD versteht.
   (es ging damals um asHTML, aber das Prinzip ist das gleiche)

--
Cheers,
haj


More information about the Munich-pm mailing list