![]() |
![]()
| ![]() |
![]()
NAMEData::Dump::Streamer - Accurately serialize a data structure as Perl code.SYNOPSISuse Data::Dump::Streamer; use DDS; # optionally installed alias Dump($x,$y); # Prints to STDOUT Dump($x,$y)->Out(); # " " my $o=Data::Dump::Streamer->new(); # Returns a new ... my $o=Dump(); # ... uninitialized object. my $o=Dump($x,$y); # Returns an initialized object my $s=Dump($x,$y)->Out(); # " a string of the dumped obj my @l=Dump($x,$y); # " a list of code fragments my @l=Dump($x,$y)->Out(); # " a list of code fragments Dump($x,$y)->To(\*STDERR)->Out(); # Prints to STDERR Dump($x,$y)->Names('foo','bar') # Specify Names ->Out(); Dump($x,$y)->Indent(0)->Out(); # No indent Dump($x,$y)->To(\*STDERR) # Output to STDERR ->Indent(0) # ... no indent ->Names('foo','bar') # ... specify Names ->Out(); # Print... $o->Data($x,$y); # OO form of what Dump($x,$y) does. $o->Names('Foo','Names'); # ... $o->Out(); # ... DESCRIPTIONGiven a list of scalars or reference variables, writes out their contents in perl syntax. The references can also be objects. The contents of each variable is output using the least number of Perl statements as convenient, usually only one. Self-referential structures, closures, and objects are output correctly.The return value can be evaled to get back an identical copy of the original reference structure. In some cases this may require the use of utility subs that Data::Dump::Streamer will optionally export. This module is very similar in concept to the core module Data::Dumper, with the major differences being that this module is designed to output to a stream instead of constructing its output in memory (trading speed for memory), and that the traversal over the data structure is effectively breadth first versus the depth first traversal done by the others. In fact the data structure is scanned twice, first in breadth first mode to perform structural analysis, and then in depth first mode to actually produce the output, but obeying the depth relationships of the first pass. Caveats Dumping Closures (CODE Refs)As of version 1.11 DDS has had the ability to dump closures properly. This means that the lexicals that are bound to the closure are dumped along with the subroutine that uses them. This makes it much easier to debug code that uses closures and to a certain extent provides a persistency framework for closure based code. The way this works is that DDS figures out what all the lexicals are that are bound to CODE refs it is dumping and then pretends that it had originally been called with all of them as its arguments, (along with the original arguments as well of course.)One consequence of the way the dumping process works is that all of the recreated subroutines will be in the same scope. This of course can lead to collisions as two subroutines can easily be bound to different variables that have the same name. The way that DDS resolves these collisions is that it renames one of the variables with a special name so that presumably there are no collisions. However this process is very simplistic with no checks to prevent collisions with other lexicals or other globals that may be used by other dumped code. In some situations it may be necessary to change the default value of the rename template which may be done by using the "EclipseName" method. Similarly to the problem of colliding lexicals is the problem of colliding lexicals and globals. DDS pays no attention to globals when dumping closures which can potentially result in lexicals being declared that will eclipse their global namesake. There is currently no way around this other than to avoid accessing a global and a lexical with the same name from the subs being dumped. An example is my $a = sub { $a++ }; Dump( sub { $a->() } ); which will not be dumped correctly. Generally speaking this kind of thing is bad practice anyway, so this should probably be viewed as a "feature". :-) Generally if the closures being dumped avoid accessing lexicals and globals with the same name from out of scope and that all of the CODE being dumped avoids vars with the "EclipseName" in their names the dumps should be valid and should eval back into existence properly. Note that the behaviour of dumping closures is subject to change in future versions as its possible that I will put some additional effort into more sophisticated ways of avoiding name collisions in the dump. USAGEWhile Data::Dump::Streamer is at heart an object oriented module, it is expected (based on experience with using Data::Dumper) that the common case will not exploit these features. Nevertheless the method based approach is convenient and accordingly a compromise hybrid approach has been provided via the "Dump()" subroutine. Such asDump($foo); $as_string= Dump($foo)->Out(); All attribute methods are designed to be chained together. This means that when used as set attribute (called with arguments) they return the object they were called against. When used as get attributes (called without arguments) they return the value of the attribute. From an OO point of view the key methods are the "Data()" and "Out()" methods. These correspond to the breadth first and depth first traversal, and need to be called in this order. Some attributes must be set prior to the "Data()" phase and some need only be set before the "Out()" phase. Attributes once set last the lifetime of the object, unless explicitly reset. Controlling Object RepresentationThis module provides hooks to allow objects to override how they are represented. The basic idea is that a subroutine (or method) is provided which is responsible for the override. The return of the method governs how the object will be represented when dumped, and how it will be restored. The basic calling convention ismy ( $proxy, $thaw, $postop )= $callback->($obj); #or = $obj->$method(); The "Freezer()" method controls what methods to use as a default method and also allows per class overrides. When dumping an object of a given class the first time it tries to execute the class specific handler if it is specified, then the user specific generic handler if its been specified and then "DDS_freeze". This means that class authors can implement a "DDS_freeze()" method and their objects will automatically be serialized as necessary. Note that if either the class specific or generic handler is defined but false "DDS_freeze()" will not be used even if it is present. The interface of the "Freezer()" handler in detail is as follows:
An example DDS_freeze method is one I had to put together for an object which contained a key whose value was a ref to an array tied to the value of another key. Dumping this got crazy, so I wanted to suppress dumping the tied array. I did it this way: sub DDS_freeze { my $self=shift; delete $self->{'tie'}; return ($self,'->fix_tie','fix_tie'); } sub fix_tie { my $self=shift; if ( ! $self->{'tie'} ) { $self->{str}="" unless defined $self->{str}; tie my @a, 'Tie::Array::PackedC', $self->{str}; $self->{'tie'} = \@a; } return $self; } The $postop means the object is relatively unaffected after the dump, the $thaw says that we should also include the method inline as we dump. An example dump of an object like this might be $Foo1=bless({ str=>'' },'Foo')->fix_tie(); Wheras if we omit the "->" then we would get: $Foo1=bless({ str=>'' },'Foo'); $Foo1->fix_tie(); In our example it wouldn't actually make a difference, but the former style can be nicer to read if the object is embedded in another. However the non arrow notation is slightly more dangerous, in that its possible that the internals of the object will not be fully linked when the method is evaluated. The second form guarantees that the object will be fully linked when the method is evaluated. See "Controlling Hash Traversal and Display Order" for a different way to control the representation of hash based objects. Controlling Hash Traversal and Display OrderWhen dumping a hash you may control the order the keys will be output and which keys will be included. The basic idea is to specify a subroutine which takes a hash as an argument and returns a reference to an array containing the keys to be dumped.You can use the KeyOrder() routine or the SortKeys() routine to specify the sorter to be used. The routine will be called in the following way: ( $key_array, $thaw ) = $sorter->($hash,($pass=0),$addr,$class); ( $key_array,) = $sorter->($hash,($pass=1),$addr,$class); $hash is the hash to be dumped, $addr is the refaddr() of the $hash, and $class will be set if the hash has been blessed. When $pass is 0 the $thaw variable may be supplied as well as the keyorder. If it is defined then it specifies what thaw action to perform after dumping the hash. See $thaw in "Controlling Object Representation" for details as to how it works. This allows an object to define those keys needed to recreate itself properly, and a followup hook to recreate the rest. Note that if a Freezer() method is defined and returns a $thaw then the $thaw returned by the sorter will override it. Controlling Array Presentation and Run Length EncodingBy default Data::Dump::Streamer will "run length encode" array values. This means that when an array value is simple (ie, its not referenced and does contain a reference) and is repeated multiple times the output will be single a list multiplier statement, and not each item output separately. Thus: "Dump([0,0,0,0])" will be output something like$ARRAY1 = [ (0) x 4 ]; This is particularly useful when dealing with large arrays that are only partly filled, and when accidentally the array has been made very large, such as with the improper use of pseudo-hash notation. To disable this feature you may set the Rle() property to FALSE, by default it is enabled and set to TRUE. Installing DDS as a package aliasIts possible to have an alias to Data::Dump::Streamer created and installed for easier usage in one liners and short scripts. Data::Dump::Streamer is a bit long to type sometimes. However because this technically means polluting the root level namespace, and having it listed on CPAN, I have elected to have the installer not install it by default. If you wish it to be installed you must explicitly state so when Build.Pl is run:perl Build.Pl DDS [Other Module::Build options] Then a normal './Build test, ./Build install' invocation will install DDS. Using DDS is identical to Data::Dump::Streamer. use-time package aliasingYou can also specify an alias at use-time, then use that alias in the rest of your program, thus avoiding the permanent (but modest) namespace pollution of the previous method.use Data::Dumper::Streamer as => 'DDS'; # or if you prefer use Data::Dumper::Streamer; import Data::Dumper::Streamer as => 'DDS'; You can use any alias you like, but that doesn't mean you should.. Folks doing as => 'DBI' will be mercilessly ridiculed. PadWalker supportIf PadWalker 1.0 is installed you can use DumpLex() to try to automatically determine the names of the vars being dumped. As long as the vars being dumped have my or our declarations in scope the vars will be correctly named. Padwalker will also be used instead of the B:: modules when dumping closures when it is available.INTERFACEData::Dumper CompatibilityFor drop in compatibility with the Dumper() usage of Data::Dumper, you may request that the Dumper() method is exported. It will not be exported by default. In addition the standard Data::Dumper::Dumper() may be exported on request as "DDumper". If you provide the tag ":Dumper" then both will be exported.
Constructors
Methods
Reading the OutputAs mentioned in Verbose there is a notation used to make understanding the output easier. However at first glance it can probably be a bit confusing. Take the following example:my $x=1; my $y=[]; my $array=sub{\@_ }->( $x,$x,$y ); push @$array,$y,1; unshift @$array,\$array->[-1]; Dump($array); Which prints (without the comments of course): $ARRAY1 = [ 'R: $ARRAY1->[5]', # resolved by fix 1 1, 'A: $ARRAY1->[1]', # resolved by fix 2 [], 'V: $ARRAY1->[3]', # resolved by fix 3 1 ]; $ARRAY1->[0] = \$ARRAY1->[5]; # fix 1 alias_av(@$ARRAY1, 2, $ARRAY1->[1]); # fix 2 $ARRAY1->[4] = $ARRAY1->[3]; # fix 3 The first entry, 'R: $ARRAY1->[5]' indicates that this slot in the array holds a reference to the currently undefined "$ARRAY1->[5]", and as such the value will have to be provided later in what the author calls 'fix' statements. The third entry 'A: $ARRAY1->[1]' indicates that is element of the array is in fact the exact same scalar as exists in "$ARRAY1->[1]", or is in other words, an alias to that variable. Again, this cannot be expressed in a single statement and so generates another, different, fix statement. The fifth entry 'V: $ARRAY1->[3]' indicates that this slots holds a value (actually a reference value) that is identical to one elsewhere, but is currently undefined. In this case it is because the value it needs is the reference returned by the anonymous array constructor in the fourth element ("$ARRAY1->[3]"). Again this results in yet another different fix statement. If Verbose() is off then only a 'R' 'A' or 'V' tag is emitted as a marker of some form is necessary. All of this specialized behaviour can be bypassed by setting Purity() to FALSE, in which case the output will look very similar to what Data::Dumper outputs in low Purity setting. In a later version I'll try to expand this section with more examples. A Note About SpeedData::Dumper is much faster than this module for many things. However IMO it is less readable, and definitely less accurate. YMMV.EXPORTBy default exports the Dump() command. Or may export on request the same command as Stream(). A Data::Dumper::Dumper compatibility routine is provided via requesting Dumper and access to the real Data::Dumper::Dumper routine is provided via DDumper. The later two are exported together with the :Dumper tag.Additionally there are a set of internally used routines that are exposed. These are mostly direct copies of routines from Array::RefElem, Lexical::Alias and Scalar::Util, however some where marked have had their semantics slightly changed, returning defined but false instead of undef for negative checks, or throwing errors on failure. The following XS subs (and tagnames for various groupings) are exportable on request. :Dumper Dumper DDumper :undump # Collection of routines needed to undump something alias_av # aliases a given array value to a scalar alias_hv # aliases a given hashes value to a scalar alias_ref # aliases a scalar to another scalar make_ro # makes a scalar read only lock_keys # pass through to Hash::Util::lock_keys lock_keys_plus # like lock_keys, but adds keys to those present lock_ref_keys # like lock_keys but operates on a hashref lock_ref_keys_plus # like lock_keys_plus but operates on a hashref dualvar # make a variable with different string/numeric # representation alias_to # pretend to return an alias, used in low # purity mode to indicate a value is actually # an alias to something else. :alias # all croak on failure alias_av(@Array,$index,$var); alias_hv(%hash,$key,$var); alias_ref(\$var1,\$var2); push_alias(@array,$var); :util blessed($var) #undef or a class name. isweak($var) #returns true if $var contains a weakref reftype($var) #the underlying type or false but defined. refaddr($var) #a references address refcount($var) #the number of times a reference is referenced sv_refcount($var) #the number of times a scalar is referenced. weak_refcount($var) #the number of weakrefs to an object. #sv_refcount($var)-weak_refcount($var) is the true #SvREFCOUNT() of the var. looks_like_number($var) #if perl will think this is a number. regex($var) # In list context returns the pattern and the modifiers, # in scalar context returns the pattern in (?msix:) form. # If not a regex returns false. readonly($var) # returns whether the $var is readonly weaken($var) # cause the reference contained in var to become weak. make_ro($var) # causes $var to become readonly, returns the value of $var. reftype_or_glob # returns the reftype of a reference, or if its not # a reference but a glob then the globs name refaddr_or_glob # similar to reftype_or_glob but returns an address # in the case of a reference. globname # returns an evalable string to represent a glob, or # the empty string if not a glob. :all # (Dump() and Stream() and Dumper() and DDumper() # and all of the XS) :bin # (not Dump() but all of the rest of the XS) By default exports only Dump(), DumpLex() and DumpVars(). Tags are provided for exporting 'all' subroutines, as well as 'bin' (not Dump()), 'util' (only introspection utilities) and 'alias' for the aliasing utilities. If you need to ensure that you can eval the results (undump) then use the 'undump' tag. BUGSCode with this many debug statements is certain to have errors. :-)Please report them with as much of the error output as possible. Be aware that to a certain extent this module is subject to whimsies of your local perl. The same code may not produce the same dump on two different installs and versions. Luckily these don't seem to pop up often. AUTHOR AND COPYRIGHTYves Orton, yves at cpan org.Copyright (C) 2003-2005 Yves Orton This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Contains code derived from works by Gisle Aas, Graham Barr, Jeff Pinyan, Richard Clamp, and Gurusamy Sarathy. Thanks to Dan Brook, Yitzchak Scott-Thoennes, eric256, Joshua ben Jore, Jim Cromie, Curtis "Ovid" Poe, Lars DXXXXXX, and anybody that I've forgotten for patches, feedback and ideas. SEE ALSO (its a crowded space, isn't it!)Data::Dumper - the mother of them allData::Dumper::Simple - Auto named vars with source filter interface. Data::Dumper::Names - Auto named vars without source filtering. Data::Dumper::EasyOO - easy to use wrapper for DD Data::Dump - Has cool feature to squeeze data Data::Dump::Streamer - The best perl dumper. But I would say that. :-) Data::TreeDumper - Non perl output, lots of rendering options And of course www.perlmonks.org and perl itself.
|