File Coverage

blib/lib/Acme/Marvel/CinematicUniverse/Mite.pm
Criterion Covered Total %
statement 81 147 55.1
branch 22 64 34.3
condition 2 13 15.3
subroutine 17 26 65.3
pod 0 3 0.0
total 122 253 48.2


line stmt bran cond sub pod time code
1 1     1   14 use 5.008001;
  1         2  
2 1     1   5 use strict;
  1         1  
  1         16  
3 1     1   4 use warnings;
  1         13  
  1         31  
4              
5             package Acme::Marvel::CinematicUniverse::Mite;
6              
7             # NOTE: Since the intention is to ship this file with a project, this file
8             # cannot have any non-core dependencies.
9              
10 1     1   15 use strict;
  1         3  
  1         19  
11 1     1   4 use warnings;
  1         2  
  1         69  
12              
13             BEGIN {
14 1         661 *_HAS_AUTOCLEAN = eval { require namespace::autoclean }
15             ? sub () { !!1 }
16             : sub () { !!0 }
17 1 50   1   2 };
18              
19             if ( $] < 5.009005 ) {
20             require MRO::Compat;
21             }
22             else {
23             require mro;
24             }
25              
26             defined ${^GLOBAL_PHASE}
27             or eval { require Devel::GlobalDestruction; 1 }
28             or do {
29             warn "WARNING: Devel::GlobalDestruction recommended!\n";
30             *Devel::GlobalDestruction::in_global_destruction = sub { undef; };
31             };
32              
33             # Constants
34             sub true () { !!1 }
35             sub false () { !!0 }
36             sub ro () { 'ro' }
37             sub rw () { 'rw' }
38             sub rwp () { 'rwp' }
39             sub lazy () { 'lazy' }
40             sub bare () { 'bare' }
41              
42             my $parse_mm_args = sub {
43             my $coderef = pop;
44             my $names = [ map { ref($_) ? @$_ : $_ } @_ ];
45             ( $names, $coderef );
46             };
47              
48             sub _is_compiling {
49 1 50   1   3 return $ENV{MITE_COMPILE} ? 1 : 0;
50             }
51              
52             sub import {
53 1     1   2 my $class = shift;
54 1         2 my %arg = map { lc($_) => 1 } @_;
  2         7  
55 1         3 my ( $caller, $file ) = caller;
56              
57             # Turn on warnings and strict in the caller
58 1         9 warnings->import;
59 1         4 strict->import;
60              
61 1 50       3 my $kind = $arg{'-role'} ? 'role' : 'class';
62              
63 1 50       2 if( _is_compiling() ) {
64 0         0 require Mite::Project;
65 0         0 Mite::Project->default->inject_mite_functions(
66             package => $caller,
67             file => $file,
68             arg => \%arg,
69             kind => $kind,
70             shim => $class,
71             );
72             }
73             else {
74             # Work around Test::Compile's tendency to 'use' modules.
75             # Mite.pm won't stand for that.
76 1 50       3 return if $ENV{TEST_COMPILE};
77              
78             # Changes to this filename must be coordinated with Mite::Compiled
79 1         3 my $mite_file = $file . ".mite.pm";
80 1 50       19 if( !-e $mite_file ) {
81 0         0 require Carp;
82 0         0 Carp::croak("Compiled Mite file ($mite_file) for $file is missing");
83             }
84              
85             {
86 1         2 local @INC = ('.', @INC);
  1         4  
87 1         411 require $mite_file;
88             }
89              
90 1         5 $class->_inject_mite_functions( $caller, $file, $kind, \%arg );
91             }
92              
93 1         81 if ( _HAS_AUTOCLEAN and not $arg{'-unclean'} ) {
94             'namespace::autoclean'->import( -cleanee => $caller );
95             }
96             }
97              
98             sub _inject_mite_functions {
99 1     1   3 my ( $class, $caller, $file, $kind, $arg ) = ( shift, @_ );
100 1 50   8   4 my $requested = sub { $arg->{$_[0]} ? 1 : $arg->{'!'.$_[0]} ? 0 : $_[1]; };
  8 100       30  
101              
102 1     1   6 no strict 'refs';
  1         2  
  1         395  
103 1         3 my $has = $class->_make_has( $caller, $file, $kind );
104 1 50       2 *{"$caller\::has"} = $has if $requested->( has => 1 );
  1         5  
105 1 50       2 *{"$caller\::param"} = $has if $requested->( param => 0 );
  1         3  
106 1 50       2 *{"$caller\::field"} = $has if $requested->( field => 0 );
  0         0  
107              
108 1 50       3 *{ $caller .'::with' } = $class->_make_with( $caller, $file, $kind )
  1         4  
109             if $requested->( with => 1 );
110              
111 1     0   13 *{ $caller .'::extends'} = sub {}
112 1 50 33     3 if $kind eq 'class' && $requested->( extends => 1 );
113 0     0   0 *{ $caller .'::requires'} = sub {}
114 1 50 33     4 if $kind eq 'role' && $requested->( requires => 1 );
115              
116 1 50       4 my $MM = ( $kind eq 'role' ) ? \@{"$caller\::METHOD_MODIFIERS"} : [];
  0         0  
117              
118 1         2 for my $modifier ( qw/ before after around / ) {
119 3 50       5 next unless $requested->( $modifier => 1 );
120              
121 3 50       5 if ( $kind eq 'class' ) {
122 3         15 *{"$caller\::$modifier"} = sub {
123 0     0   0 $class->$modifier( $caller, @_ );
124 0         0 return;
125 3         8 };
126             }
127             else {
128 0         0 *{"$caller\::$modifier"} = sub {
129 0     0   0 my ( $names, $coderef ) = &$parse_mm_args;
130 0         0 push @$MM, [ $modifier, $names, $coderef ];
131 0         0 return;
132 0         0 };
133             }
134             }
135             }
136              
137             sub _make_has {
138 1     1   2 my ( $class, $caller, $file, $kind ) = @_;
139              
140             return sub {
141 8     8   12 my $names = shift;
142 8 50       18 $names = [$names] unless ref $names;
143 8         13 my %args = @_;
144 8         16 for my $name ( @$names ) {
145 8         11 $name =~ s/^\+//;
146              
147 8         11 my $default = $args{default};
148 8 50       15 if ( ref $default eq 'CODE' ) {
149 1     1   6 no strict 'refs';
  1         2  
  1         55  
150 0         0 ${$caller .'::__'.$name.'_DEFAULT__'} = $default;
  0         0  
151             }
152              
153 8         9 my $builder = $args{builder};
154 8 50       10 if ( ref $builder eq 'CODE' ) {
155 1     1   6 no strict 'refs';
  1         1  
  1         52  
156 0         0 *{"$caller\::_build_$name"} = $builder;
  0         0  
157             }
158              
159 8         15 my $trigger = $args{trigger};
160 8 50       12 if ( ref $trigger eq 'CODE' ) {
161 1     1   6 no strict 'refs';
  1         1  
  1         672  
162 0         0 *{"$caller\::_trigger_$name"} = $trigger;
  0         0  
163             }
164             }
165              
166 8         26 return;
167 1         5 };
168             }
169              
170             sub _make_with {
171 1     1   3 my ( $class, $caller, $file, $kind ) = @_;
172              
173             return sub {
174 0     0     while ( @_ ) {
175 0           my $role = shift;
176 0 0         my $args = ref($_[0]) ? shift : undef;
177 0 0 0       if ( $INC{'Role/Tiny.pm'} and 'Role::Tiny'->is_role( $role ) ) {
178 0           $class->_finalize_application_roletiny( $role, $caller, $args );
179             }
180             else {
181 0           $role->__FINALIZE_APPLICATION__( $caller, $args );
182             }
183             }
184 0           return;
185 1         3 };
186             }
187              
188             {
189             my ( $cb_before, $cb_after );
190             sub _finalize_application_roletiny {
191 0     0     my ( $class, $role, $caller, $args ) = @_;
192              
193 0 0         if ( $INC{'Role/Hooks.pm'} ) {
194 0   0       $cb_before ||= \%Role::Hooks::CALLBACKS_BEFORE_APPLY;
195 0   0       $cb_after ||= \%Role::Hooks::CALLBACKS_AFTER_APPLY;
196             }
197 0 0         if ( $cb_before ) {
198 0 0         $_->( $role, $caller ) for @{ $cb_before->{$role} || [] };
  0            
199             }
200              
201 0           'Role::Tiny'->_check_requires( $caller, $role );
202              
203 0           my $info = $Role::Tiny::INFO{$role};
204 0 0         for ( @{ $info->{modifiers} || [] } ) {
  0            
205 0           my @args = @$_;
206 0           my $kind = shift @args;
207 0           $class->$kind( $caller, @args );
208             }
209              
210 0 0         if ( $cb_after ) {
211 0 0         $_->( $role, $caller ) for @{ $cb_after->{$role} || [] };
  0            
212             }
213              
214 0           return;
215             }
216             }
217              
218             {
219             my $get_orig = sub {
220             my ( $caller, $name ) = @_;
221              
222             my $orig = $caller->can($name);
223             return $orig if $orig;
224              
225             require Carp;
226             Carp::croak( "Cannot modify method $name in $caller: no such method" );
227             };
228              
229             sub before {
230 0     0 0   my ( $me, $caller ) = ( shift, shift );
231 0           my ( $names, $coderef ) = &$parse_mm_args;
232 0           for my $name ( @$names ) {
233 0           my $orig = $get_orig->( $caller, $name );
234 0           local $@;
235 0 0         eval <<"BEFORE" or die $@;
236             package $caller;
237             no warnings 'redefine';
238             sub $name {
239             \$coderef->( \@_ );
240             \$orig->( \@_ );
241             }
242             1;
243             BEFORE
244             }
245 0           return;
246             }
247              
248             sub after {
249 0     0 0   my ( $me, $caller ) = ( shift, shift );
250 0           my ( $names, $coderef ) = &$parse_mm_args;
251 0           for my $name ( @$names ) {
252 0           my $orig = $get_orig->( $caller, $name );
253 0           local $@;
254 0 0         eval <<"AFTER" or die $@;
255             package $caller;
256             no warnings 'redefine';
257             sub $name {
258             my \@r;
259             if ( wantarray ) {
260             \@r = \$orig->( \@_ );
261             }
262             elsif ( defined wantarray ) {
263             \@r = scalar \$orig->( \@_ );
264             }
265             else {
266             \$orig->( \@_ );
267             1;
268             }
269             \$coderef->( \@_ );
270             wantarray ? \@r : \$r[0];
271             }
272             1;
273             AFTER
274             }
275 0           return;
276             }
277              
278             sub around {
279 0     0 0   my ( $me, $caller ) = ( shift, shift );
280 0           my ( $names, $coderef ) = &$parse_mm_args;
281 0           for my $name ( @$names ) {
282 0           my $orig = $get_orig->( $caller, $name );
283 0           local $@;
284 0 0         eval <<"AROUND" or die $@;
285             package $caller;
286             no warnings 'redefine';
287             sub $name {
288             \$coderef->( \$orig, \@_ );
289             }
290             1;
291             AROUND
292             }
293 0           return;
294             }
295             }
296              
297             1;
298              
299             __END__