Badger

 view release on metacpan or  search on metacpan

lib/Badger/Class.pm  view on Meta::CPAN

        debug => 0;

    # no need to declare 'our $DEBUG' - the above import hook did that
    print $DEBUG;           # 0

You can also use the class subroutine to modify remote classes, i.e. classes
other than the current one.

    class->('Existing::Class')->methods(
        wiz => sub {
            # new wiz() method for Existing::Class
        }
    );

You can construct entirely new classes on-the-fly.

    class('Amplifier')
        ->base('Badger::Base')
        ->constant( max_volume => 10 )
        ->methods( about => sub {
              "This amp goes up to " . shift->max_volume
          } );

    Amplifier->about;                   # This amp goes up to 10

And subclasses of your new subclasses.

    class('Nigels::Amplifier')
        ->base('Amplifier')
        ->constant( max_volume => 11 );

    Nigels::Amplifier->about;           # This amp goes up to 11

Being able to define new class on the fly using nothing more than a handful of
methods is really quite useful. You can take an existing class, subclass it,
tweak it, attach some custom methods, instantiate it and then call a method on
it, all in a single expression. You don't need to use any Perl statements or
keywords to get the job done, so there's no need to C<eval> any code (this
should make you feel warm and fuzzy in that special Badger place if
auto-generating classes is your thing).

=head2 CLASS INSPECTION

The C<Badger::Class> object provides a number of methods for inspecting
and manipulating the current class.  For example, there are methods to
set and get package variables for class.

    class->var( X => 10 );          # same as: $X = 10
    class->var('X');                # same as: $X

In this simple example, the effect is exactly the same as modifying the C<$X>
I<package> variable directly. However, this method (and related methods)
provides an abstraction of I<class> variables that works correctly with
respect to subclassing. That is, accessing a I<class> variable in a subclass
of C<Your::Module> will resolve to the I<package> variable in the subclass,
rather than the base class. If instead you write C<$X> then you'll always get
the variable in the base class package (which may be what you want, of
course).

A form of inheritance for class variables can be implemented using the
L<any_var()> method.  This looks for a package variable in the current class
or in any of the base classes.

    class->any_var('X');            # $X with @ISA inheritance

This idiom is particularly useful to provide default values for a class that
you might want to re-define later in a subclass. We'll look at some examples
of that shortly.

=head2 SUBCLASSING Badger::Class

The L<Badger::Class> module can itself be subclassed, allowing you to create
more specialised class metaprogramming modules to suit your own needs. For a
simple example, you can create a class module for a particular project that
hooks into your own modules that define constants, utility functions, and so
on.

    # defining a Badger::Class subclass...
    package My::Class;

    # ...using Badger::Class, of course
    use Badger::Class
        uber     => 'Badger::Class',
        constant => {
            CONSTANTS => 'My::Constants',
            UTILS     => 'My::Utils',
        };

The trick here is to use the C<uber> hook instead of C<base>. This is a
special case that applies only when you're subclassing C<Badger::Class> (or
another module derived from C<Badger::Class>). In addition to adding
C<Badger::Class> (or whatever class module you specify) as a base class of the
current module, it also performs some extra magic to ensure that the
L<class()> and L<classes()> subroutines return objects of your new class (e.g.
C<My::Class>) instead of C<Badger::Class>. You don't need to worry too much
about the details. Just use C<uber> instead of C<base> when you subclass a
C<Badger::Class> module and we'll take care of everything for you. See the
L<uber()> and L<UBER()> methods for further details.

Once your class module is defined, you can use it to generate new classes
for your application.

    # defining classes using your new class module
    package My::Example;

    use My::Class
        version   =>  2,                     # inherited Badger::Class options
        base      => 'My::Base',
        constants => 'black white blue',     # imported from My::Constants
        utils     => 'wibble frusset pouch'; # imported from My::Utils

You can easily create your own methods and corresponding import hooks to
implement whatever metaprogramming functionality you require for a
particular project.  Here's a trivial example which defines a method to
set a C<$FOO> package variable in the target class.

    package My::Class;

    use Badger::Class
        uber  => 'Badger::Class',
        hooks => 'foo';

lib/Badger/Class.pm  view on Meta::CPAN

You can also call C<class> as an object method. Perl implicitly passes the
object reference (traditionally called C<$self>) as the first argument So the
C<class> subroutine Just Works[tm] and returns a C<Badger::Class> object for
the object's class.

    package Your::Module;

    use Badger::Class 'class';

    sub introspect {
        my $self  = shift;          # object $self is first argument
        my $class = $self->class;   # same as class($self)

        # $class is an object, but gets auto-stringified to class name
        print "I am a $class instance\n";
    }

One important thing to understand is that calling C<class> as a method
will always return the relevant class for the object.  If C<$self> is
an instance of C<Your::Module>, then you'll get a C<Badger::Class>
object for C<Your::Module>.

    my $ym = Your::Module->new;
    $ym->introspect;                # I am a Your::Module instance

However, if C<$self> is an instance of a I<subclass> of C<Your::Module>, say,
C<My::Module>, then you'll get a C<Badger::Object> back for C<My::Module>
instead.

    package My::Module;
    use base 'Your::Module';

    package main;
    my $mm = My::Module->new;
    $mm->introspect;                # I am a My::Module instance

In this simple example it would have been just as easy to use C<ref> to find
out what kind of object we were dealing with, especially when all we're doing
is printing the class name. However, things get more interesting when we
combine that with the ability to inspect and define class variables.

Consider this base class module:

    package Amplifier;

    use Badger::Class
        base        => 'Badger::Base',
        import      => 'class',
        get_methods => 'max_volume';

    our $MAX_VOLUME = 10;

    sub init {
        my ($self, $config) = @_;
        $self->{ volume     } = 0;   # start quietly
        $self->{ max_volume } = $config->{ max_volume }
            || $MAX_VOLUME;
        return $self;
    }

The C<init()> method (see L<Badger::Base>) looks for a C<max_volume>
setting in the configuration parameters, or defaults to the C<$MAX_VOLUME>
package variable.

    my $amp = Amplifer->new;      # default max_volume: 10

So you're on ten here, all the way up, all the way up, all the way up,
you're on ten on your guitar. Where can you go from there? Where? Nowhere.
Exactly. What we do is, if we need that extra push over the cliff, you know
what we do?

    my $amp = Amplifier->new( max_volume => 11 );

Eleven. Exactly. One louder.

So far, so good. But what if we wanted to make this the default? Sure, we
could make ten louder and make that be the top number, or we could remember to
specify the C<max_volume> parameter each time we use it. But let's assume
we're working with temperamental artistes who will be too busy worrying about
the quality of the backstage catering to think about checking their volume
settings before they go on stage.

Thankfully we didn't hard-code the maximum volume but used the C<$MAX_VOLUME>
package variable instead. We can change it directly like this:

    $Amplifier::MAX_VOLUME = 11;

Or using the class L<var()> method (just to show you what the roundabout way
looks like):

    Amplifier->class->var( MAX_VOLUME => 11 );

Either way has the desired effect of changing the default maximum volume
setting without having to go and edit the source code of the module.

The downside to this is that it is an all-encompassing change that will affect
all future instances of C<Amplifier> and any subclasses derived from it that
don't define their own C<max_volume> parameter explicitly.

But what if that's not what you want? What if you're playing a Jazz/Blues
festival on the Isle of Lucy, for example, or performing a musical trilogy in
D minor, the saddest of all keys? In that case you don't want to change I<all>
the amplifiers, just I<some> of them.

This is the kind of problem that is easily solved by using inheritance. Your
base class amplifier defines the default properties and behaviours for the
I<general case>, leaving subclasses to reimplement anything that needs
changing for more I<specific cases>.  All the bits that don't get redefined
by a subclass are automatically inherited from the base class.

The only problem is that Perl's limited OO model only applies inheritance to
methods and not package variables. However, we can use the C<Badger::Class>
object to roll our own inheritance mechanism for package variables where
needed.

Let's look again at the relevant line from the C<init()> method where
the C<max_volume> is set:

    $self->{ max_volume } = $config->{ max_volume }
        || $MAX_VOLUME;

Rather than accessing C<$MAX_VOLUME> directly, we can instead use the
class object to fetch the value of the C<$MAX_VOLUME> class variable for us.

    $self->{ max_volume } = $config->{ max_volume }
        || $self->class->var('MAX_VOLUME');

This will continue to work as before for all instances of C<Amplifer>. It's a
little more long-winded and involves an extra method call or two, but it has
the benefit of working correctly with respect to inheritance. That means we
can now subclass C<Amplifier> and define a different default value for
C<$MAX_VOLUME>.

    package Nigels::Amplifier;
    use base 'Amplifier';
    our $VOLUME = 11;

The C<init()> method will now look for the C<$MAX_VOLUME> variable in our
subclass package (C<Nigels::Amplifier>) instead of the base class
package (C<Amplifier>).

One further enhancement we can make is to use L<any_var()> instead of
L<var()>.

    $self->{ max_volume } = $config->{ max_volume }
        || $self->class->any_var('MAX_VOLUME');

If you don't define a new C<$MAX_VOLUME> class variable in the subclass then
C<any_var()> will walk upwards through all the base classes until it finds



( run in 0.620 second using v1.01-cache-2.11-cpan-364913b4093 )