Badger
view release on metacpan or search on metacpan
lib/Badger/Factory/Class.pm view on Meta::CPAN
#========================================================================
#
# Badger::Factory::Class
#
# DESCRIPTION
# Subclass of Badger::Class for creating Badger::Factory sub-classes.
#
# AUTHOR
# Andy Wardley <abw@wardley.org>
#
#========================================================================
package Badger::Factory::Class;
use Carp;
use Badger::Class
version => 0.01,
debug => 0,
uber => 'Badger::Class',
hooks => 'item path names default',
words => 'ITEM ITEMS',
utils => 'plural permute_fragments',
import => 'CLASS',
constants => 'DELIMITER ARRAY HASH',
constant => {
PATH_SUFFIX => '_PATH',
NAMES_SUFFIX => '_NAMES',
FACTORY => 'Badger::Factory',
};
# chicken and egg
# exports => {
# fail => \&_export_fail_hook,
# };
CLASS->export_before(\&_export_before_hook);
CLASS->export_fail(\&_export_fail_hook);
# catch a hook that has the same name as the items, i.e. widgets
sub _export_before_hook {
my ($class, $target) = @_;
my $klass = class($target, $class);
# special-case: we don't want to force the factory base class on
# Badger::Class if it's loading this module as the uber parent of a
# Factory::Class subclass (e.g. Template::TT3::Factory::Class).
return if $target eq 'Badger::Class';
$class->debug("$class setting $klass ($target) base class to ", $class->FACTORY)
if DEBUG;
$klass->base($class->FACTORY);
}
sub _export_fail_hook {
my ($class, $target, $symbol, $symbols) = @_;
my $klass = class($target, $class);
my $items = $klass->var(ITEMS);
# look for $ITEMS or fall back on plural($ITEM)
unless ($items) {
my $item = $klass->var(ITEM);
$items = plural($item) if $item;
}
# $target->debug("looking for $items to match $symbol\n");
# if the import symbols matches $items (e.g. widgets) then push the
# next argument into the relevant package var (e.g. $WIDGETS)
if ($items && $items eq $symbol) {
croak "You didn't specify a value for the '$items' load option."
unless @$symbols;
$klass->var( uc($items) => shift @$symbols );
}
else {
$class->_export_fail($target, $symbol, $symbols);
}
}
sub default {
my ($self, $item) = @_;
$self->var( DEFAULT => $item );
return $self;
}
sub item {
my ($self, $item) = @_;
$self->var( ITEM => $item );
return $self;
}
sub items {
my ($self, $items) = @_;
$self->var( ITEMS => $items );
return $self;
}
sub path {
my ($self, $path) = @_;
my $type = $self->var(ITEM)
|| croak "\$ITEM is not defined for $self. Please add an 'item' option";
my $var = uc($type) . PATH_SUFFIX;
$path = [ map { permute_fragments($_) } split(DELIMITER, $path) ]
unless ref $path eq ARRAY;
$self->debug("adding $var => [", join(', ', @$path), "]") if DEBUG;
# $self->base(FACTORY);
# we use import_symbol() rather than var() so that it gets declared
# properly, thus avoiding undefined symbol warnings
$self->import_symbol( $var => \$path );
return $self;
}
sub names {
my ($self, $map) = @_;
my $type = $self->var(ITEM)
|| croak "\$ITEM is not defined for $self. Please add an 'item' option";
my $var = uc($type) . NAMES_SUFFIX;
$self->debug("$self adding names $var => {", join(', ', %$map), "}") if DEBUG;
# we use import_symbol() rather than var() so that it gets declared
# properly, thus avoiding undefined symbol warnings
$self->import_symbol( $var => \$map );
return $self;
}
=head1 NAME
Badger::Factory::Class - class module for Badger::Factory sub-classes
=head1 SYNOPSIS
This module can be used to create subclasses of L<Badger::Factory>.
package My::Widgets;
use Badger::Factory::Class
version => 0.01,
item => 'widget',
path => 'My::Widget Your::Widget',
widgets => {
extra => 'Another::Widget::Module',
super => 'Golly::Gosh',
},
names => {
html => 'HTML',
color => 'Colour',
};
package main;
# class method
my $widget = My::Widgets->widget( foo => @args );
# object method
my $widgets = My::Widgets->new;
my $widget = $widgets->widget( foo => @args );
=head1 DESCRIPTION
This module is a subclass of L<Badger::Class> specialised for the purpose
of creating L<Badger::Factory> subclasses. It is used by the
L<Badger::Codecs> module among others.
=head1 METHODS
The following methods are provided in addition to those inherited
from the L<Badger::Class> base class.
=head2 item($name)
The singular name of the item that the factory manages. This is used
to set the C<$ITEM> package variable for L<Badger::Factory> to use.
=head2 items($name)
The plural name of the item that the factory manages. This is used
to set the C<$ITEMS> package variable for L<Badger::Factory> to use.
=head2 path($name)
A list of module names that form the search path when loading modules. This
will set the relevant package variable depending on the value of C<$ITEMS> (or
the regular plural form of C<$ITEM> if C<$ITEMS> is undefined). For example,
is C<$ITEMS> is set to C<widgets> then this method will set C<$WIDGETS_PATH>.
You can specify the path as a reference to a list of module bases, e.g.
use Badger::Factory::Class
item => 'widget',
path => ['My::Widget', 'Your::Widget'];
Or as a single string containing multiple values separated by whitespace.
use Badger::Factory::Class
item => 'widget',
path => 'My::Widget Your::Widget';
If you specify it as a single string then you can also include optional
and/or alternate parts in parentheses. For example the above can be
written more concisely as:
use Badger::Factory::Class
item => 'widget',
path => '(My|Your)::Widget';
If the parentheses don't contain a vertical bar then then enclosed fragment
is treated as being optional. So instead of writing something like:
use Badger::Factory::Class
item => 'widget',
path => 'Badger::Widget BadgerX::Widget';
You can write:
use Badger::Factory::Class
item => 'widget',
path => 'Badger(X)::Widget';
See the L<permute_fragments()|Badger::Utils/permute_fragments()> function in
L<Badger::Utils> for further details on how fragments are expanded.
=head2 names($names)
A reference to a hash array of name mappings. This can be used to handle any
unusual spellings or capitalisations. See L<Badger::Factory> for further
details.
=head2 default($name)
The default name to use when none is specified in a request for a module.
=head1 AUTHOR
Andy Wardley L<http://wardley.org/>
=head1 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.
=head1 SEE ALSO
L<Badger::Factory>, L<Badger::Codecs>
=cut
# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4:
1;
( run in 2.222 seconds using v1.01-cache-2.11-cpan-b16cb0d3907 )