Skip to content

Commit

Permalink
Import of MSCHWERN/mixin-0.01 from CPAN.
Browse files Browse the repository at this point in the history
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
schwern authored and Gitpan committed Oct 21, 2014
0 parents commit b30147e
Show file tree
Hide file tree
Showing 9 changed files with 3,029 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Changes
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
9 changes: 9 additions & 0 deletions MANIFEST
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
56 changes: 56 additions & 0 deletions Makefile.PL
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;
}
}
117 changes: 117 additions & 0 deletions lib/mixin.pm
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;
114 changes: 114 additions & 0 deletions lib/mixin/with.pm
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;

Loading

0 comments on commit b30147e

Please sign in to comment.