-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Import of MSCHWERN/mixin-0.01 from CPAN.
gitpan-cpan-distribution: mixin gitpan-cpan-version: 0.01 gitpan-cpan-path: MSCHWERN/mixin-0.01.tar.gz gitpan-cpan-author: MSCHWERN gitpan-cpan-maturity: released
- Loading branch information
0 parents
commit b30147e
Showing
9 changed files
with
3,029 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
0.01 Tue Apr 16 15:42:01 EDT 2002 | ||
* It works |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
Changes | ||
MANIFEST | ||
Makefile.PL | ||
lib/mixin.pm | ||
lib/mixin/with.pm | ||
t/lib/Test/Builder.pm | ||
t/lib/Test/More.pm | ||
t/lib/Test/Simple.pm | ||
t/mixin.t |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# A template for Makefile.PL. | ||
# - Set the $PACKAGE variable to the name of your module. | ||
# - Set $LAST_API_CHANGE to reflect the last version you changed the API | ||
# of your module. | ||
# - Fill in your dependencies in PREREQ_PM | ||
# Alternatively, you can say the hell with this and use h2xs. | ||
|
||
use ExtUtils::MakeMaker; | ||
|
||
$PACKAGE = 'mixin'; | ||
($PACKAGE_FILE = $PACKAGE) =~ s|::|/|g; | ||
$LAST_API_CHANGE = 0; | ||
|
||
eval "require $PACKAGE"; | ||
|
||
unless ($@) { # Make sure we did find the module. | ||
print <<"CHANGE_WARN" if ${$PACKAGE.'::VERSION'} < $LAST_API_CHANGE; | ||
NOTE: There have been API changes between this version and any older | ||
than version $LAST_API_CHANGE! Please read the Changes file if you | ||
are upgrading from a version older than $LAST_API_CHANGE. | ||
CHANGE_WARN | ||
} | ||
|
||
WriteMakefile( | ||
NAME => $PACKAGE, | ||
VERSION_FROM => "lib/$PACKAGE_FILE.pm", # finds $VERSION | ||
PREREQ_PM => { }, | ||
); | ||
|
||
|
||
{ | ||
package MY; | ||
|
||
sub test_via_harness { | ||
my($self, $orig_perl, $tests) = @_; | ||
|
||
my @perls = ($orig_perl); | ||
push @perls, qw(bleadperl | ||
perl5.6.1 | ||
perl5.6.0 | ||
perl5.005_03 | ||
perl5.004_05 | ||
perl5.004_04 | ||
perl5.004) | ||
if $ENV{PERL_TEST_ALL}; | ||
|
||
my $out; | ||
foreach my $perl (@perls) { | ||
$out .= $self->SUPER::test_via_harness($perl, $tests); | ||
} | ||
|
||
return $out; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package mixin; | ||
|
||
use strict; | ||
no strict 'refs'; | ||
use vars qw($VERSION); | ||
$VERSION = '0.01'; | ||
|
||
|
||
=head1 NAME | ||
mixin - Mix-in inheritance, an alternative to multiple inheritance | ||
=head1 SYNOPSIS | ||
package Dog; | ||
sub speak { print "Bark!\n" } | ||
sub new { bless {} } | ||
package Dog::Small; | ||
use base 'Dog'; | ||
sub speak { print "Yip!\n"; } | ||
package Dog::Retriever; | ||
use mixin::with 'Dog'; | ||
sub fetch { print "Get your own stinking $_[1]\n" } | ||
package Dog::Small::Retriever; | ||
use base 'Dog::Small'; | ||
use mixin qw(Dog::Small Dog::Retriever); | ||
my $small_retriever = Dog::Small::Retriever->new; | ||
$small_retriever->speak; # Yip! | ||
$small_retriever->fetch('ball'); # Get your own stinking ball | ||
=head1 DESCRIPTION | ||
Mixin inheritance is an alternative to the usual multiple-inheritance | ||
and solves the problem of knowing which parent will be called. | ||
It also solves a number of tricky problems like diamond inheritence. | ||
The idea is to solve the same sets of problems which MI solves without | ||
the problems of MI. | ||
=head2 Using a mixin class. | ||
There are two steps to using a mixin-class. | ||
First, make sure you are inherited from the class with which the | ||
mixin-class is to be mixed. | ||
package Dog::Small::Retriever; | ||
use base 'Dog::Small'; | ||
Since Dog::Small isa Dog, that does it. Then simply mixin the new | ||
functionality | ||
use mixin 'Dog::Retriever'; | ||
and now you can use fetch(). | ||
=cut | ||
|
||
sub import { | ||
my($class, @mixins) = @_; | ||
my $caller = caller; | ||
|
||
foreach my $mixin (@mixins) { | ||
_mixup($mixin, $caller); | ||
} | ||
} | ||
|
||
sub _mixup { | ||
my($mixin, $caller) = @_; | ||
|
||
require mixin::with; | ||
my($with, $pkg) = mixin::with->__mixers($mixin); | ||
_croak("$caller must be a subclass of $with") | ||
unless $caller->isa($with); | ||
|
||
# This has to happen here and not in mixin::with because "use | ||
# mixin::with" typically runs *before* the rest of the mixin's | ||
# subroutines are declared. | ||
_thieve_public_methods( $mixin, $pkg ); | ||
|
||
push @{$caller.'::ISA'}, $pkg; | ||
} | ||
|
||
|
||
my %Thieved = (); | ||
sub _thieve_public_methods { | ||
my($mixin, $pkg) = @_; | ||
|
||
return if $Thieved{$mixin}++; | ||
|
||
local *glob; | ||
while( my($sym, $glob) = each %{$mixin.'::'}) { | ||
next if $sym =~ /^_/; | ||
next unless defined $glob; | ||
*glob = *$glob; | ||
*{$pkg.'::'.$sym} = *glob{CODE} if *glob{CODE}; | ||
} | ||
} | ||
|
||
|
||
sub _croak { | ||
require Carp; | ||
Carp::croak(@_); | ||
} | ||
|
||
=head1 AUTHOR | ||
Michael G Schwern E<lt>schwern@pobox.comE<gt> | ||
=cut | ||
|
||
1; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package mixin::with; | ||
|
||
use strict; | ||
no strict 'refs'; | ||
use vars qw($VERSION); | ||
$VERSION = 0.01; | ||
|
||
=head1 NAME | ||
mixin::with - declaring a mix-in class | ||
=head1 SYNOPSIS | ||
package Dog::Retriever; | ||
use mixin::with 'Dog'; | ||
=head1 DESCRIPTION | ||
mixin::with is used to declare mix-in classes. | ||
=head2 Creating a mixin class. | ||
There are three critical differences between a normal subclass and one | ||
intended to be mixin. | ||
=over 4 | ||
=item 1. It can have no superclasses. | ||
=item 2. It can have no private methods. Instead, use private functions. | ||
C<_private($self, @args)> instead of C<$self->_private(@args);> | ||
=item 3. The mixin class is useless on it's own. | ||
You can't just "use Dog::Retriever" alone and expect it to do | ||
anything useful. It must be mixed. | ||
=back | ||
Mixin classes useful for those that I<add new functionality> to an | ||
existing class. If you find yourself doing: | ||
package Foo::ExtraStuff; | ||
use base 'Foo'; | ||
package Bar; | ||
use base qw(Foo Foo::ExtraStuff); | ||
it's a good indication that Foo::ExtraStuff might do better as a mixin. | ||
=head2 How? | ||
Basic usage is simple: | ||
package Foo::Extra; | ||
use mixin::with 'Foo'; | ||
sub new_thing { | ||
my($self) = shift; | ||
...normal method... | ||
} | ||
C<use mixin::with 'Foo'> is I<similar> to subclassing from 'Foo'. | ||
All public methods of Foo::Extra will be mixed in. mixin::with | ||
considers all methods that don't start with an '_' as public. | ||
=cut | ||
|
||
my %Mixers = (); | ||
my $Tmp_Counter = 0; | ||
sub import { | ||
my($class, $mixed_with) = @_; | ||
my $mixin = caller; | ||
|
||
_carp("Mixin classes should not have superclasses") | ||
if @{$mixin.'::ISA'}; | ||
|
||
my $tmp_pkg = __PACKAGE__.'::tmp'.$Tmp_Counter++; | ||
$Mixers{$mixin} = { mixed_with => $mixed_with, | ||
tmp_pkg => $tmp_pkg, | ||
}; | ||
|
||
return 1; | ||
} | ||
|
||
|
||
sub __mixers { | ||
my($class, $mixin) = @_; | ||
|
||
return @{$Mixers{$mixin}}{'mixed_with', 'tmp_pkg'}; | ||
} | ||
|
||
|
||
sub _carp { | ||
require Carp; | ||
Carp::carp(@_); | ||
} | ||
|
||
|
||
=head1 AUTHOR | ||
Michael G Schwern <schwern@pobox.com> | ||
=head1 SEE ALSO | ||
L<mixin>, L<ruby> from which I stole this idea. | ||
=cut | ||
|
||
1; | ||
|
Oops, something went wrong.