The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

NEXT::init

DWIM data inherited data initialization via NEXT.

SYNOPSIS

Data can be hash or array based.

Hashes use EVERY::LAST to assign slices (i.e., derived classes override the base class' values, assign new keys).

Arrays use either EVERY::init (queue) or EVERY::LAST (stack) to push each layer's data onto the object.

Hash-based

Each level in the init does an array-slice assignment, overwriting values from less-derived classes as necessary.

Given:

        package Base_Hash;

        use NEXT::init
        {
                foo => 1,

                package => 'Base'
        };

        package Derived;

        use base qw( Base_Hash );

        use NEXT::init
        {
                bar => 1,

                package => 'Derived'
        };

    Base:

            {
                    foo => 1,
                    package => 'Base'
            }

    Derived:

            {
                    foo => 1,
                    bar => 1,
                    package => 'Derived'
            }

Array-based

There are two flavors of array: queue and stack.

Both push successive class' data onto the object. The difference is that queues use EVERY::init to push the data going down the inheritence tree; stacks use EVERY::LAST to push it going back up.

queues leave the arguments at the front of the list where shift or for(@$obj) will find them first; stacks leave the argumnts at the end where pop will get them.

For example, from test.pl:

Given a base class of "MyArray" and two derived classes, one queue one stack:

        package MyArray;

        use NEXT::init [ qw( a b c ) ];

        package Queue;

        use base qw( MyArray );

        use NEXT::init qw( :type=queue d e f );

        package Stack;

        use base qw( MyArray );

        use NEXT::init qw( :type=stack d e f );

Calling the constructor as:

        my $obj8 = Queue->construct( qw( queue ) );

        my $obj9 = Stack->construct( qw( stack ) );

Yields a queue of:

        bless( [
        'queue',
        'd',
        'e',
        'f',
        'a',
        'b',
        'c'
        ], 'Queue' )

and a stack of:

        bless( [
        'a',
        'b',
        'c',
        'd',
        'e',
        'f',
        'stack'
        ], 'Stack' )

DESCRIPTION

This is a generic initializer class for objects that need to inherit hash or array data. When use-ed the import module installs "init" and "constructor" method which merge each level of inherited data and the arguments into the object. A method for accessing the class data and the class data itself are installed in *meta.

The resulting objects do not inherit anything from NEXT::init (i.e., it is not a base class). The constructor uses whatever classes the object is based on to locate "init" methods during object construction; NEXT::init only provides an import sub to install the methods and validate the base data types.

For effeciency the init handers are closures which handle either hash or array data propery (i.e., no repeated if- logic, changing the base data type of the object after construction will likely break the associated init handlers).

The object's type (hash or array) is determined either by the type of referent passed into init or an initial ":type=foo" argument for arrays.

Classes used as bases for other classes should probably just pass the data in as a referent to begin with:

        use NEXT::init
        {
                key => value
        };

for hashes or

        use NEXT::init
        [
                array 
                data
        ];

for arrays.

Derived classes can then just pass in lists, which will be formatted appropriately based on the ref type of the base classses:

        use NEXT::init
        (
                simple
                list
                goes
                here
        );

Installed symbols

construct

Normal use of the classes will be via:

        ClassName->construct( runtime => 'arguments' );

The arguments will be converted to an approprpriate type for the data.

init

This called from the constructor to add class data to the object. This is where hashes are udpated via hash slice and arrays via push.

There is no reason to call this externally.

%meta and @meta

The values passed into NEXT::init via use are installed as *$class::meta = ref via the Symbol module. This allows the class to inspect or modify its class data. A method named 'meta' is installed which returns a reference to the class metadata. One use for this is in NEXT::init's import, where having an unblessed referent simplifies determining the base type.

The value of $meta is built from Symbol, with the hash-or-array data assigned via referent:

        my $meta  =   qualify_to_ref 'meta', $caller;

        *$meta = $data;

The initializers are closures which use the lexical $meta glob to access the class data:

        @{$object}{ keys %{*$meta} } = values %{*$meta}

        push @$object, @{ *$meta };

The package can simply refer to %meta or @meta:

        use strict;

        use NEXT::init { qw( foo bar ) };

        scalar %meta

        __END__

works just fine (with %meta installed as a valid package symbol before strict complains about %meta being unknown).

Import arguments

:type=hash
:type=queue
:type=stack

Given a hash or array referent as input these default to hash or queue respectively. The only real use for these is passing in simple-list data (vs. a referent) for base classes [i.e., artistic preference] or setting array types to stack.

Setting this to a value that does not match the base type in a derived class (e.g., :type=hash with a base class of queue or stack) is fatal.

Mixing and matching queue and stack types is allowed and works pretty much as expected since both types push their data at each stage.

:verbose
:verbose=1

Defaults to one if provided, use ":verbose=0" to turn it off, prints a bit more information during the import cycle.

Main use is in #! code to control debugging of classes called from the main code:

        #!/opt/bin/perl

        use NEXT::init qw( :verbose=1 );

Will turn on verbose output for any subsequent classes.

The remainder of these are really only intended for development.

:debug
:debug=1
:debug=2

Defaults to zero normally, to one if provided. This is used in the code as:

        $DB::single = 1 if $debug;

or

        $DB::single = 1 if $debug > 1;

This is also mainly for use in #! code (see :verbose, above).

:export
:export=1

Controls whether the symbols are exported after the arguments are processed. Defaults to 0 if the calling package is "main". This is mainly for internal use to allow #! code's setting verbose and debug.

AUTHOR

Steven Lembark (lembark@wrkhors.com)

To Do

- Deal with non-array/-hash referent types (suggestions welcome). CODE ref's might be executed in the proper order but that doesn't seem much like "inheritence".

COPYRIGHT

This code is released as-is under the same terms as Perl-5.8 (or any later version of perl at the users preference).

SEE ALSO

NEXT perlreftut perlootut