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

NAME

Package::Data::Inheritable - Inheritable and overridable package data/variables

VERSION

Version 0.05

SYNOPSIS

  use strict;
  package Base;
  use base qw( Package::Data::Inheritable );

  BEGIN {
      Base->pkg_inheritable('$foo' => 'a not so ordinary package variable');
  }

  print $foo;


  package Derived;
  use base qw( Base );

  BEGIN {
      Derived->pkg_inheritable('$bar');
      Derived->pkg_inheritable('@baz' => [1,2,3]);

      inherit Base;
  }

  print $foo,
        @baz, $bar;

DESCRIPTION

This module tries to deliver inheritable package data (variables) with a reasonably convenient interface. After declaration the variables can be used like ordinary package variables. Most importantly, these variables can be inherited by derived classes (packages) by calling the inherit() method. If a derived class doesn't call inherit() it will still be able to define its variables and make them inheritable by its subclasses. Scalar variables can be declared constant.

Within your class (hierarchy) code you will benefit from compiler checks on those variables. The overall result is close to real class data members. Of course you can wrap your variables in accessor/mutators methods as you need.

The semantic provided mimics the class data members in languages like C++ and Java. When you assign to an inherited variable within a derived class, every class in the inheritance hierarchy will see the new value. If you want to override a variable you must redeclare it explicitly.

To declare inheritable variables two interfaces are provided: a method interface via pkg_inheritable() and pkg_const_inheritable(). an Exporter-like interface, via the array @EXPORT_INHERIT.

Inheriting always requires invoking the inherit() method. The variable visibility (scope) depends on the interface you used. If you use the Exporter-like interface, variables will be declared via our, while if you use the method interface it will be like you had imported those variables. The Exporter like interface does not currently support constants.

EXPORT

Package::Data::Inheritable is an Exporter, inheriting from it (via use base or @ISA) will make your class an Exporter as well. The package variable @EXPORT_INHERIT contains the symbols that will be inherited and @EXPORT_OK will always contain at least those symbols.

The Exporter like interface allows your class to set @EXPORT_INHERIT in pretty much the same way you would set @EXPORT and @EXPORT_OK with Exporter.

DEFINING AND INHERITING VARIABLES

Method interface

  BEGIN {
      Class->pkg_inheritable('$scalar');
      Class->pkg_inheritable('@array' => [1,2,3]);
      Class->pkg_const_inheritable('$const_scalar' => 'readonly');
      inherit BaseClass;
  }

Every variable declaration must be inside a BEGIN block because there's no 'our' declaration of that variable and we need compile time installation of that symbol in the package symbol table. The same holds for the call to inherit(), inherited variables must be installed at compile time.

Exporter like interface

  BEGIN {
      our @EXPORT_INHERIT = qw( $scalar @array );
      inherit BaseClass;
  }
  our $scalar;
  our @array = (1,2,3);

If you're defining variables, none of which is overriding a parent package's one (see overriding below), it's not required to define @EXPORT_INHERIT inside a BEGIN block. You will declare the variables via 'our' in the usual way. The actual our declaration of each variable must be outside the BEGIN block in any case because of 'our' scoping rules.

OVERRIDING VARIABLES

In order to override a parent variable you just have to redefine that variable in the current package. When you use the Exporter like interface and you want to override a parent package variable you must define @EXPORT_INHERIT before calling inherit(), otherwise inherit() will not find any of your overrides. On the contrary, if you use the pkg_inheritable() method interface, ordering doesn't matter.

METHODS

inherit

Make the caller package inherit variables from the package on which the method is invoked. e.g.

    package Derived;
      BEGIN {
        inherit Base;
        # or
        Base->inherit;
      }

will make Derived inherit variables from Base.

This method must be invoked from within a BEGIN block in order to install the inherited variables at compile time. Otherwise any attempt to refer to those package variables in your code will trigger a 'Global symbol "$yourvar" requires explicit package name' error.

pkg_inheritable

    Class->pkg_inheritable('$variable_name');
    Class->pkg_inheritable('$variable_name' => $value);
    Class->pkg_inheritable('@variable_name' => ['value1','value2']);

Method interface to declare/override an inheritable package variable. $variable_name will be installed in the package symbol table like it had been declared with use 'vars' and then initialized. The variable will be inherited by packages invoking inherit() on class 'Class'.

pkg_const_inheritable

    Class->pkg_const_inheritable('$variable_name');
    Class->pkg_const_inheritable('$variable_name' => $value);
    Class->pkg_const_inheritable('@variable_name' => ['value1','value2']);

Method interface to declare/override an inheritable constant package variable. It is similar to pkg_inheritable but the variable will be made constant. Only constant scalars are supported. It's possible to override a parent package var that was constant and make it non constant, as well as the opposite.

EXAMPLES

Inheriting and overriding

   # set up Base class with the method interface:
    use strict;
    package Base;
    use base qw( Package::Data::Inheritable );
   
    BEGIN {
        Base->pkg_inheritable('$scalar1' => 'Base scalar');
        Base->pkg_inheritable('$scalar2' => 'Base scalar');
        Base->pkg_inheritable('@array'   => [1,2,3]);
    }
   
    print $scalar1;         # prints "Base scalar"
    print @array;           # prints 123
   
   # set up Derived class with the Exporter like interface:
    package Derived;
    use base qw( Base );
   
    BEGIN {
        # declare our variables and overrides *before* inheriting
        our @EXPORT_INHERIT = qw( $scalar2 @array );
   
        inherit Base;
    }
    our @array = (2,4,6);
    our $scalar2 = "Derived scalar";
   
    print $scalar2;             # prints "Derived scalar"
    print $Base::scalar2;       # prints "Base scalar"
    print @array;               # prints 246
    print $scalar1;             # prints "Base scalar"

    $scalar1 = "Base and Derived scalar";
    print $Base::scalar1,       # prints "Base and Derived scalar" twice
          $Derived::scalar1;

Accessing and wrapping data members

Be aware that when you qualify your variables with the package prefix you're giving up compiler checks on those variables. In any case, direct access to class data from outside your classes is better avoided.

    use strict;
    package Base;
    use base qw( Package::Data::Inheritable );

    BEGIN {
        Base->pkg_inheritable('$_some_scalar'  => 'some scalar');
        Base->pkg_inheritable('$public_scalar' => 'public scalar');
    }

    sub new { bless {}, shift }

    # accessor/mutator example
    sub some_scalar {
        my $class = shift;
        if (@_) {
           my $val = shift;
           # check $val, caller etc. or croak...
           $_some_scalar = $val;
        }
        return $_some_scalar;
    }

    sub do_something {
        my ($self) = @_;
        print $public_scalar;           # ok
        print $Base::public_scalar;     # ok, but dangerous

        print $publicscalar;            # compile error

        print $Base::publicscalar;      # variable undefined but no compile
                                        #  error because of package prefix
    }

    package Derived;
    use base qw( Base );
    BEGIN {
        inherit Base;
    }
  
  And then in some user code:

    use strict;
    use Base;
    use Derived;
    
    print $Base::public_scalar;     # prints "public scalar". Discouraged.
    print Base->some_scalar;        # prints "some scalar"
    
    Base->some_scalar("reset!");
    my $obj = Base->new;
    print Base->some_scalar;        # prints "reset!"
    print $obj->some_scalar;        # prints "reset!"
    print Derived->some_scalar;     # prints "reset!"

    Derived->some_scalar("derived reset!");
    print Derived->some_scalar;     # prints "derived reset!"
    print Base->some_scalar;        # prints "derived reset!"

CAVEATS

The interface of this module is not stable yet. I'm still looking for ways to reduce the amount of boilerplate code needed. Suggestions and comments are welcome.

AUTHOR

Giacomo Cerrai, <gcerrai at cpan.org>

BUGS

Please report any bugs or feature requests to bug-package-data-inheritable at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Package-Data-Inheritable. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Package::Data::Inheritable

You can also look for information at:

SEE ALSO

Class::Data::Inheritable,

ACKNOWLEDGEMENTS

COPYRIGHT & LICENSE

Copyright 2007 Giacomo Cerrai, all rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.