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

NAME

Class::Maker - classes, reflection, schemas, serialization, attribute- and multiple inheritance

SYNOPSIS

  use Class::Maker qw(:all);

  class Something;

  class Person,
  {
     isa => [ 'Something' ],

     public =>
     {
       scalar => [qw( name age internal )],
     },

     private
     {
       int => [qw( internal )],
     },
  };

  sub Person::hello
  {
    my $this = shift;

    $this->_internal( 2123 ); # the private one

    printf "Here is %s and i am %d years old.\n",  $this->name, $this->age;
  };

  my $p = Person->new( name => Murat, age => 27 );

  $p->hello;

DESCRIPTION

This package is for everybody who wants to program oo-perl and does not really feel comfortable with the common way. Class::Maker introduces the concept of classes via a "class" function. It automatically creates packages, ISA, new and attribute-handlers. The classes can inherit from common perl-classes and class-maker classes. Single and multiple inheritance is supported.

Reflection is transparently implemented and allows one to inspect the class properties and methods during runtime. This is helpfull for implementing persistance and serialization. A Tangram (see cpan) schema generator is included to the package, so one can use Tangram object-persistance on the fly as long as he uses Class::Maker classes.

INTRODUCTION

When you want to program oo-perl, mostly you suffer under the flexibility of perl. It is so flexibel, you have to do alot by hand. Here an example (slightly modified) from perltoot perl documentation for demonstration:

 package Person;

 @ISA = qw(Something);

 sub new {
    my $self  = {};
    $self->{NAME}   = undef;
    $self->{AGE}    = undef;
    bless($self);           # but see below
    return $self;
 }

 sub name {
    my $self = shift;
    if (@_) { $self->{NAME} = shift }
    return $self->{NAME};
 }

 sub age {
    my $self = shift;
    if (@_) { $self->{AGE} = shift }
    return $self->{AGE};
 }

C++ has really straightforward class decleration style. It looks really beautiful. At that time many cpan modules tried to compensate with perl idiom, i still rather missed something. This package though has a "class" function which transparetly decleares perl classes with some rememberance to other languages. It smoothly integrates into perl code and handles may issues a beginner would immediately stumble (such as package issues etc). So the above example could be now written as:

 use Class::Maker qw(class);

 class 'Person',
 {
   isa => [ 'SomeBaseClass' ],

   public =>
   {
     scalar => [qw( name age )],
   },
 };

When using "class", you do not explictly need "package". The function does all symbol creation for you. It is more a class decleration (like in java/cpp/..). So here we now leap into the documentation.

FUNCTIONS

class()

The 'class' function is very central to Class::Maker.

 class 'Class',
 {
   ..FIELDS..
 };

[Note] The parantheses for the class() function are optional.

Here 'Class' is the Name for the class. It is also the name for the package where the symbols for the class are created. Examples: 'Animal', 'Animal::Spider', 'Histology::Structures::Epithelia'.

Normally the class is created related to the main package:

  package Far::Far::Away;

  class 'Galaxy',
  {
  };

Like with package 'Galaxy' would become to 'main::Galaxy' (and not Far::Far::Away::Galaxy).

FIELDS

Fields are the keys in the hashref given as the second (or first if the first argument (classname) is omitted) argument to "class". Here are the basic fields (for adding new fields read the Class::Maker::Basic::Fields).

isa => aref

Same as the @ISA array in an package (see perltoot).

Some short-cut syntax is available:

a) when the name is started with an '.' or '*' the package name is extrapolated to that name:

 package Far::Far::Away;

 class Galaxy,
 {
   isa => [qw( .AnotherGalaxy )],
 };

Then '.AnotherGalaxy' becomes expanded to 'Far::Far::Away::AnotherGalaxy'.

public => href

 public => 
 {
   int => [qw(id)],
 },

leads to a attribute-handler which can be used like:

 $obj->id( 123 );

 my $value = $obj->id;

Because the default handler is an lvalue function, the following call is also valid:

 $obj->id = 5678;

These keys are 'type-identifiers' (no fear, its simple), which help you to sort things. In general these are used to create handlers for the type. It is somehow like the get/set like method functions to access class-properties, but its more generalized and not so restrictive. By default, every non-known type-identifier is a simple scalar handler. Class::Maker will not warn you at any point, if you use a unknown type-identifier. So that

   public =>
   {
    scalar => ...
    array => ...
    hash => ...

    _anthing_here_ => ..    
   }

Because b<array> and b<hash> are internally decleared and creating special mutators/handlers they will be not create scalar handlers, but 'scalar' and '_anything_here' will create scalara mutators, as they are forwarded to the default scalar handlers; both are internally not explicitly defined. The mechanism is extendable, see Class::Maker::Basic::Fields.

private => href

All properties in the 'private' section, get a '_' prepended to their names.

 private =>
 {
   int => [qw(uid gid)],
 },

So you must access 'uid' with $obj->_uid();

 public =>
 {
   int => [qw(uid gid)],

   string => [qw(name lastname)],

   ref => [qw(father mother)],

   array => [qw(friends)],

   custom => [qw(anything)],
 },

Nothing more, nothing less. The significant part is that no encapsulation as such is present (as in cpp). The only encapsulation is the "secret" that you have to prepend and '_' in front of the name.

configure => href

This Field is for general options. Basicly following options are supported:

a) new: The name of the default constructor is 'new'. With this option you can change the name to something of your choice. For instance:

 configure => 
 {
   new => 'connect'
 },

Could be used for database objects. So you would use

 my $obj AnyClass->connect( );

to create an AnyClass object.

PS. Class::Maker provides a very sophisticated default constructor that does a lot (including the inhertance issues) and is explained somewhere else.

c) private: Prefix string (default '_') for private functions can be changed with this.

 private =>
 {
   int => [qw(dummy1)],
 },
 
 configure =>
 {
   private => { prefix => '__' },
 },

would force to access 'dummy1' via ->__dummy1().

automethod

Reserved.

has

Reserved. Is planned to be used for 'has a' relationships.

default => href

Give default values for class attributes. It is the same as the handler was called with the value within the _postinit function.

 default =>
 {
   name => 'John',

   friends => [qw(Petra Jenna)],
 },

So after construction the CLASS->name method would return 'John' etc.

version => scalar

Give the class/objects a version number. Internally the $VERSION is set to that value.

persistance => href

Here you can set options and add information for the reflect-function. You can also add custom information, you may want to process when you reflect objects.

For example the tangram-schema generator looks for an 'abstract' key, to handle this class as an abstract class:

 persistance =>
 {
   abstract => 1,
 },

You can read more about Persistance under the Class::Maker::Extension::Persistance manpage.

Global flags

$Class::Maker::explicit

Internally an instance of a class holds all properties/attributes in an hash (The object is blessed with a hash-ref). The keys are normally exactly the same as you declare in the descriptors. In special cases you want inheritance per se, but still might be interested to call parent methods explicitly. Put another way, when you use 'soft' inheritance, you may have name clashes if a parent object uses the same name for a property as its child. To compensate that problem, set this global (very early in your program, best is BEGIN block) explicit to something true (i.e. 1). This will lead to internal prepending of the classname to the key name:

BEGIN { $Class::Maker::explicit = 1; }

'A' inherits 'B'. Both have a 'name' property. With explicit internally the fields are distinct:

 A::name
 B::name

[Note] This does not collide with attribute-overloading/inheritance ! Because the first attribute-handler in the isa-tree is always called. You do not have to care for this. Only use this feature, if you have fear that name clashes could appear, beside overloading. Per default it is turned off, because i suppose that most class designers care for name clashes themselfs.

INTERNALS

For this example:

 class 'Person',
 {
   isa => [ 'SomeBaseClass' ],

   public =>
   {
     scalar => [qw( name age )],
   },
 };

Following happens in the background, when using 'class':

1. creates a package "Person".
2. sets @Person::ISA to the [ 'SomeBaseClass' ].
3. creates method handlers for the attributes (including lvalue methods). While "hash" and "array" keys are really functional keywords, any other key will simply result in a scalar get/set method.
4. exports a default constructor (i.e."Person::new()") which handles argument initialization. It has also a mechanism for initializing the parent objects (including MI).
5. creates $Person::CLASS holding a hashref to the unmodified second argument to 'class' (or the first, if the package name is omitted). This is essential for reflection: i.e. you can get runtime information about the class. See below.

USING AN CLASS/OBJECT

CONSTRUCTION

Once a class is created it is shipped with a versatile new() constructor. It is central to Class::Maker because it deploy the object correctly, including constructing the multiple-inheritance chain and presetting class fields. To have fine grained control over the construction process following special methods are available for modification during construction. See the Class::Maker::Basic::Constructor deeper explanation of the construction process.

METHODS

DESTRUCTION

RESERVED SYMBOLS

%CLASS

As said, once a class is created Class::Maker creates a CLASS hash into the package. It is required for the process of runtime introspection (reflection). In general it is mostly similar to the FIELDS hash during decleration, but one shouldnt count on that, because it is surely modified in future. Refer to the Class::Maker::Basic::Reflection for correctly access the introspective freatures. Although today it has the function to:

- have dependency/class walking (see the contrib/ directory of the distribution for an example script).
- creating on-the-fly persistance => (for an example with Tangram see below)
- it creates the complete tangram schema tree (Tangram users know how hard it is

PERFORMANCE

I never seriously benchmarked Class::Maker. Because the internal representation is just the same as for standard perl-classes, only a minimal delay in the constructor (during scan through the class hirarchy for _init() routines) should be apparent. Beware that the accessors for any member of course delay the processsing (wildly guessed to be 3x slower). There is a hack-ish way to circumvent this, and may, increase speed when required: - directly going into the object gut with $this->{member}. Beware that the member can be hidden as ->{SUPERCLASS::member}.

EXAMPLES

All test files (test.pl and t/) are verbose enough for a good overview. Visit the Class::Maker::Examples manpage for examples how to write basic data-type-like classes and basic classes used for i.e. e-commerce applications.

EXPORT

facultative: qw(reflect schema) obligate: qw(class)

class by default.

[Note] If you care about ns pollution, just use Class::Maker::class directly.

Class::Maker::class 'Person', { ... };

KNOWN BUGS/PROBLEMS

isa => [qw( )] isnt in sync with @ISA. When @ISA (or isa) is modified after initation, the $reflex->{isa} will only represent the state during object initiation.

<& /maslib/signatures.mas:author_as_pod, &>

Contributions (Ideas or Code):

        - Terrence Brannon

COPYRIGHT

(c) 2001 by Murat Uenalan. All rights reserved. Note: This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

SEE ALSO

Class::Maker::Exception, Class::Maker::Basic::Fields, Class::Maker::Basic::Reflection, Class::Maker::Basic::Handler::Attributes,Class::Maker::Basic::Types,Class::Maker::Examples, Class::Maker::Generator, Class::Maker::Extension::Schema::Tangram.

Search for Class::Maker::* at CPAN

Also at CPAN: Class::*, Tangram

LITERATURE

[1] Object-oriented Perl, Damian Conway [2] Perl Cookbook, Nathan Torkington et al.

3 POD Errors

The following errors were encountered while parsing the POD:

Around line 754:

'=item' outside of any '=over'

Around line 763:

=back without =over

Around line 792:

You forgot a '=back' before '=head1'