Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

While working on a module for object persistence (Class::AutoDB), I had a need modify Data::Dumper very slightly - basically allowed for Dumper to check whether the Freezer method is defined for an object before calling it (that way every method doesn't HAVE to implement a Freezer method, only the ones that want to be serialized).

Anyway, I tried on a number of occasions to contact the maintainer but after at least three tries over the same number of weeks, I gave up and contacted the author (Gurusamy Sarathy), who told me to hand the patch off to perl-5-porters. I did so, but that was over 5 weeks ago. I have heard nothing since, despite my prompts for status.

The change is very minor, all of Data::Dumper's tests still pass and I included a test for Dumper to test the new functionality.

So my question is this: is it a no-no to just upload a new version of Data::Dumper to the CPAN with the patched source?

As of the latest Class::AutoDB release, I have inlcluded the patches and instructions for patching. But this is really quite unsatisfactory. I would rather just specify the latest version of Dumper because, after all, this is perl, not C++ :)

Replies are listed 'Best First'.
•Re: socio-political guidance sought
by merlyn (Sage) on Aug 20, 2004 at 00:31 UTC
    I've dumped objects before that don't have freezer method. Perhaps you can explain your quest a bit better.

    As far as Data::Dumper, it's owned by the core, so you should not mess with the process.

    If your explanation was a confusing to the P5P as it was here, I can imagine you've gotten very little feedback. {grin}

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

Re: socio-political guidance sought
by tilly (Archbishop) on Aug 20, 2004 at 03:06 UTC
    First of all it would be a major transgression to try to offer your own version of a core module. You don't want to go there. Please believe me on that.

    Secondly it would be good if you linked to the email with the patch that you sent to p5p. (There are several publically available websites that mirror that email list.)

    That may clarify what you're asking for. It sounds like you want Data::Dumper to not try to dump objects that don't have a Freezer method. If this is really what you're asking for, you will never get it. A lot of people like being able to dump objects by default. If you're asking for something else you'll need to be clearer before anyone will act on it.

    As for Class:AutoDB, I just glanced at the documentation. Without testing, it is a safe bet that you have serious bugs if the object winds up being destroyed during global destruction. With Perl 5.8 or better there is a hack that reduces how easily the bugs are hit. See Re (tilly) 1: using DESTROY to DBI disconnect and Re: Destructor Order for two posts where I mention this issue, and suggestions for how to avoid it. (Basically you have to do extra work to be sure that things will get cleaned up in an END block if they survived that long.) So even if you get nothing else out of this thread, you just got a pointer to a likely bug!

      The object isn't destroyed at global destruction (though I certainly ran head-first into that issue during the first implementations). When the object is created, the Class::AutoDB::SmartProxy class is unshifted onto its ISA array. SmartProxy handles the object's persistence at its own destruction time (in a DESTROY block).
      This seems to happen reliably, thoough I did implement a cache class for the dual purpose of speeding up in-memory access and acting as a container for flyweights (SmartProxy objects) to be destroyed all at once. Only the first purpose is being exercised since I prefer to have database I/O (during object destruction adn subsequent persistence) handled as needed.
Re: socio-political guidance sought
by ysth (Canon) on Aug 20, 2004 at 00:46 UTC
    Yes, it's a no-no, especially so for a core module.

    Keep trying perl5-porters; sometimes people get busy and miss messages. You will generally get more comment on your patch if it is included in the body of your message, or an uncompressed attachment. Also try putting [PATCH] in your Subject.

    The listed maintainer of the CPAN module is ilyam@cpan.org, but the change really needs to be accepted in the core before going in a cpan version.

    Does Data::Dump::Streamer do what you need? You might consider it instead.

Re: socio-political guidance sought
by Tuppence (Pilgrim) on Aug 20, 2004 at 02:31 UTC

    Getting your patch into Data::Dumper is fine and everything, but have you considered either subclassing Data::Dumper or implementing your specific requirements in a much less general module?

    That would at least make it easier for your users to use your module, which is really the point I suspect.

      yes, this is what I assumed to be the second best (right behind using the patched CPAN version) approach.

      Its second best because I would have to keep up with DD bug fixes in my subclass. But I agree that its, probably better than distributing a patch (which is also tied to the current DD version).

Re: socio-political guidance sought
by samtregar (Abbot) on Aug 20, 2004 at 15:01 UTC
    One option is to patch Data::Dumper on the fly. I did this in IPC::SharedCache so I could make my release before a bug in IPC::ShareLite was released. Something like (untested):

    no warnings 'redefine'; if ($Data::Dumper::VERSION < NEXT_VERSION) { *Data::Dumper::some_method = sub { # replacement code here }; }

    The upside here is that only users of your module will see the new Data::Dumper behavior, and you can document it in your module. The downside is that if Data::Dumper doesn't get your patch by NEXT_VERSION then you'll need to change your module to take it working.

    All that said, I think you should probably take a step back here. Are you sure you need to change Data::Dumper? It's an old and rather stable module. If it doesn't do what you want maybe you need to sub-class it or replace it with something more suited to your needs.

    -sam

Re: socio-political guidance sought
by Jenda (Abbot) on Aug 20, 2004 at 15:34 UTC

    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

      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-&gt;{toaster}; - if (UNIVERSAL::can($val, $toaster)){ - $out .= '-&gt;' . $s-&gt;{toaster} . '()' if $s-&gt;{toaster} ne ''; - } + $out .= '-&gt;' . $s-&gt;{toaster} . '()' if $s-&gt;{toaster} ne ''; $s-&gt;{apad} = $blesspad; } $s-&gt;{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 &quot;DUMPER_freeze&quot; via package &quot;TestAutoDBOutside_1&quot; 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 &quot;-&quot; x 100, &quot;\n&quot;; print &quot;Freezable\n&quot;; print &quot;-&quot; x 100, &quot;\n&quot;; 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 &quot;-&quot; x 100, &quot;\n&quot;; print &quot;Unfreezable\n&quot;; print &quot;-&quot; x 100, &quot;\n&quot;; 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 &quot;>>> DUMPER_freeze &quot;,$self->oid,&quot;\n&quot;; $self->{_OID} = $self->oid; $self->{_CLASS} = ref $self; return $self; } sub DUMPER_thaw { my($self)=@_; print &quot;&lt;&lt;&lt; DUMPER_thaw &quot;, $self->oid, &quot;\n&quot;; 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