File Coverage

blib/lib/mixin.pm
Criterion Covered Total %
statement 48 48 100.0
branch 16 18 88.8
condition 3 3 100.0
subroutine 8 8 100.0
pod n/a
total 75 77 97.4


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