| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
Class::Mutator - Run-time Dynamic Multiple Inheritance |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
package Frog; |
|
10
|
|
|
|
|
|
|
use Class::Mutator qw( -isasubclass ); |
|
11
|
|
|
|
|
|
|
sub new { ... } |
|
12
|
|
|
|
|
|
|
sub eat_flies { ... } |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
package Prince; |
|
15
|
|
|
|
|
|
|
sub be_charming { ... } |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
my $froggie = Frog->new; |
|
18
|
|
|
|
|
|
|
$froggie->mutate('Prince'); |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
# Now the froggie has been reblessed into a "Frog Prince" |
|
21
|
|
|
|
|
|
|
# class and can take advantage of methods in both classes. |
|
22
|
|
|
|
|
|
|
$froggie->eat_flies; |
|
23
|
|
|
|
|
|
|
$froggie->be_charming; |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
Class::Mutator adds the power of "dynamic polymorphism" to Perl |
|
28
|
|
|
|
|
|
|
objects. |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
Any object that inherits Class::Mutator principally gains two new |
|
31
|
|
|
|
|
|
|
methods, mutate and unmutate that allows them to add methods to |
|
32
|
|
|
|
|
|
|
themselves at runtime from other packages. The most recently mutated |
|
33
|
|
|
|
|
|
|
packages take precedence when methods with the same name are defined |
|
34
|
|
|
|
|
|
|
in more than one package. |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
This module is similar to Sex.pm, which Michael Schwern was working |
|
37
|
|
|
|
|
|
|
on around the same time, only a little bit more predictable. It |
|
38
|
|
|
|
|
|
|
came about while I was doing some training at the BBC and someone |
|
39
|
|
|
|
|
|
|
asked how you could do this easily; after discussion with my fellow |
|
40
|
|
|
|
|
|
|
London.pm'ers, in particular Piers Cawley, this module came about. |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
More recently Matthew Simon Cavalletto sent me a version with |
|
43
|
|
|
|
|
|
|
everything I had been meaning to do on the module a little after I |
|
44
|
|
|
|
|
|
|
uploaded version 0.03 which only had more substantial tests. So major |
|
45
|
|
|
|
|
|
|
kudos to Matthew. |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
=head1 USE |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
To enable a class of objects to mutate, make it a subclass of |
|
50
|
|
|
|
|
|
|
Class::Mutator. |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
package MyBaseClass; |
|
53
|
|
|
|
|
|
|
use Class::Mutator; |
|
54
|
|
|
|
|
|
|
push @ISA, 'Class::Mutator'; |
|
55
|
|
|
|
|
|
|
... |
|
56
|
|
|
|
|
|
|
MyBaseClass->new()->mutate( ... ); |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
As a shortcut, you may pass the C<-isasubclass> flag in your use |
|
59
|
|
|
|
|
|
|
statement, which will produce the same result. |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
package MyBaseClass; |
|
62
|
|
|
|
|
|
|
use Class::Mutator '-isasubclass'; |
|
63
|
|
|
|
|
|
|
... |
|
64
|
|
|
|
|
|
|
MyBaseClass->new()->mutate( ... ); |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
Finally, if you need to retroactively add mutation capabilities to |
|
67
|
|
|
|
|
|
|
an existing class, you can do so using the same syntax, with the |
|
68
|
|
|
|
|
|
|
target class passeds as a parameter. |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
package main; |
|
71
|
|
|
|
|
|
|
use MyBaseClass; |
|
72
|
|
|
|
|
|
|
use Class::Mutator '-isasubclass' => 'MyBaseClass'; |
|
73
|
|
|
|
|
|
|
MyBaseClass->new()->mutate( ... ); |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
You can also import the specific methods and functions described |
|
76
|
|
|
|
|
|
|
below and call them directly. |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
package main; |
|
79
|
|
|
|
|
|
|
use MyBaseClass; |
|
80
|
|
|
|
|
|
|
use Class::Mutator 'mutate'; |
|
81
|
|
|
|
|
|
|
mutate( MyBaseClass->new(), ... ); |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
=cut |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
package Class::Mutator; |
|
86
|
|
|
|
|
|
|
$VERSION='0.04'; |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
@EXPORT_OK = qw( mutate unmutate apply_mutation modify_mutation_list |
|
89
|
|
|
|
|
|
|
get_mutation_list build_mutation_package ); |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
sub import { |
|
92
|
2
|
|
|
2
|
|
4393
|
my $class = shift; |
|
93
|
|
|
|
|
|
|
|
|
94
|
2
|
100
|
|
|
|
12
|
if ( ! scalar @_ ) { |
|
|
|
50
|
|
|
|
|
|
|
95
|
1
|
|
|
|
|
16
|
return; |
|
96
|
|
|
|
|
|
|
} elsif ( $_[0] eq '-isasubclass' ) { |
|
97
|
1
|
|
33
|
|
|
10
|
my $target_class = $_[1] || ( caller )[0]; |
|
98
|
1
|
|
|
1
|
|
29447
|
no strict 'refs'; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
188
|
|
|
99
|
1
|
|
|
|
|
2184
|
push @{"$target_class\::ISA"}, $class |
|
|
0
|
|
|
|
|
0
|
|
|
100
|
1
|
50
|
|
|
|
3
|
unless ( grep { $_ eq $class } @{"$target_class\::ISA"} ); |
|
|
1
|
|
|
|
|
8
|
|
|
101
|
|
|
|
|
|
|
} else { |
|
102
|
0
|
0
|
|
|
|
0
|
require Exporter and goto &Exporter::import # lazy Exporter |
|
103
|
|
|
|
|
|
|
} |
|
104
|
|
|
|
|
|
|
} |
|
105
|
|
|
|
|
|
|
|
|
106
|
1
|
|
|
1
|
|
9
|
use strict; |
|
|
1
|
|
|
|
|
5
|
|
|
|
1
|
|
|
|
|
595
|
|
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=head1 METHODS |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
These methods provide the module's public object-oriented interface. |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=head2 mutate |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
$object->mutate( @packages ); |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
Adds a mutation. |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=cut |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
sub mutate { |
|
121
|
3
|
|
|
3
|
1
|
1567
|
my $self = shift; |
|
122
|
3
|
|
|
|
|
6
|
my @packages = @_; |
|
123
|
3
|
|
|
|
|
10
|
apply_mutation($self, '+', @packages); |
|
124
|
|
|
|
|
|
|
} |
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=head2 unmutate |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
$object->unmutate( @packages ); |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
Remove mutation abilities via a package |
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=cut |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub unmutate { |
|
135
|
3
|
|
|
3
|
1
|
828
|
my $self = shift; |
|
136
|
3
|
|
|
|
|
8
|
my @packages = @_; |
|
137
|
3
|
|
|
|
|
7
|
apply_mutation($self, '-', @packages); |
|
138
|
|
|
|
|
|
|
} |
|
139
|
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=head1 FUNCTIONS |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
These functions are used internally to support the methods described |
|
144
|
|
|
|
|
|
|
above. |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
=head2 apply_mutation |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
$reblessed_object = apply_mutation( $object, $op, @packages ); |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
Builds a new package list, based on the current package for a given |
|
151
|
|
|
|
|
|
|
object, the operator (either "+" or "-"), and the packages to be |
|
152
|
|
|
|
|
|
|
added or removed. |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=cut |
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
sub apply_mutation { |
|
157
|
6
|
|
|
6
|
1
|
10
|
my $self = shift; |
|
158
|
6
|
|
|
|
|
19
|
bless $self, build_mutation_package( modify_mutation_list( $self, @_ ) ) |
|
159
|
|
|
|
|
|
|
} |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=head2 get_mutation_list |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
@packages = get_mutation_list($object_or_class); |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
Returns the list of classes a mutated object is based on. If the |
|
166
|
|
|
|
|
|
|
object is not a mutant, its normal class name will be returned. |
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=cut |
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
sub get_mutation_list { |
|
171
|
6
|
|
|
6
|
1
|
8
|
my $self = shift; |
|
172
|
|
|
|
|
|
|
|
|
173
|
6
|
|
33
|
|
|
16
|
my $curr = ref($self) || $self; |
|
174
|
|
|
|
|
|
|
|
|
175
|
6
|
100
|
|
|
|
29
|
if ($curr =~ s/^Class::Mutator:://) { |
|
176
|
5
|
|
|
|
|
15
|
return map { s/__/::/g; $_ } split /::/,$curr; |
|
|
11
|
|
|
|
|
17
|
|
|
|
11
|
|
|
|
|
30
|
|
|
177
|
|
|
|
|
|
|
} else { |
|
178
|
1
|
|
|
|
|
3
|
return $curr |
|
179
|
|
|
|
|
|
|
} |
|
180
|
|
|
|
|
|
|
} |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
=head2 build_mutation_package |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
$new_class_name = build_mutation_package(@packages); |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
Builds the new mutation package. Returns the name of the new class. |
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
=cut |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
sub build_mutation_package { |
|
191
|
6
|
|
|
6
|
1
|
12
|
my @class_list = @_; |
|
192
|
6
|
|
|
|
|
11
|
my @ingredients = map { s/::/__/g; $_ } @class_list; |
|
|
12
|
|
|
|
|
16
|
|
|
|
12
|
|
|
|
|
41
|
|
|
193
|
6
|
|
|
|
|
16
|
my $package_name = 'Class::Mutator::'.join('::',@ingredients); |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# If our target class has an inheritance tree, we've already |
|
196
|
|
|
|
|
|
|
# set it up on a previous invocation, so there's nothing to do. |
|
197
|
1
|
|
|
1
|
|
9
|
no strict 'refs'; |
|
|
1
|
|
|
|
|
17
|
|
|
|
1
|
|
|
|
|
445
|
|
|
198
|
6
|
100
|
|
|
|
8
|
unless ( scalar @{$package_name . '::ISA'} ) { |
|
|
6
|
|
|
|
|
72
|
|
|
199
|
5
|
|
|
|
|
10
|
@{$package_name . '::ISA'} = reverse @class_list; |
|
|
5
|
|
|
|
|
97
|
|
|
200
|
|
|
|
|
|
|
} |
|
201
|
6
|
|
|
|
|
42
|
return $package_name; |
|
202
|
|
|
|
|
|
|
} |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=head2 modify_mutation_list |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
@packages = modify_mutation_list($object_or_class, $op, @packages); |
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
Builds a new package list based on the current packages list and the |
|
209
|
|
|
|
|
|
|
operation and package (the operation is upon) handed to this method. |
|
210
|
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=cut |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
sub modify_mutation_list { |
|
214
|
6
|
|
|
6
|
1
|
7
|
my $self = shift; |
|
215
|
6
|
|
|
|
|
13
|
my ($op, @packages) = @_; |
|
216
|
|
|
|
|
|
|
|
|
217
|
6
|
|
|
|
|
13
|
my @active_classes = get_mutation_list( $self ); |
|
218
|
|
|
|
|
|
|
|
|
219
|
6
|
|
|
|
|
13
|
foreach my $package ( @packages ) { |
|
220
|
8
|
|
|
|
|
13
|
@active_classes = grep { $_ ne $package } @active_classes; |
|
|
15
|
|
|
|
|
48
|
|
|
221
|
|
|
|
|
|
|
} |
|
222
|
|
|
|
|
|
|
|
|
223
|
6
|
100
|
|
|
|
17
|
if ($op eq '+') { |
|
|
|
50
|
|
|
|
|
|
|
224
|
3
|
|
|
|
|
6
|
push(@active_classes, @packages); |
|
225
|
|
|
|
|
|
|
} elsif ($op eq '-') { |
|
226
|
|
|
|
|
|
|
# We've already got this functionality out of the grep above |
|
227
|
|
|
|
|
|
|
} else { |
|
228
|
|
|
|
|
|
|
# Invalid operation |
|
229
|
|
|
|
|
|
|
} |
|
230
|
|
|
|
|
|
|
|
|
231
|
6
|
|
|
|
|
22
|
return @active_classes; |
|
232
|
|
|
|
|
|
|
} |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=head1 AUTHORS |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
Greg McCarroll |
|
237
|
|
|
|
|
|
|
Mail : greg@mccarroll.demon.co.uk |
|
238
|
|
|
|
|
|
|
Jabber : greg@jabber.mccarroll.org.uk |
|
239
|
|
|
|
|
|
|
Homepage : http://www.mccarroll.org.uk/~gem/ |
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
Matthew Simon Cavalletto |
|
242
|
|
|
|
|
|
|
Mail : simonm@cavalletto.org, |
|
243
|
|
|
|
|
|
|
Homepage : http://www.evoscript.org/ |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
=cut |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
1; |