folks,

attached is 8k of module code (small enough to just send ?)
with the candidate name;

Data::Dumper::Functor

This is a structural name, but other candidates are too ambiguous,
and somewhat presumptuous;

DD::Easy, DD::Auto, DD::Lazy, DD::Brief


BTW - it has a package main section at bottom, you can just run it to see a simple sanity-test.

perl Functor.pm



Comments about the code itself are also welcome, off-list if you
think that doesnt belong here.

tia
jimc
#!perl

package Data::Dumper::Functor;
use Data::Dumper;

our VERSION = 0.01;

=head1 NAME

Data::Dumper::Functor - wraps DD for easy use of printing styles

=head1 SYNOPSIS

use Data::Dumper::Functor;

{
    # build one
    my $ddf = Data::Dumper::Functor->new(indent=>1,terse=>1);

    # use ddf obj repeatedly
    print "default: ", $ddf->($_) for @userdata;

    # auto-labelling
    print "labelled: ", $ddf->(foo => $_) for @userdata;

    # alter existing functor, and reuse
    $ddf->Indent(0);
    print "default: ", $ddf->($_) for @userdata;

    # alter many params at once
    $ddf->Set(indent=>1,terse=>0,sortkeys=>1);
    print "default: ", $ddf->($_) for @userdata;

}

=head1 DESCRIPTION

This package wraps Data::Dumper, and adapts its API for easier control
of output format.

=head1 Data::Dumper OO API usage problems this addresses

In following docs, I often use DD as shorthand for Data::Dumper, and
OO for its Object Oriented API.

=head2 calls to OO-DD are Too Verbose

For everything but exported Dumper(), invoking is needlessly baroque.
This class provides a functor, so that printing data is as easy/brief
as possible; you dont even need a method name!

=head2 Too Hard to Label data

    print Data::Dumper->Dump([$a,$b,$c], [qw(a b c)]);

This non-OO usage is just too punctuation intensive, too dependent on
having exactly 2 arrayref args, and the label position is
counterintuitive; ie after the data (ie: B<< tag => value >>).

=head2 Format Control is Cumbersome

{
    local $Data::Dumper::Indent = 1;
    local $Data::Dumper::Sortkeys = 1;
    print Dumper (@foo);
}

Without using OO form, your only choice wrt print-style is either
localizing package variables each time you use Data::Dumper, or
changing them globally.

=head2 Early Binding of Data

    # OO usage
    foreach $datum (@data) {
        $d = Data::Dumper->new($datum);
        $d->Purity(1)->Terse(1)->Deepcopy(1);
        print $d->Dump;
    }

In DD OO, you must provide the data to be dumped when the object is
created.  Only afterwards can you control that objects print format.
This means lots of extra typing and function calls, thus discouraging
use of OO style.  I often live with indent=2, which I personally find
harder to read than indent=1.

=head1 FEATURES

Ok, so the functor thing may be over-cool, after all $d->Dump isnt so
bad.  It was an experiment (in blessed coderefs) that went well enough
to polish and publish.

=head2 Auto-Labelling

Arguments are checked to see if they can be interpreted as labels, ie
values at odd indexes must be scalars.  If this test passes, the data
is rendered using DD labelling feature, see L<"Too Hard to Label
Data"> above.

This maybe a bit aggressive for your tastes, but I dont often use DD
with simple scalars, and my habit with Dumper() is to always pass a
single data-arg anyway.  YMMV.

=head2 Speed 

Untested yet, but Dumper() builds a new DD object for each print,
which presents opportunities for real runtime improvements.
    The 'cost' is some hackery at Data::Dumper internals.

=head1 Possible Applications

A client-class dumper.  With a singleton in a pkg-var or file-my-var,
you can set the B<sortkeys> attr to dump only the object keys you care
to see for debugging purposes.  A small number of such specialists
should serve all your needs.


=head1 IMPLEMENTATION

The class builds a blessed CODEref object, for which perl also allows
method-lookups.  This hybrid nature is key to the viability of the
design.

new() builds a Data::Dumper object, then builds and returns a closure
on that DD object.  The closure provides the interface to DD::Dump,
and also provides access to the underlying DD object for the other
methods; Set() and AUTOLOAD().

AUTOLOAD() provides an interface to all of DDs format-control and
maintenance methods.  Set() is a 'convenience' method that allows
setting of multiple DD properties simultaneously.  AUTOLOAD actually
calls Set(), as the latter checks that the properties being set have a
corresponding setter method.

Set() doesnt provide accessor facilities, as most of the functions
return the DD object in support of method chaining, and thus cannot
return the attribute values.

=head1 CAVEATS

 Brand new code.
    developed on 5.8.2, and may be dependent on advanced features.
    No version dependency in yet, Im hoping CPANTS will tell me ;-)

 too much dependency on DD attrs,
    partly/mostly/completely solvable with existing DD methods

 format control not per-use (object only)
    it may be possible to use 'defined wantarray' to implement.

 no global format control (will add import tags)

 auto-labelling may be overzealous.

 flexibility wrt capitalization of attr/methods
    can issue warnings with different capitalization from usage

 not *entirely* data-agnostic,
    pass '__SA__' to functor iff you want the underlying DD object

 Comments welcome.

=head1 ACKNOWLEDGMENTS

Gurusamy Sarathy for writing DD, I love it and use it *ALL* the time,
its often my microscope of choice.  I cant leave the above critique as
the only commentary.  

=head1 AUTHOR

Jim Cromie <[EMAIL PROTECTED]>

Copyright (c) 2003 Jim Cromie. All rights reserved.  This program is
free software; you can redistribute it and/or modify it under the same
terms as Perl itself.

I dont suppose Ill ever recover the development time via reduced
keystrokes, but CPAN has saved me so much already; heres a little
give-back.  Anyway, perl is fun, like an always-new toy.

=cut

##################

package Data::Dumper::Functor;
use Data::Dumper;
use Carp 'carp';

sub AUTOLOAD {
    my ($ddf, $arg) = @_;
    (my $meth = $AUTOLOAD) =~ s/.*:://;
    return if $meth eq 'DESTROY';
    $ddf->Set($meth,$arg);
}

# 1 forces method test via symbol table
my $viasymtbl = 0;      # 1 doesnt work !

sub Set {
    # sets internal state of private data dumper object
    my ($ddf, %cfg) = @_;

    my $ddo = $ddf->('__SA__') if ref $ddf eq __PACKAGE__;
    #print "setting ddo: ", Dumper $ddo, \%cfg if %cfg;

    for my $item (keys %cfg) {
        #print "$item => $cfg{$item}\n";

        if ($viasymtbl) {
            my $meth = ucfirst $item;
            unless (\&{"Data::Dumper::$meth"}) {
                carp "illegal $item on DDobj\n"
                    .\&{"Data::Dumper::$meth"};
                next;
            }
            $ddo->$meth($cfg{$item});
        }
        else { # direct ddo update
            my $meth = ucfirst $item;
            my $attr = lc $item;
            unless (exists $ddo->{$attr}) {
                carp "illegal method <$attr>";
                next;
            }
            # 
            eval { $ddo->$meth($cfg{$item}) };
            carp "illegal method <$attr>" if $@;
        }
    }
}

sub new {
    my ($cls, %cfg) = @_;

    my $ddo = Data::Dumper->new([]); # bogus data
    Set($ddo, %cfg);

    my $code = sub { # closure on $ddo
        my @args = @_;
        $ddo->Reset unless $ddo->{ddf_noreset};

        if (@args == 1) {
            # special access for AUTOLOAD
            return $ddo if $args[0] eq '__SA__';

            # else Regular usage
            $ddo->{todump} = [EMAIL PROTECTED];
            return $ddo->Dump();
        }
        # else
        if (not @args % 2) {
            # possible labelled usage - check!

            my %rev = reverse @args;
            if (grep {ref $_} values %rev) {
                # odd elements are refs. print as array
                $ddo->{todump} = [EMAIL PROTECTED];
                return $ddo->Dump();
            }
            my (@labels,@vals);
            while (@args) {
                push @labels, shift @args;
                push @vals,   shift @args;
            }
            $ddo->{todump} = [EMAIL PROTECTED];
            $ddo->{names}  = [EMAIL PROTECTED];
            return $ddo->Dump();
        }
        else {
            $ddo->{todump} = [EMAIL PROTECTED];
            return $ddo->Dump();
        }
    };
    #print $code;
    return bless $code, $cls;
}

package main;

if ($0 =~ /Functor\.pm$/) {

    $foo = [qw/ hello there /];
    $bar = {qw/ alpha 1 beta 2 zed 26 /};
    
    $baseline = Data::Dumper->new([$foo], ['foo']);
    print $baseline->Dump();

    my $ddf = Data::Dumper::Functor->new;
    print "new thingy: ", Dumper $ddf;
    
    print "used on \$foo: ", $ddf->($foo);
    print "used on \$bar: ", $ddf->($bar);

    print "w label \$foo: ", $ddf->(foo=>$foo);
    print "w label \$bar: ", $ddf->(bar=>$bar);
    
    $ddf->Indent(1);
    print "used on $foo: ", $ddf->($foo);

    $ddf->Set(indent=>1,sortkeys=>1);
    print "used on bar: ", $ddf->($bar);

    $ddf->poop(1);
    
    #print "used on $foo: ", $ddf->($foo);
}

1;

__END__

Reply via email to