File Coverage

blib/lib/Bio/Phylo/Util/MOP.pm
Criterion Covered Total %
statement 143 227 63.0
branch 12 30 40.0
condition 3 12 25.0
subroutine 41 61 67.2
pod 0 27 0.0
total 199 357 55.7


line stmt bran cond sub pod time code
1             package Bio::Phylo::Util::MOP;
2 57     57   311 use strict;
  57         108  
  57         1423  
3 57     57   260 use warnings;
  57         98  
  57         1318  
4 57     57   17740 use attributes;
  57         51736  
  57         283  
5 57     57   20669 use Attribute::Handlers;
  57         138429  
  57         273  
6 57     57   5333 use Data::Dumper;
  57         45376  
  57         2858  
7 57     57   3108 use Bio::Phylo::Util::Exceptions 'throw';
  57         114  
  57         2565  
8 57     57   4339 use Bio::Phylo::Util::Logger ':levels';
  57         110  
  57         6605  
9 57     57   360 use Scalar::Util qw( refaddr );
  57         109  
  57         4595  
10              
11             =head1 NAME
12              
13             Bio::Phylo::Util::MOP - Meta-object programming, no serviceable parts inside
14              
15             =cut
16              
17             # this will be populated when the attribute handlers are triggered
18             my %methods;
19              
20             # this will progressively store/memoize all superclasses for given classes
21             my %classes;
22              
23             # this will progressively store/memoize the methods for given classes
24             my %class_methods;
25              
26             # this might be used to check the interface of alien subclasses
27       235     sub import {
28            
29             }
30              
31             # my %sims = $mop->get_symtable('Bio::Phylo');
32             sub get_symtable {
33 0     0 0 0 my ( $self, $package ) = @_;
34 0         0 my %symtable;
35             {
36 57     57   337 no strict 'refs';
  57         109  
  57         2432  
  0         0  
37 0         0 %symtable = %{"${package}::"};
  0         0  
38 57     57   298 use strict;
  57         113  
  57         3145  
39             }
40 0         0 return \%symtable;
  0         0  
41             }
42              
43             # $mop->get_method('Bio::Phylo::new')->()
44             sub get_method {
45 1852     1852 0 2494 my ( $self, $fqn ) = @_;
46 1852         1889 my $coderef;
47 0         0 eval {
48 57     57   311 no strict 'refs';
  57         111  
  57         2145  
49 1852         1866 $coderef = \&{"${fqn}"};
  1852         4542  
50 57     57   294 use strict;
  57         120  
  57         11819  
51 1852         2103 };
52 1852         3056 return $coderef;
53             }
54              
55             # @methods = @{ $mop->get_implementations( 'new', $obj || $package ) };
56             sub get_implementations {
57 0     0 0 0 my ( $self, $method, $obj ) = @_;
58 0         0 my @methods = grep { $_->{'name'} eq $method } @{ $self->get_methods($obj) };
  0         0  
  0         0  
59 0         0 return \@methods;
60             }
61              
62             # my @classes = @{ $mop->get_classes($obj) }
63             sub get_classes {
64 13710     13710 0 21443 my ( $self, $obj, $all ) = @_;
65 13710   33     26113 my $class = ref $obj || $obj;
66            
67             # return if already cached
68 13710 100       22840 if ( $classes{$class} ) {
69 13487         41369 return $classes{$class};
70             }
71            
72             # compute, cache, return
73             else {
74 223         529 my ( $seen, $isa ) = ( {}, [] );
75 223         672 _recurse_isa($class, $isa, $seen, $all);
76 223         424 $classes{$class} = $isa;
77 223         1104 return $isa;
78             }
79             }
80              
81             # starting from $class, push all superclasses (+$class) into @$isa,
82             # %$seen is just a helper to avoid getting stuck in cycles
83             sub _recurse_isa {
84 1752     1752   2683 my ( $class, $isa, $seen, $all ) = @_;
85 1752 50       3181 if ( not $seen->{$class} ) {
86 1752         2639 $seen->{$class} = 1;
87 1752 50 33     4998 if ( ( $class ne 'Exporter' and $class ne 'DynaLoader' ) or $all ) {
      33        
88 1752         2662 push @{$isa}, $class;
  1752         2770  
89             }
90 1752         2117 my @isa;
91             {
92 57     57   333 no strict 'refs';
  57         109  
  57         2239  
  0         0  
93 1752         1843 @isa = @{"${class}::ISA"};
  1752         6144  
94 57     57   290 use strict;
  57         115  
  57         34385  
95             }
96 1752         1866 _recurse_isa( $_, $isa, $seen, $all ) for @isa;
  1752         4576  
97             }
98             }
99              
100             # my @methods = @{ $mop->get_methods($obj) };
101             sub get_methods {
102 0     0 0 0 my ( $self, $obj ) = @_;
103 0   0     0 my $class = ref $obj || $obj;
104            
105             # return if already cached
106 0 0       0 if ( $class_methods{$class} ) {
107 0         0 return $class_methods{$class};
108             }
109            
110             # compute, cache, return
111             else {
112 0         0 my $isa = $self->get_classes($obj);
113 0         0 my @methods;
114 0         0 for my $package ( @{ $isa } ) {
  0         0  
115              
116 0         0 my %symtable = %{ $self->get_symtable($package) };
  0         0  
117            
118             # at this point we have lots of things, we just want methods
119 0         0 for my $entry ( keys %symtable ) {
120            
121             # check if entry is a CODE reference
122 0         0 my $can = $package->can( $entry );
123 0 0       0 if ( ref $can eq 'CODE' ) {
124             push @methods, {
125             'package' => $package,
126             'name' => $entry,
127 0         0 'glob' => $symtable{$entry},
128             'code' => $can,
129             };
130             }
131             }
132             }
133 0         0 $class_methods{$class} = \@methods;
134 0         0 return \@methods;
135             }
136             }
137              
138             sub get_methods_by_attribute {
139 326     326 0 491 my ( $self, $obj, $attribute ) = @_;
140 326         540 my $isa = $self->get_classes($obj);
141 326         518 my $methods = $methods{$attribute};
142 326         384 my @return;
143 326         366 for my $class ( @{ $isa } ) {
  326         504  
144 2438 100       4037 if ( $methods->{$class} ) {
145 807         823 for my $key ( keys %{ $methods->{$class} } ) {
  807         1606  
146             push @return, {
147             'package' => $class,
148             'name' => $key,
149 2205         5186 'code' => $methods->{$class}->{$key}
150             };
151             }
152             }
153             }
154 326         707 return \@return;
155             }
156              
157             sub get_accessors {
158 0     0 0 0 my ( $self, $obj ) = @_;
159 0         0 return $self->get_methods_by_attribute($obj,'Accessor');
160             }
161              
162             sub get_mutators {
163 0     0 0 0 my ( $self, $obj ) = @_;
164 0         0 return $self->get_methods_by_attribute($obj,'Mutator');
165             }
166              
167             sub get_abstracts {
168 0     0 0 0 my ( $self, $obj ) = @_;
169 0         0 return $self->get_methods_by_attribute($obj,'Abstract');
170             }
171              
172             sub get_constructors {
173 109     109 0 161 my ( $self, $obj ) = @_;
174 109         214 return $self->get_methods_by_attribute($obj,'Constructor');
175             }
176              
177             sub get_clonables {
178 109     109 0 170 my ( $self, $obj ) = @_;
179 109         212 return $self->get_methods_by_attribute($obj,'Clonable');
180             }
181              
182             sub get_deep_clonables {
183 108     108 0 169 my ( $self, $obj ) = @_;
184 108         215 return $self->get_methods_by_attribute($obj,'DeepClonable');
185             }
186              
187             sub get_destructors {
188 0     0 0 0 my ( $self, $obj ) = @_;
189 0         0 return $self->get_methods_by_attribute($obj,'Destructor');
190             }
191              
192             sub get_privates {
193 0     0 0 0 my ( $self, $obj ) = @_;
194 0         0 return $self->get_methods_by_attribute($obj,'Private');
195             }
196              
197             sub get_statics {
198 0     0 0 0 my ( $self, $obj ) = @_;
199 0         0 return $self->get_methods_by_attribute($obj,'Static');
200             }
201              
202             sub get_serializers {
203 0     0 0 0 my ( $self, $obj ) = @_;
204 0         0 return $self->get_methods_by_attribute($obj,'Serializer');
205             }
206              
207             sub _handler {
208 1886     1886   2744 eval {
209 1886         3291 my ($package, $symbol, $referent, $attr, $data) = @_;
210 1886 50       4680 return if $symbol eq 'ANON';
211 1886         4894 my $name = *$symbol;
212 1886         14361 $name =~ s/.*://;
213 1886 100       5949 $methods{$attr} = {} unless $methods{$attr};
214 1886 100       5249 $methods{$attr}->{$package} = {} unless $methods{$attr}->{$package};
215 1886         4540 $methods{$attr}->{$package}->{$name} = $referent;
216             };
217 1886 50       5021 if ( $@ ) {
218 0           throw 'API' => $@;
219             }
220             }
221              
222             sub UNIVERSAL::Accessor : ATTR(CODE) {
223 0     0 0 0 my ($package, $symbol, $referent, $attr, $data) = @_;
224 0         0 _handler(@_);
225 57     57   369 }
  57         117  
  57         323  
226              
227             sub UNIVERSAL::Private : ATTR(CODE) {
228 0     0 0 0 my ($package, $symbol, $referent, $attr, $data) = @_;
229 57     57   15607 no warnings 'redefine';
  57         139  
  57         8549  
230 0 0       0 return if $symbol eq 'ANON';
231             *$symbol = sub {
232 0     0   0 my ($calling_package) = caller;
233 0         0 my $symname = *$symbol;
234 0         0 $symname =~ s/^\*//;
235 0         0 $symname =~ s/::[^:]+$//;
236 0 0       0 if ( $symname ne $package ) {
237 0         0 throw 'API' => "Attempt to call Private method from outside package";
238             }
239 0         0 $referent->(@_);
240 0         0 };
241 0         0 _handler(@_);
242 57     57   352 }
  57         115  
  57         219  
243              
244             sub UNIVERSAL::Protected : ATTR(CODE) {
245 0     0 0 0 my ($package, $symbol, $referent, $attr, $data) = @_;
246 57     57   13957 no warnings 'redefine';
  57         166  
  57         11264  
247 0 0       0 return if $symbol eq 'ANON';
248             *$symbol = sub {
249 0     0   0 my ($calling_package) = caller;
250 0         0 my $symname = *$symbol;
251 0         0 my $method = $symname;
252 0         0 $symname =~ s/^\*//;
253 0         0 $symname =~ s/::[^:]+$//;
254 0         0 my @package_names = split /::/, $package;
255 0         0 my @calling_names = split /::/, $calling_package;
256 0         0 my $seen_class = $package_names[0] eq $calling_names[0];
257 0 0       0 if ( not $seen_class ) {
258 0         0 throw 'API' => "Attempt to call Protected method $method from outside of top-level namespace";
259             }
260 0         0 $referent->(@_);
261 0         0 };
262 0         0 _handler(@_);
263 57     57   397 }
  57         130  
  57         246  
264              
265             sub UNIVERSAL::Constructor : ATTR(CODE) {
266 180     180 0 350933 my ($package, $symbol, $referent, $attr, $data) = @_;
267 180         567 _handler(@_);
268 57     57   15353 }
  57         116  
  57         202  
269              
270             sub UNIVERSAL::Destructor : ATTR(CODE) {
271 272     272 0 103954 my ($package, $symbol, $referent, $attr, $data) = @_;
272 272         776 _handler(@_);
273 57     57   14990 }
  57         155  
  57         244  
274              
275             sub UNIVERSAL::Static : ATTR(CODE) {
276 0     0 0 0 my ($package, $symbol, $referent, $attr, $data) = @_;
277 0         0 _handler(@_);
278 57     57   15350 }
  57         142  
  57         229  
279              
280             sub UNIVERSAL::Mutator : ATTR(CODE) {
281 0     0 0 0 my ($package, $symbol, $referent, $attr, $data) = @_;
282 0         0 _handler(@_);
283 57     57   15807 }
  57         111  
  57         182  
284              
285             sub UNIVERSAL::Abstract : ATTR(CODE) {
286 0     0 0 0 my ($package, $symbol, $referent, $attr, $data) = @_;
287 0         0 _handler(@_);
288 0 0       0 return if $symbol eq 'ANON';
289 57     57   18433 no warnings 'redefine';
  57         119  
  57         4933  
290 0     0   0 *$symbol = sub { throw 'NotImplemented' => "Abstract method, can't call $symbol" };
  0         0  
291 57     57   338 }
  57         101  
  57         189  
292              
293             sub UNIVERSAL::Clonable : ATTR(CODE) {
294 1316     1316 0 501378 my ($package, $symbol, $referent, $attr, $data) = @_;
295 1316         2980 _handler(@_);
296 57     57   16698 }
  57         111  
  57         221  
297              
298             sub UNIVERSAL::DeepClonable : ATTR(CODE) {
299 118     118 0 6134 my ($package, $symbol, $referent, $attr, $data) = @_;
300 118         316 _handler(@_);
301 57     57   16316 }
  57         343  
  57         191  
302              
303             sub UNIVERSAL::Serializer : ATTR(CODE) {
304 0     0 0 0 my ($package, $symbol, $referent, $attr, $data) = @_;
305 0         0 _handler(@_);
306 57     57   17565 }
  57         117  
  57         197  
307              
308             1;
309              
310