Badger::Prototype(3pm)

NAME
Badger::Prototype - base class for creating prototype classes

SYNOPSIS
    package Badger::Example;
    use base 'Badger::Prototype';

    sub greeting {
        my $self = shift;

        # get prototype object if called as a class method
        $self = $self->prototype() unless ref $self;

        # continue as normal, now $self is an object
        if (@_) {
            # set greeting if called with args
            return ($self->{ greeting } = shift);
        }
        else {
            # otherwise get greeting
            return $self->{ greeting };
        }
    }

DESCRIPTION
This module is a subclass of Badger::Base that additionally provides the prototype() method. It is used as a base class for modules that have methods that can be called as either class or object methods.

    # object method
    my $object = Badger::Example->new();
    $object->greeting('Hello World');

    # class method
    Badger::Example->greeting('Hello World');

The prototype() method returns a singleton object instance which can be used as a default object by methods that have been called as class methods.

Here's an example of a greeting() method that can be called with an argument to set a greeting message:

    $object->greeting('Hello World');

Or without any arguments to get the current message:

    print $object->greeting;            # Hello World

As well as being called as an object method, we want to be able to call it as a class method:

    Badger::Example->greeting('Hello World');
    print Badger::Example->greeting();  # Hello World

Here's what the greeting() method looks like.

    package Badger::Example;
    use base 'Badger::Prototype';

    sub greeting {
        my $self = shift;

        # get prototype object if called as a class method
        $self = $self->prototype() unless ref $self;

        # continue as normal, now $self is an object
        if (@_) {
            # set greeting if called with args
            return ($self->{ greeting } = shift);
        }
        else {
            # otherwise get greeting
            return $self->{ greeting };
        }
    }

We use "ref $self" to determine if greeting() has been called as an object method ($self contains an object reference) or as a class method ($self contains the class name, in this case "Badger::Example"). In the latter case, we call prototype() as a class method (remember, $self contains the "Badger::Example" class name at this point) to return a prototype object instance which we then store back into $self.

        # get prototype object if called as a class method
        $self = $self->prototype() unless ref $self;

For the rest of the method we can continue as if called as an object method because $self now contains a "Badger::Example" object either way.

Note that the prototype object reference is stored in the $PROTOTYPE variable in the package of the calling object's class. So if you call prototype on a "Badger::Example::One" object that is subclassed from "Badger::Prototype" then the prototype object will be stored in the $Badger::Example::One::PROTOTYPE package variable.

METHODS

  prototype(@args)
Constructor method to create a prototype object and cache it in the $PROTOTYPE package variable for subsequent use. This is usually called from inside methods that can operate as class or object methods, as shown in the earlier example.

    sub example {
        my $self = shift;

        # upgrade $self to an object when called as a class method
        $self = $self->prototype() unless ref $self;

        # ...code follows...
    }

If you prefer a more succinct idiom and aren't too worried about calling the prototype method unnecessarily, then you can write it like this:

    sub greeting {
        my $self = shift->prototype;
        # ...code follows...
    }

If any arguments are passed to the prototype() method then it forces a new prototype object to be created, replacing any existing one cached in the $PROTOTYPE package variable. The arguments are forwarded to the new() constructor method called to create the object.

If a single undefined value is passed as an argument then any existing prototype is released by setting the $PROTOTYPE package variable to "undef". The existing prototype is then returned, or undef if there was no prototype defined.

  has_prototype()
Returns true or false to indicate if a prototype is defined for a class. It can be called as a class or object method.

AUTHOR
Andy Wardley <http://wardley.org/>

COPYRIGHT
Copyright (C) 2006-2009 Andy Wardley. All Rights Reserved.

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