-# This file is part of Class::Modular and is released under the terms
-# of the GPL version 2, or any later version at your option. See the
-# file README and COPYING for more information.
-# Copyright 2003, 2004 by Don Armstrong <don@donarmstrong.com>.
+# This module is part of DA, Don Armstrong's Modules, and is released
+# under the terms of the GPL version 2, or any later version. See the
+# file README and COPYING for more information. Copyright 2003 by Don
+# Armstrong <don@donarmstrong.com>.
# $Id$
package Class::Modular;
=head1 SYNOPSIS
- package Foo::Bar;
+ package Foo;
use base qw(Class::Modular);
+ [...]
+
+ use Foo;
+
+ $foo = new Foo;
+ $foo->load('Bar');
+ $foo->method_that_bar_provides;
+
+
=head1 DESCRIPTION
Class::Modular is a superclass for generating modular classes, where
without modifying the base classes. This allows for code to be more
modular, and reduces code duplication.
-It fills the middle ground between traditional class based OOP and
-classless OOP. L<Class::Mutator> and L<Sex> are similar to
-Class::Modular but less manic.
-
=head1 FUNCTIONS
=cut
$DEBUG = 0 unless defined $DEBUG;
}
+# This is the class_modular namespace, so we don't muck up the
+# subclass(es) by accident.
+
+my $cm = q(__class_modular);
+
our $AUTOLOAD;
+=head2 new
+
+=head3 Usage
+
+ $obj = Foo::Bar->new();
+
+=head3 Function
+
+Creates a new Foo::Bar object
+
+Aditional arguments can be passed to this creator, and they are stored
+in $self->{creation_args} (and $self->{$cm}{creation_args} by
+_init. You can also override the new method in your subclass. It's
+just provided here for completeness.
+
+=cut
+
+sub new {
+ my ($class,@args) = @_;
+
+ # We shouldn't be called $me->new, but just in case
+ $class = ref($class) || $class;
+
+ my $self = {};
+ bless $self, $class;
+
+ $self->_init(@args);
+
+ return $self;
+}
+
+
+=head2 _init
+
+=head3 Usage
+
+ $self->_init(@args);
+
+=head3 Function
+
+Stores the arguments used at new so modules that are loaded later can
+read them from B<creation_args>
+
+You can also override this method, but if you do so, you should call
+Class::Modular::_init(@_) if you don't set creation_args.
+
+=cut
+
+sub _init {
+ my ($self,@creation_args) = @_;
+
+ my $creation_args = [@_];
+ $self->{creation_args} = $creation_args if not exists $self->{creation_args};
+
+ # Make another reference to this, so we can get it if a subclass
+ # overwrites it, or if it was already set for some reason
+ $self->{$cm}->{creation_args} = $creation_args;
+}
+
+
=head2 load
=head3 Usage
# check to see if the subclass has already been loaded.
- if (not defined $self->{_subclasses}->{$subclass}){
+ if (not defined $self->{$cm}{_subclasses}->{$subclass}){
eval {
no strict 'refs';
+ # Yeah, I don't care if calling an inherited AUTOLOAD
+ # for a non method is deprecated. Bite me.
+ no warnings 'deprecated';
eval "require $subclass" or die $@;
- my @methods = ();
- if (UNIVERSAL::can($subclass,'METHODS')) {
- push @methods,&{"${subclass}::METHODS"};
- }
- elsif (UNIVERSAL::can($subclass,'METHODS')) {
- push @methods,&{"${subclass}::_methods"};
- }
- $self->_addmethods($subclass,@methods);
- my $initref = UNIVERSAL::can($subclass,'_init');
- &$initref($self,$options) if defined $initref;
+ $self->_addmethods($subclass,&{"${subclass}::_methods"}($self,$options));
+ &{"${subclass}::_init"}($self);
};
- die $@ if $@;
- $self->{_subclasses}->{$subclass} = {};
+ die $@ if $@ and $@ !~ /^Undefined function ${subclass}::_init at [^\n]*$/;
+ $self->{$cm}{_subclasses}->{$subclass} = {};
}
else {
carp "Not reloading subclass $subclass" if $DEBUG;
}
}
-=head2 override
+=head2 _addmethods
=head3 Usage
+ $self->_addmethods()
+
+=head3 Function
+
+Given an array of methods, adds the methods into the _methodhash
+calling table.
+
+Methods that have previously been overridden by override are _NOT_
+overridden again. This may need to be adjusted in load.
+
+=cut
+
+sub _addmethods($@) {
+ my ($self,$subclass,@methods) = @_;
+
+ # stick the method into the table
+ # DLA: Make with the munchies!
+
+ foreach my $method (@methods) {
+ if (not $method =~ /^$subclass/) {
+ $method = $subclass.'::'.$method;
+ }
+ my ($method_name) = $method =~ /\:*([^\:]+)\s*$/;
+ if (exists $self->{$cm}{_methodhash}->{$method_name}) {
+ if ($self->{$cm}{_methodhash}->{$method_name}->{overridden}) {
+ carp "Not overriding already overriden method $method_name\n" if $DEBUG;
+ next;
+ }
+ carp "Overriding $method_name $self->{$cm}{_methodhash}->{$method_name}->{reference} with $method\n";
+ }
+ $self->{$cm}{_methodhash}->{$method_name}->{reference} = $method;
+ $self->{$cm}{_methodhash}->{$method_name}->{subclass} = $subclass;
+ }
+
+}
+
+=head2 override
+
+=head3 Function
+
$obj->override('methodname', $code_ref)
+=head3 Returns
+
+TRUE on success, FALSE on failure.
+
=head3 Function
Allows you to override utility functions that are called internally to
sub override {
my ($self, $method_name, $function_reference) = @_;
- $self->{_methodhash}->{$method_name}->{reference} = $function_reference;
- $self->{_methodhash}->{$method_name}->{overridden} = 1;
+ $self->{$cm}{_methodhash}->{$method_name}->{reference} = $function_reference;
+ $self->{$cm}{_methodhash}->{$method_name}->{overridden} = 1;
}
=head2 clone
Calls _clone on all loaded subclasses.
-Warns if debugging is on for classes which don't have a _clone method.
-Dies on other errors.
+Warns if debugging is on for classes which don't have a _clone
+method. Dies on other errors.
=cut
bless $clone, ref($self);
# copy data structures at this level
- $clone->{_methodhash} = dclone($self->{_methodhash});
- $clone->{_subclasses} = dclone($self->{_subclasses});
-
- foreach my $subclass (keys %{$self->{_subclasses}}) {
- # use eval to try and call the subclasses _clone method.
- # Ignore no such function errors, but trap other types of
- # errors.
+ $clone->{$cm}{_methodhash} = dclone($self->{$cm}{_methodhash});
+ $clone->{$cm}{_subclasses} = dclone($self->{$cm}{_subclasses});
- # XXX Switch to can instead.
+ foreach my $subclass (keys %{$self->{$cm}{_subclasses}}) {
+ # Find out if the subclass has a clone method.
+ # If it does, call it, die on errors.
+ my $function = UNIVERSAL::can($subclass, '_clone');
eval {
no strict 'refs';
+ # No, I could care less that AUTOLOAD is
+ # deprecated. Eat me.
+ no warnings 'deprecated';
&{"${subclass}::_clone"}($self,$clone);
};
if ($@) {
# Die unless we've hit an undefined subroutine.
- die $@ unless $@ =~ /Undefined\s*function\s*.*\:\:\_clone/;
- warn "$subclass doesn't have a _clone method\n$@" if $DEBUG;
+ if ($@ !~ /^Undefined function ${subclass}::_clone at [^\n]*$/){
+ die "Failed while trying to clone: $@";
+ }
+ else {
+ carp "No _clone method defined for $subclass" if $DEBUG;
+ }
}
-
-
}
}
sub can{
my ($self,$method,$vars) = @_;
- if (ref $self and exists $self->{_methodhash}->{$method}) {
+ croak "Usage: can(object-ref, method, [vars]);\n" if not defined $method;
+
+ if (ref $self and exists $self->{$cm}{_methodhash}->{$method}) {
# If the method is defined, return a reference to the
# method.
- return $self->{_methodhash}->{$method}->{reference};
+ return $self->{$cm}{_methodhash}->{$method}->{reference};
}
else {
# Otherwise, let UNIVERSAL::can deal with the method
$method_name =~ s/.*\://;
- if (exists $self->{_methodhash}->{$method_name}) {
- return $self->{_methodhash}->{$method_name}->{subclass};
+ if (exists $self->{$cm}{_methodhash}->{$method_name}) {
+ return $self->{$cm}{_methodhash}->{$method_name}->{subclass};
}
return undef;
}
-=head1 INTERNAL FUNCTIONS
-
-The functions below are functions internal to Class::Modular. The
-first two, new and _init should probably be overriden in any class
-that inherits from Class::Modular, but they are provided just in case
-you don't want to write a new and/or _init.
-
-=cut
-
-=head2 new
-
-=head3 Usage
-
- $obj = Foo::Bar->new();
-
-=head3 Function
-
-Creates a new C<Foo::Bar> object.
-
-Aditional arguments can be passed to this creator, and they are stored
-in C<$self->{_creation_args}>. You can also override the new method in
-your subclass. It's just provided here for completeness.
-
-=cut
-
-sub new {
- my ($class,@args) = @_;
-
- # We shouldn't be called $me->new, but just in case
- $class = ref($class) || $class;
-
- my $self = {};
- bless $self, $class;
-
- $self->_init(@args);
-
- return $self;
-}
-
-
-=head2 _init
-
-=head3 Usage
-
- $self->_init(@args);
-
-=head3 Function
-
-Stores the arguments used at new so modules that are loaded later can
-read them
-
-This function is called by default from new. Classes may only wish to
-override _init.
-
-=cut
-
-sub _init {
- my ($self,@creation_args) = @_;
-
- $self->{creation_args} = [@creation_args];
-}
-
-
-=head2 _addmethods
-
-=head3 Usage
-
- $self->_addmethods($subclass,&{"${subclass}::_methods"}($self,$options));
-
-=head3 Function
-
-Given an array of methods, adds the methods into the _methodhash
-calling table.
-
-Methods that have previously been overridden by override are _NOT_
-overridden again. This may need to be adjusted in load.
-
-=cut
-
-sub _addmethods($@) {
- my ($self,$subclass,@methods) = @_;
-
- # stick the method into the table
- # DLA: Make with the munchies!
-
- foreach my $method (@methods) {
- if (not $method =~ /^$subclass/) {
- $method = $subclass.'::'.$method;
- }
- my ($method_name) = $method =~ /\:*([^\:]+)\s*$/;
- if (exists $self->{_methodhash}->{$method_name}) {
- if ($self->{_methodhash}->{$method_name}->{overridden}) {
- carp "Not overriding already overriden method $method_name\n" if $DEBUG;
- next;
- }
- carp "Overriding $method_name $self->{_methodhash}->{$method_name}->{reference} with $method\n";
- }
- $self->{_methodhash}->{$method_name}->{reference} = $method;
- $self->{_methodhash}->{$method_name}->{subclass} = $subclass;
- }
-
-}
=head2 DESTROY
=cut
-
-
sub DESTROY{
my $self = shift;
- foreach my $subclass (keys %{$self->{_subclasses}}) {
+ foreach my $subclass (keys %{$self->{$cm}{_subclasses}}) {
# use eval to try and call the subclasses _destroy method.
# Ignore no such function errors, but trap other types of
# errors.
- my $destroy_func = UNIVERSAL::can($subclass,'_destroy');
- &$destroy_func($self) if defined $destroy_func;
+ eval {
+ no strict 'refs';
+ &{"${subclass}::_destroy"}($self);
+ };
+ if ($@) {
+ if ($@ !~ /^Undefined function ${subclass}::_destroy at [^\n]*$/){
+ die "Failed while trying to destroy: $@";
+ }
+ else {
+ carp "No _destroy method defined for $subclass" if $DEBUG;
+ }
+ }
}
}
=head2 AUTOLOAD
-=head3 Function
-
The AUTOLOAD function is responsible for calling child methods which
have been installed into the current Class::Modular handle.
return;
}
- if (exists $self->{_methodhash}->{$method} and
- defined $self->{_methodhash}->{$method}->{reference}) {
+ if (exists $self->{$cm}{_methodhash}->{$method} and
+ defined $self->{$cm}{_methodhash}->{$method}->{reference}) {
eval {
no strict 'refs';
- goto &{$self->{_methodhash}{$method}{reference}};
+ goto &{$self->{$cm}{_methodhash}{$method}{reference}};
}
}
else {