in reply to socio-political guidance sought

Either my version of Data::Dumper already has your patch or I'm missing something vital:

#Data/Dumper.pm 2.12 line 233-235 if (my $freezer = $s->{freezer}) { $val->$freezer() if UNIVERSAL::can($val, $freezer); }
"If the user asked Data::Dumper to call a freezer method and if the reference is an object that has this method, do call the freezer method."

Jenda
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
   -- Rick Osborne

Replies are listed 'Best First'.
Re^2: socio-political guidance sought
by Anonymous Monk on Aug 20, 2004 at 17:54 UTC
    My apologies for the abbreviated explaination, I was more asking about procedure than code, but since there is interest ;)
    The culprit is here:
    @@ -395,10 +395,7 @@ if ($realpack) { # we have a blessed ref $out .= ', \'' . $realpack . '\'' . ' )'; - my $toaster = $s->{toaster}; - if (UNIVERSAL::can($val, $toaster)){ - $out .= '->' . $s->{toaster} . '()' if $s->{toaster} ne ''; - } + $out .= '->' . $s->{toaster} . '()' if $s->{toaster} ne ''; $s->{apad} = $blesspad; } $s->{level}--;

    Without the patch, if I set up an instance of Data::Dumper as such:
    my $dumper = new Data::Dumper(undef,'thaw')-> Freezer('DUMPER_freeze')->Toaster('DUMPER_thaw');
    and one or more of the serialized objects is frozen like such:
    my $freeze=$dumper->Values($ref_to_some_object)->Dump;
    All serialized output will be of the form:
    bless(DATA, CLASS)->(DUMPER_thaw);
    so when I attempt to reconstitute it (thaw it):
    my $thaw; # 'thaw' is the handle that I told DD to use eval $freeze # sets thaw
    DD calls the DUMPER_thaw method on the object, whether it had it or not. If it has it, fine, use it. Otherwise, DD should use its default thawing capabilities.
    Currenlty it will die with:
    WARNING(Freezer method call failed): Can't locate object method "DUMPER_freeze" via package "TestAutoDBOutside_1" at /tools/lib/perl5/5.8.4/i686-linux-thread-multi/Data/Dumper.pm line 158. ... Attempt to free temp codematurely: SV 0x83ee194 at /tools/lib/perl5/5.8.4/i686-linux-thread-multi/Data/Dumper.pm line 501. Scalars leaked: 1

    which all look like warnings, but the object never gets thawed.
    Here is the test wich I included with the patch that explains things more thoroughly (it should only pass after DD is patched):
    package DD_test; use lib qw(../blib/lib ../blib/arch); use Data::Dumper; use Test::More qw/no_plan/; $Data::Dumper::Useperl = 1; my $DUMPER=new Data::Dumper([undef],['thaw'])->Purity(1)->Indent(1)->Freezer('DUMPER_ +freeze')->Toaster('DUMPER_thaw'); my $f = new freezable; my $t = new unthawable; $f->{_OTHER} = new unthawable; $t->{_OTHER} = new freezable; my ($thaw); print "-" x 100, "\n"; print "Freezable\n"; print "-" x 100, "\n"; my $th = $DUMPER->Values([$f])->Dump; #print Dumper $th; eval $th; #sets $thaw #print Dumper $thaw; isa_ok($thaw,'freezable'); is($thaw->oid, 1); isnt($thaw->can('DUMPER_thaw'),undef); is($thaw->{_OTHER}->oid, 3); is($thaw->{_OTHER}->can('DUMPER_thaw'),undef); undef $thaw; print "-" x 100, "\n"; print "Unfreezable\n"; print "-" x 100, "\n"; my $th2 = $DUMPER->Values([$t])->Dump; #sets $thaw #print Dumper $th2; eval $th2; #sets $thaw #print Dumper $thaw; is($thaw->oid, 3); is($thaw->can('DUMPER_thaw'),undef); is($thaw->{_OTHER}->oid, 1); isnt($thaw->{_OTHER}->can('DUMPER_thaw'),undef); ## freezable package package freezable; sub new { my($self)=@_; return bless {}, $self; } sub oid { return 1; } sub DUMPER_freeze { my($self)=@_; print ">>> DUMPER_freeze ",$self->oid,"\n"; $self->{_OID} = $self->oid; $self->{_CLASS} = ref $self; return $self; } sub DUMPER_thaw { my($self)=@_; print "<<< DUMPER_thaw ", $self->oid, "\n"; return $self; } sub oid2object { shift @_ unless ref($_[0])=~/DBI::/; %__PACKAGE__::OID_2_OBJECT=shift @_ if @_; return \%$__PACKAGE__::OID_2_OBJECT; } ## unthawable package (no DUMPER_freeze, DUMPER_thaw method) package unthawable; sub new { my($self)=@_; return bless {_OID=>$self->oid,_CLASS=>$self}, $self; } sub oid { return 3; }

      I see. The problem is not with the existence of the Freezer method but the Toaster method, now I understand. This actually looks like a real bug to me. I don't think your code is entirelycorrect though. You call the UNIVERSAL::can() even if the Toaster is not specified. I think the whole change needed is the condition on the line that adds the toaster call:

      if ($realpack) { # we have a blessed ref $out .= ', \'' . $realpack . '\'' . ' )'; - $out .= '->' . $s->{toaster} . '()' if $s->{toaster} ne ''; + $out .= '->' . $s->{toaster} . '()' + if $s->{toaster} ne '' and UNIVERSAL::can( $val, $s->{toaster +}); $s->{apad} = $blesspad; }

      Update: The problem is that this tests for the Toaster method while the data is serialized/exported, not while it's deserialized/imported. Which might make a difference. With the original code as soon as the export was created with Toaster set to something you can get "notified" about all restored objects, all you have to do is to create a sub in UNIVERSAL package. With your (or mine) change your method only gets called for objects that had the Toaster method at the time of the export.

      You could change Data::Dumper to generate something like this:

      ... map( (UNIVERSAL::can( $_, 'Toaster') ? $_->Toaster() : $_), bless ( {}, 'Package::Name') ), ...
      but I think the easiest solution is to define
      sub UNIVERSAL::Toaster { $_[0] }
      in your script/module and all classes that do not define their own Toaster will inherit this default one.

      Jenda
      Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
         -- Rick Osborne

        Thanks for the optimization, I'll use it for the Dumper.pm patch.

        > The problem is that this tests for the Toaster method while the data is serialized/exported, not while it's deserialized/imported.

        This is true, but for my purposes, this is better. Since the serialized string is being inserted into the database, I don't want it to have the thaw method associated with it unless it has a thaw method (justification: its misleading to the user)

        > but I think the easiest solution is to define
        > sub UNIVERSAL::Toaster { $_[0] }

        ahh, but I don't have control over any non-AutoDB objects (I can't inject any methods into their space). Non-AutoDB objects are persisted only because they are pointed to by AutoDB objects (DD happily recurses over all references and builds a structure). But they have no ancestral connection to AutoDB.

        Thus, this method won't work for me :(