File Coverage

blib/lib/Class/Mixin.pm
Criterion Covered Total %
statement 67 68 98.5
branch 15 20 75.0
condition 7 10 70.0
subroutine 11 11 100.0
pod 1 1 100.0
total 101 110 91.8


line stmt bran cond sub pod time code
1             =pod
2              
3             =head1 NAME
4              
5             Class::Mixin - API for aliasing methods to/from other classes
6              
7             =head1 OVERVIEW
8              
9             Class::Mixin provides a way to mix methods from one class into another,
10             such that the target class can use both its methods as well as those
11             of the source class.
12              
13             The primary advantage is that the behavior of a class can be modified
14             to effectively be another class without changing any of the calling
15             code -- just requires using the new class that mixes into the original.
16              
17             =head1 SYNOPSIS
18              
19             # class1.pm
20             package class1;
21             sub sub1 { return 11 };
22             ...
23              
24             # class2.pm
25             package class2;
26             use Class::Mixin to=> 'class1';
27             sub sub2 { return 22 };
28              
29             # Original calling code
30             use class1;
31             print class1->sub1; # 11
32             print class1->can('sub2'); # false
33              
34             # Updated calling code
35             use class1;
36             use class2; # performs the mixing-in
37             print class1->sub1; # 11
38             print class1->can('sub2'); # true
39             print class1->sub2; # 22 <-- note class1 now has the class2 method
40              
41             =head1 METHODS
42              
43             =cut
44              
45             #######################################################
46             package Class::Mixin;
47 7     7   131210 use strict;
  7         18  
  7         257  
48              
49 7     7   6379 use Symbol ();
  7         7139  
  7         172  
50 7     7   48 use Carp;
  7         16  
  7         563  
51 7     7   40 use warnings::register;
  7         13  
  7         3005  
52              
53             our $VERSION = '1.00';
54              
55             my %r = map { $_=> 1 } qw(
56             BEGIN
57             INIT
58             CHECK
59             END
60             DESTROY
61             AUTOLOAD
62             ISA
63            
64             import
65             can
66             isa
67             ISA
68             STDIN
69             STDOUT
70             STDERR
71             ARGV
72             ARGVOUT
73             ENV
74             INC
75             SIG
76             );
77              
78             sub __new {
79 14 100   14   11886 return $Class::Mixin::OBJ if defined $Class::Mixin::OBJ;
80 7         21 $Class::Mixin::OBJ = bless {}, shift;
81 7         55 return $Class::Mixin::OBJ;
82             }
83              
84             =pod
85              
86             =head2 import
87              
88             Method used when loading class to import symbols or perform
89             some function. In this case we take the calling classes methods
90             and map them into the class passed in as a parameter.
91              
92             =over 2
93              
94             =item Input
95              
96             =over 2
97              
98             =item None
99              
100             =back
101              
102             =item Output
103              
104             None
105              
106             =back
107              
108             =cut
109              
110             sub import {
111 8     8   914 my $cl = shift;
112 8 100       56 return unless @_;
113 6         23 my $obj = Class::Mixin->__new;
114 6         19 my $p = { @_ };
115 6 100 100     207 Carp::croak q{Must mixin 'to' or 'from' something} unless exists $p->{to} || exists $p->{from};
116              
117 5         12 my $class = caller;
118 5 100       19 if( exists $p->{to} ){
119 3   50     60 $obj->{mixins}->{ $class }->{ $p->{to} } ||= [];
120             }
121 5 100       2125 if( exists $p->{from} ){
122 2   50     1747 $obj->{mixins}->{ $p->{from} }->{ $class } ||= [];
123             }
124             }
125              
126 5     5   3511 CHECK { resync() }
127              
128             =pod
129              
130             =head2 B DESTROY
131              
132             This modules uses a destructor for un-mixing methods. This is done in
133             the case that this module is unloaded for some reason. It will return
134             modules to their original states.
135              
136             =over 2
137              
138             =item Input
139              
140             =over 2
141              
142             =item *
143              
144             Class::Mixin object
145              
146             =back
147              
148             =item Output
149              
150             =over 2
151              
152             =item None
153              
154             =back
155              
156             =back
157              
158             =cut
159              
160             sub DESTROY {
161 2     2   6 my $obj = shift;
162 2         18 foreach my $mixin ( keys %{$obj->{mixins}} ) {
  2         23  
163 2         38 foreach my $target ( keys %{$obj->{mixins}->{$mixin}} ) {
  2         34  
164 2         6 foreach my $v ( @{ $obj->{mixins}->{$mixin}->{$target} } ){
  2         8  
165 7     7   39 no strict 'refs';
  7         13  
  7         1428  
166 4         12 my $m = $v->{'method'};
167 4         12 my $c = $v->{'class'} . '::';
168 4         9 my $s = $v->{'symbol'};
169 4         6 *{ $s } = undef;
  4         9  
170 4         7 delete ${ $c }{ $m };
  4         15  
171 4         20 $s = undef;
172             }
173             }
174             }
175             }
176              
177             =pod
178              
179             =head2 resync
180              
181             Function used to process registered 'mixins'. Typically automatically
182             called once immediately after program compilation. Sometimes though you
183             may want to call it manually if a modules is reloaded.
184              
185             =over 2
186              
187             =item Input
188              
189             =over 2
190              
191             =item None
192              
193             =back
194              
195             =item Output
196              
197             =over 2
198              
199             =item None
200              
201             =back
202              
203             =back
204              
205             =cut
206              
207             sub resync {
208 6     6 1 34 my $obj = Class::Mixin->__new;
209 6         17 my $class = caller;
210              
211 6         10 foreach my $mixin ( keys %{$obj->{mixins}} ) {
  6         972  
212 5         10 foreach my $target ( keys %{$obj->{mixins}->{$mixin}} ) {
  5         17  
213              
214 5         13 my $mixinSym = $mixin . '::';
215 5         11 my $targetSym = $target . '::';
216              
217 5 50 66     66 next if $class ne $mixin && !$class->isa( __PACKAGE__ );
218              
219 7     7   36 no strict 'refs';
  7         13  
  7         1755  
220              
221 5         18 foreach my $method ( keys %$mixinSym ) {
222 10 100       33 if ( exists $r{ $method } ) {
  8 50       31  
223 2 50       272 warnings::warn "Unable to Mixin method '$method', restricted"
224             if warnings::enabled();
225             } elsif ( exists ${ $targetSym }{ $method } ) {
226 0 0       0 warnings::warn qq{
227             Unable to Mixin method '$method'
228             FROM $mixin
229             TO $target
230             already defined in $target
231             } if warnings::enabled();
232             } else {
233 8         32 my $m = Symbol::qualify_to_ref( $method, $mixin );
234 8         188 my $t = Symbol::qualify_to_ref( $method, $target );
235 8         130 *{ $t } = *{ $m };
  8         27  
  8         15  
236              
237 8         14 push @{ $obj->{mixins}->{$mixin}->{$target} }, {
  8         4531  
238             class=> $target,
239             method=> $method,
240             symbol=> $t,
241             };
242             }
243             }
244              
245             }
246             }
247             }
248              
249             1;
250              
251             __END__