File Coverage

blib/lib/Mite/Trait/HasSuperclasses.pm
Criterion Covered Total %
statement 79 81 97.5
branch 11 18 61.1
condition 2 3 66.6
subroutine 17 18 94.4
pod 0 4 0.0
total 109 124 87.9


line stmt bran cond sub pod time code
1 109     109   2805 use 5.010001;
  109         423  
2 109     109   777 use strict;
  109         417  
  109         15534  
3 109     109   738 use warnings;
  109         362  
  109         6972  
4              
5             package Mite::Trait::HasSuperclasses;
6 109     109   894 use Mite::Miteception -role, -all;
  109         389  
  109         1126  
7              
8             our $AUTHORITY = 'cpan:TOBYINK';
9             our $VERSION = '0.012000';
10              
11             # Super classes as class names
12             has extends =>
13             is => bare,
14             accessor => 'superclasses',
15             isa => ArrayRef[ValidClassName],
16             default => sub { [] },
17             default_does_trigger => true,
18             trigger => sub {
19 176     176   467 my $self = shift;
20              
21 176 50       1436 return if !$self->name; # called from constructor
22              
23             # Set up our @ISA so we can use mro to calculate the class hierarchy
24 176         1014 $self->_set_isa;
25              
26             # Allow $self->parents to recalculate itself
27 176         1214 $self->_clear_parents;
28             };
29              
30             has superclass_args =>
31             is => rw,
32             isa => Map[ NonEmptyStr, HashRef|Undef ],
33 143     143   637 builder => sub { {} };
34              
35             # Super classes as Mite::Classes populated from $self->superclasses
36             has parents =>
37             is => ro,
38             isa => ArrayRef[MiteClass],
39             # Build on demand to allow the project to load all the classes first
40             lazy => true,
41             builder => '_build_parents',
42             clearer => '_clear_parents';
43              
44             sub _set_isa {
45 176     176   499 my $self = shift;
46              
47 176         704 my $name = $self->name;
48              
49 176         1475 mro::set_mro($name, "c3");
50 109     109   1069 no strict 'refs';
  109         994  
  109         10532  
51 176         404 @{$name.'::ISA'} = @{$self->superclasses};
  176         4078  
  176         1022  
52              
53 176         827 return;
54             }
55              
56             sub get_isa {
57 2     2 0 4 my $self = shift;
58              
59 2         7 my $name = $self->name;
60              
61 109     109   969 no strict 'refs';
  109         345  
  109         63812  
62 2         4 return @{$name.'::ISA'};
  2         16  
63             }
64              
65             sub linear_isa {
66 621     621 0 1184 my $self = shift;
67              
68 621         963 return @{mro::get_linear_isa($self->name)};
  621         4997  
69             }
70              
71             sub linear_parents {
72 71     71 0 200 my $self = shift;
73              
74 71         265 my $project = $self->project;
75              
76 71         315 return grep defined, map { $project->class($_) } $self->linear_isa;
  166         520  
77             }
78              
79             sub handle_extends_keyword {
80 23     23 0 67 my $self = shift;
81              
82 23         58 my ( @extends, %extends_args );
83 23         116 while ( @_ ) {
84 25         66 my $class = shift;
85 25 100       138 my $args = Str->check( $_[0] ) ? undef : shift;
86 25         491 push @extends, $class;
87 25         121 $extends_args{$class} = $args;
88             }
89 23         156 $self->superclasses( \@extends );
90 23         188 $self->superclass_args( \%extends_args );
91              
92 23         82 return;
93             }
94              
95             sub _build_parents {
96 106     106   308 my $self = shift;
97              
98 106         482 my $extends = $self->superclasses;
99 106 100       687 return [] if !@$extends;
100              
101             # Load each parent and store its Mite::Class
102 18         41 my @parents;
103 18         71 for my $parent_name (@$extends) {
104 21         72 push @parents, $self->_get_parent($parent_name);
105             }
106              
107 18         122 return \@parents;
108             }
109              
110             sub _get_parent {
111 98     98   284 my ( $self, $parent_name ) = ( shift, @_ );
112              
113 98         306 my $project = $self->project;
114              
115             # See if it's already loaded
116 98         413 my $parent = $project->class($parent_name);
117 98 100       519 return $parent if $parent;
118              
119             # If not, try to load it
120 10         673 eval "require $parent_name;";
121 10         394 $parent = $project->class($parent_name);
122 10 50       28 return $parent if $parent;
123              
124 10         70 return;
125             }
126              
127             before inject_mite_functions => sub {
128             my ( $self, $file, $arg ) = ( shift, @_ );
129              
130             my $requested = sub { $arg->{$_[0]} ? 1 : $arg->{'!'.$_[0]} ? 0 : $arg->{'-all'} ? 1 : $_[1]; };
131             my $defaults = ! $arg->{'!-defaults'};
132             my $shim = $self->shim_name;
133             my $package = $self->name;
134             my $fake_ns = $self->project->can('_module_fakeout_namespace') && $self->project->_module_fakeout_namespace;
135              
136 109     109   1047 no strict 'refs';
  109         638  
  109         76310  
137              
138             if ( $requested->( 'extends', $defaults ) ) {
139              
140             *{ $package .'::extends' } = sub {
141 0 0   0     return $self->handle_extends_keyword(
    0          
142             defined( $fake_ns )
143             ? map Str->check($_) ? "$fake_ns\::$_" : $_, @_
144             : @_
145             );
146             };
147              
148             $self->imported_keywords->{'extends'} = 'sub {}';
149             }
150             };
151              
152             around compilation_stages => sub {
153             my ( $next, $self ) = ( shift, shift );
154             my @stages = $self->$next( @_ );
155             push @stages, qw( _compile_extends );
156             return @stages;
157             };
158              
159             around _compile_meta_method => sub {
160             my ( $next, $self ) = ( shift, shift );
161              
162             # Check if we are inheriting from a Mite class in this project
163             my $inherit_from_mite = do {
164             # First parent
165             my $first_isa = do {
166             my @isa = $self->linear_isa;
167             shift @isa;
168             shift @isa;
169             };
170             !! ( $first_isa and $self->_get_parent( $first_isa ) );
171             };
172              
173             return '' if $inherit_from_mite;
174              
175             return $self->$next( @_ );
176             };
177              
178             sub _compile_extends {
179 113     113   349 my $self = shift;
180              
181 113         537 my $extends = $self->superclasses;
182 113 100       1006 return '' unless @$extends;
183              
184 25         99 my $source = $self->source;
185              
186             my $require_list = join "\n\t",
187 7         50 map { "require $_;" }
188             # Don't require a class from the same source
189 25   66     142 grep { !$source || !$source->has_class($_) }
  27         255  
190             @$extends;
191              
192             my $version_tests = join "\n\t",
193             map { sprintf '%s->VERSION( %s );',
194             B::perlstring( $_ ),
195 0         0 B::perlstring( $self->superclass_args->{$_}{'-version'} )
196             }
197             grep {
198 25         88 $self->superclass_args->{$_}
199 27 50       163 and $self->superclass_args->{$_}{'-version'}
200             }
201             @$extends;
202              
203 25         192 my $isa_list = join ", ", map B::perlstring($_), @$extends;
204              
205 25         206 return <<"END";
206             BEGIN {
207             $require_list
208             $version_tests
209             use mro 'c3';
210             our \@ISA;
211             push \@ISA, $isa_list;
212             }
213             END
214             }
215              
216             around _compile_mop_postamble => sub {
217             my ( $next, $self ) = ( shift, shift );
218             my $code = $self->$next( @_ );
219              
220             my @superclasses = @{ $self->superclasses || [] }
221             or return $code;
222             $code .= sprintf "Moose::Util::find_meta( %s )->superclasses( %s );\n",
223             B::perlstring( $self->name ),
224             join q{, }, map B::perlstring( $_ ), @superclasses;
225              
226             return $code;
227             };
228              
229             1;