File Coverage

blib/lib/mixin.pm
Criterion Covered Total %
statement 51 51 100.0
branch 16 18 88.8
condition 3 3 100.0
subroutine 9 9 100.0
pod n/a
total 79 81 97.5


line stmt bran cond sub pod time code
1             package mixin;
2              
3 8     8   6220 use strict;
  8         15  
  8         199  
4 8     8   75 use warnings;
  8         14  
  8         225  
5 8     8   37 no strict 'refs';
  8         14  
  8         244  
6 8     8   36 use vars qw($VERSION);
  8         13  
  8         4737  
7             $VERSION = '0.08';
8              
9              
10             =head1 NAME
11              
12             mixin - Mix-in inheritance, an alternative to multiple inheritance
13              
14             =head1 SYNOPSIS
15              
16             package Dog;
17             sub speak { print "Bark!\n" }
18             sub new { my $class = shift; bless {}, $class }
19              
20             package Dog::Small;
21             use base 'Dog';
22             sub speak { print "Yip!\n"; }
23              
24             package Dog::Retriever;
25             use mixin::with 'Dog';
26             sub fetch { print "Get your own stinking $_[1]\n" }
27              
28             package Dog::Small::Retriever;
29             use base 'Dog::Small';
30             use mixin 'Dog::Retriever';
31              
32             my $small_retriever = Dog::Small::Retriever->new;
33             $small_retriever->speak; # Yip!
34             $small_retriever->fetch('ball'); # Get your own stinking ball
35              
36             =head1 DESCRIPTION
37              
38             B You probably want to look into the similar but superior
39             concept of traits/roles instead. See L for suggested
40             modules.
41              
42             Mixin inheritance is an alternative to the usual multiple-inheritance
43             and solves the problem of knowing which parent will be called.
44             It also solves a number of tricky problems like diamond inheritence.
45              
46             The idea is to solve the same sets of problems which MI solves without
47             the problems of MI. For all practical purposes you can think of a
48             mixin as multiple inheritance without the actual inheritance.
49              
50             Mixins are a band-aid for the problems of MI. A better solution is to
51             use traits (called "Roles" in Perl 6), which are like mixins on
52             steroids. Class::Trait implements this.
53              
54              
55             =head2 Using a mixin class
56              
57             There are two steps to using a mixin-class.
58              
59             First, make sure you are inherited from the class with which the
60             mixin-class is to be mixed.
61              
62             package Dog::Small::Retriever;
63             use base 'Dog::Small';
64              
65             Since Dog::Small isa Dog, that does it. Then simply mixin the new
66             functionality
67              
68             use mixin 'Dog::Retriever';
69              
70             and now you can use fetch().
71              
72              
73             =head2 Writing a mixin class
74              
75             See L.
76              
77              
78             =head2 Mixins, Inheritance and SUPER
79              
80             A class which uses a mixin I inherit from it. However,
81             through some clever trickery, C continues to work. Here's an
82             example.
83              
84             {
85             package Parent;
86             sub foo { "Parent" }
87             }
88              
89             {
90             package Middle;
91             use mixin::with "Parent";
92              
93             sub foo {
94             my $self = shift;
95             return $self->SUPER::foo(), "Middle";
96             }
97             }
98              
99             {
100             package Child;
101             use base "Parent";
102             use mixin "Middle";
103              
104             sub foo {
105             my $self = shift;
106             return $self->SUPER::foo(), "Child";
107             }
108             }
109              
110             print join " ", Child->foo; # Parent Middle Child
111              
112             This will print C. You'll note that this is the
113             same result if Child inherited from Middle and Middle from Parent.
114             Its also the same result if Child multiply inherited from Middle and
115             Parent but I if it inherited from Parent then Middle. The
116             advantage of mixins vs multiple inheritance is such ambiguities do not
117             exist.
118              
119             Note that even though both the Child and Middle define foo() the
120             Middle mixin does not overwrite Child's foo(). A mixin does not
121             simply export its methods into the mixer and thus does not blow over
122             existing methods.
123              
124             =cut
125              
126             sub import {
127 12     12   2127 my($class, @mixins) = @_;
128 12         28 my $caller = caller;
129              
130 12         25 foreach my $mixin (@mixins) {
131             # XXX This is lousy, but it will do for now.
132 12 50       20 unless( defined ${$mixin.'::VERSION'} ) {
  12         94  
133 12         616 eval qq{ require $mixin; };
134 12 100 100     381 _croak($@) if $@ and $@ !~ /^Can't locate .*? at /;
135 11 100       15 unless( %{$mixin."::"} ) {
  11         72  
136 1         7 _croak(<
137             Mixin class package "$mixin" is empty.
138             (Perhaps you need to 'use' the module which defines that package first?)
139             ERROR
140             }
141             }
142 10         30 _mixup($mixin, $caller);
143             }
144             }
145              
146             sub _mixup {
147 10     10   24 my($mixin, $caller) = @_;
148              
149 10         42 require mixin::with;
150 10         43 my($with, $pkg) = mixin::with->__mixers($mixin);
151              
152 10 100       42 _croak("$mixin is not a mixin") unless $with;
153 9 100       88 _croak("$caller must be a subclass of $with to mixin $mixin")
154             unless $caller->isa($with);
155              
156             # This has to happen here and not in mixin::with because "use
157             # mixin::with" typically runs *before* the rest of the mixin's
158             # subroutines are declared.
159 8         21 _thieve_public_methods( $mixin, $pkg );
160 8         20 _thieve_isa( $mixin, $pkg, $with );
161              
162 8         10 unshift @{$caller.'::ISA'}, $pkg;
  8         9594  
163             }
164              
165              
166             my %Thieved = ();
167             sub _thieve_public_methods {
168 8     8   15 my($mixin, $pkg) = @_;
169              
170 8 100       33 return if $Thieved{$mixin}++;
171              
172 7         18 local *glob;
173 7         10 while( my($sym, $glob) = each %{$mixin.'::'}) {
  35         162  
174 28 100       71 next if $sym =~ /^_/;
175 27 50       56 next unless defined $glob;
176 27         31 *glob = *{$mixin.'::'.$sym};
  27         81  
177 27 100       94 *{$pkg.'::'.$sym} = *glob{CODE} if *glob{CODE};
  7         65  
178             }
179              
180 7         19 return 1;
181             }
182              
183             sub _thieve_isa {
184 8     8   16 my($mixin, $pkg, $with) = @_;
185              
186 8         14 @{$pkg.'::ISA'} = grep $_ ne $with, @{$mixin.'::ISA'};
  8         87  
  8         36  
187              
188 8         17 return 1;
189             }
190              
191              
192             sub _croak {
193 4     4   18 require Carp;
194 4         672 goto &Carp::croak;
195             }
196              
197              
198             =head1 NOTES
199              
200             A mixin will not warn if the mixin and the user define the same method.
201              
202              
203             =head1 AUTHOR
204              
205             Michael G Schwern Eschwern@pobox.comE
206              
207              
208             =head1 LICENSE
209              
210             Copyright 2002-2015 by Michael G Schwern
211              
212             This library is free software; you can redistribute it and/or modify it
213             under the same terms as Perl itself.
214              
215             L
216              
217              
218             =head1 SEE ALSO
219              
220             L - A stand alone implementation of traits/roles, like mixins but better.
221              
222             L - Moose's implementation of traits/roles.
223              
224             L and L make multiple inheritance work more sensibly.
225              
226             =cut
227              
228             1;