File Coverage

blib/lib/Sub/HandlesVia/Mite.pm
Criterion Covered Total %
statement 64 171 37.4
branch 18 66 27.2
condition 1 19 5.2
subroutine 15 30 50.0
pod 0 11 0.0
total 98 297 33.0


line stmt bran cond sub pod time code
1             # NOTE: Since the intention is to ship this file with a project, this file
2             # cannot have any non-core dependencies.
3             package Sub::HandlesVia::Mite;
4 96     96   1683 use 5.008001;
  96         350  
5 96     96   588 use strict;
  96         203  
  96         2110  
6 96     96   471 use warnings;
  96         237  
  96         2878  
7 96     96   752 no strict 'refs';
  96         407  
  96         31275  
8              
9             if ( $] < 5.009005 ) { require MRO::Compat; }
10             else { require mro; }
11              
12             defined ${^GLOBAL_PHASE}
13             or eval { require Devel::GlobalDestruction; 1 }
14             or do {
15             carp( "WARNING: Devel::GlobalDestruction recommended!" );
16             *Devel::GlobalDestruction::in_global_destruction = sub { undef; };
17             };
18              
19             # Constants
20             sub true () { !!1 }
21             sub false () { !!0 }
22             sub ro () { 'ro' }
23             sub rw () { 'rw' }
24             sub rwp () { 'rwp' }
25             sub lazy () { 'lazy' }
26             sub bare () { 'bare' }
27              
28             # More complicated constants
29             BEGIN {
30 96     96   602 my @bool = ( \&false, \&true );
31 96         432 *_HAS_AUTOCLEAN = $bool[ 0+!! eval { require namespace::autoclean } ];
  96         40575  
32 96   0     1399945 *STRICT = $bool[ 0+!! ( $ENV{PERL_STRICT} || $ENV{EXTENDED_TESTING} || $ENV{AUTHOR_TESTING} || $ENV{RELEASE_TESTING} ) ];
33             };
34              
35             # Exportable error handlers
36             sub _error_handler {
37 0     0   0 my ( $func, $message, @args ) = @_;
38 0 0       0 if ( @args ) {
39 0         0 require Data::Dumper;
40 0         0 local $Data::Dumper::Terse = 1;
41 0         0 local $Data::Dumper::Indent = 0;
42             $message = sprintf $message, map {
43 0 0       0 ref($_) ? Data::Dumper::Dumper($_) : defined($_) ? $_ : '(undef)'
  0 0       0  
44             } @args;
45             }
46 0         0 my $next = do { require Carp; \&{"Carp::$func"} };
  0         0  
  0         0  
  0         0  
47 0         0 @_ = ( $message );
48 0         0 goto $next;
49             }
50              
51 0     0 0 0 sub carp { unshift @_, 'carp' ; goto \&_error_handler }
  0         0  
52 0     0 0 0 sub croak { unshift @_, 'croak' ; goto \&_error_handler }
  0         0  
53 0     0 0 0 sub confess { unshift @_, 'confess'; goto \&_error_handler }
  0         0  
54              
55             # Exportable guard function
56             {
57             my $GUARD_PACKAGE = __PACKAGE__ . '::Guard';
58 12823 50   12823   42106 *{"$GUARD_PACKAGE\::DESTROY"} = sub { $_[0][0] or $_[0][1]->() };
59 0     0   0 *{"$GUARD_PACKAGE\::restore"} = sub { $_[0]->DESTROY; $_[0][0] = true };
  0         0  
60 0     0   0 *{"$GUARD_PACKAGE\::dismiss"} = sub { $_[0][0] = true };
61 0     0   0 *{"$GUARD_PACKAGE\::peek"} = sub { $_[0][2] };
62 12823     12823   44367 *guard = sub (&) { bless [ 0, @_ ] => $GUARD_PACKAGE };
63             }
64              
65             # Exportable lock and unlock
66             sub _lul {
67 0     0   0 my ( $lul, $ref ) = @_;
68 0 0       0 if ( ref $ref eq 'ARRAY' ) {
69 0         0 &Internals::SvREADONLY( $ref, $lul );
70 0         0 &Internals::SvREADONLY( \$_, $lul ) for @$ref;
71 0         0 return;
72             }
73 0 0       0 if ( ref $ref eq 'HASH' ) {
74 0         0 &Internals::hv_clear_placeholders( $ref );
75 0         0 &Internals::SvREADONLY( $ref, $lul );
76 0         0 &Internals::SvREADONLY( \$_, $lul ) for values %$ref;
77 0         0 return;
78             }
79 0         0 return;
80             }
81              
82             sub lock {
83 0     0 0 0 unshift @_, true;
84 0         0 goto \&_lul;
85             }
86              
87             sub unlock {
88 0     0 0 0 my $ref = shift;
89 0         0 _lul( 0 , $ref );
90 0     0   0 &guard( sub { _lul( 1, $ref ) } );
  0         0  
91             }
92              
93             sub _is_compiling {
94 570 50   570   2916 defined $Mite::COMPILING and $Mite::COMPILING eq __PACKAGE__;
95             }
96              
97             sub import {
98 570     570   1676 my $me = shift;
99 570         4433 my %arg = map +( lc($_) => true ), @_;
100 570         2797 my ( $caller, $file ) = caller;
101              
102 570 50       1972 if( _is_compiling() ) {
103 0         0 require Mite::Project;
104 0         0 'Mite::Project'->default->inject_mite_functions(
105             'package' => $caller,
106             'file' => $file,
107             'arg' => \%arg,
108             'shim' => $me,
109             );
110             }
111             else {
112             # Changes to this filename must be coordinated with Mite::Compiled
113 570         1843 my $mite_file = $file . '.mite.pm';
114 570         3587 local @INC = ( '.', @INC );
115 570         1147 local $@;
116 570 50       1196 if ( not eval { require $mite_file; 1 } ) {
  570         204350  
  570         4077  
117 0         0 my $e = $@;
118 0         0 croak "Compiled Mite file ($mite_file) for $file is missing or an error occurred loading it: $e";
119             }
120             }
121              
122 570         8241 'warnings'->import;
123 570         2763 'strict'->import;
124             'namespace::autoclean'->import( -cleanee => $caller )
125 570 50       4713 if _HAS_AUTOCLEAN && !$arg{'-unclean'};
126             }
127              
128             {
129             my ( $cb_before, $cb_after );
130             sub _finalize_application_roletiny {
131 0     0   0 my ( $me, $role, $caller, $args ) = @_;
132 0 0       0 if ( $INC{'Role/Hooks.pm'} ) {
133 0   0     0 $cb_before ||= \%Role::Hooks::CALLBACKS_BEFORE_APPLY;
134 0   0     0 $cb_after ||= \%Role::Hooks::CALLBACKS_AFTER_APPLY;
135             }
136 0 0       0 if ( $cb_before ) {
137 0 0       0 $_->( $role, $caller ) for @{ $cb_before->{$role} || [] };
  0         0  
138             }
139 0         0 'Role::Tiny'->_check_requires( $caller, $role );
140 0         0 my $info = $Role::Tiny::INFO{$role};
141 0 0       0 for ( @{ $info->{modifiers} || [] } ) {
  0         0  
142 0         0 my @args = @$_;
143 0         0 my $modification = shift @args;
144 0         0 my $handler = "HANDLE_$modification";
145 0         0 $me->$handler( $caller, undef, @args );
146             }
147 0 0       0 if ( $cb_after ) {
148 0 0       0 $_->( $role, $caller ) for @{ $cb_after->{$role} || [] };
  0         0  
149             }
150 0         0 return;
151             }
152              
153             # Usage: $me, $caller, @with_args
154             sub HANDLE_with {
155 0     0 0 0 my ( $me, $caller ) = ( shift, shift );
156 0         0 while ( @_ ) {
157 0         0 my $role = shift;
158 0 0       0 my $args = ref($_[0]) ? shift : undef;
159 0 0 0     0 if ( $INC{'Role/Tiny.pm'} and 'Role::Tiny'->is_role( $role ) ) {
160 0         0 $me->_finalize_application_roletiny( $role, $caller, $args );
161             }
162             else {
163 0         0 $role->__FINALIZE_APPLICATION__( $caller, $args );
164             }
165             }
166 0         0 return;
167             }
168             }
169              
170             # Usage: $me, $caller, $keyword, @has_args
171             sub HANDLE_has {
172 3984     3984 0 8154 my ( $me, $caller, $keyword, $names ) = ( shift, shift, shift, shift );
173 3984 50       8506 if ( @_ % 2 ) {
174 0         0 my $default = shift;
175 0 0       0 unshift @_, ( 'CODE' eq ref( $default ) )
176             ? ( is => lazy, builder => $default )
177             : ( is => ro, default => $default );
178             }
179 3984         10034 my %spec = @_;
180 3984         5513 my $code;
181 3984 100       8030 for my $name ( ref($names) ? @$names : $names ) {
182 4458         7123 $name =~ s/^\+//;
183             'CODE' eq ref( $code = $spec{default} )
184 4458 100       9441 and ${"$caller\::__$name\_DEFAULT__"} = $code;
  96         607  
185             'CODE' eq ref( $code = $spec{builder} )
186 4458 100       8982 and *{"$caller\::_build_$name"} = $code;
  1038         5576  
187             'CODE' eq ref( $code = $spec{trigger} )
188 4458 50       8668 and *{"$caller\::_trigger_$name"} = $code;
  0         0  
189             'CODE' eq ref( $code = $spec{clone} )
190 4458 50       9156 and *{"$caller\::_clone_$name"} = $code;
  0         0  
191             }
192 3984         9540 return;
193             }
194              
195             {
196             my $_kind = sub { ${ shift() . '::USES_MITE' } =~ /Role/ ? 'role' : 'class' };
197              
198             sub _get_orig_method {
199 1     1   2 my ( $caller, $name ) = @_;
200 1         9 my $orig = $caller->can( $name );
201 1 50       5 return $orig if $orig;
202 0         0 croak "Cannot modify method $name in $caller: no such method";
203             }
204              
205             sub _parse_mm_args {
206 1     1   2 my $coderef = pop;
207 1 50       3 my $names = [ map { ref($_) ? @$_ : $_ } @_ ];
  1         6  
208 1         3 ( $names, $coderef );
209             }
210              
211             # Usage: $me, $caller, $caller_kind, @before_args
212             sub HANDLE_before {
213 0     0 0 0 my ( $me, $caller, $kind ) = ( shift, shift, shift );
214 0         0 my ( $names, $coderef ) = &_parse_mm_args;
215 0   0     0 $kind ||= $caller->$_kind;
216 0 0       0 if ( $kind eq 'role' ) {
217 0         0 push @{"$caller\::METHOD_MODIFIERS"},
  0         0  
218             [ before => $names, $coderef ];
219 0         0 return;
220             }
221 0         0 for my $name ( @$names ) {
222 0         0 my $orig = _get_orig_method( $caller, $name );
223 0         0 local $@;
224 0 0       0 eval <<"BEFORE" or die $@;
225             package $caller;
226             no warnings 'redefine';
227             sub $name {
228             \$coderef->( \@_ );
229             \$orig->( \@_ );
230             }
231             1;
232             BEFORE
233             }
234 0         0 return;
235             }
236              
237             # Usage: $me, $caller, $caller_kind, @after_args
238             sub HANDLE_after {
239 0     0 0 0 my ( $me, $caller, $kind ) = ( shift, shift, shift );
240 0         0 my ( $names, $coderef ) = &_parse_mm_args;
241 0   0     0 $kind ||= $caller->$_kind;
242 0 0       0 if ( $kind eq 'role' ) {
243 0         0 push @{"$caller\::METHOD_MODIFIERS"},
  0         0  
244             [ after => $names, $coderef ];
245 0         0 return;
246             }
247 0         0 for my $name ( @$names ) {
248 0         0 my $orig = _get_orig_method( $caller, $name );
249 0         0 local $@;
250 0 0       0 eval <<"AFTER" or die $@;
251             package $caller;
252             no warnings 'redefine';
253             sub $name {
254             my \@r;
255             if ( wantarray ) {
256             \@r = \$orig->( \@_ );
257             }
258             elsif ( defined wantarray ) {
259             \@r = scalar \$orig->( \@_ );
260             }
261             else {
262             \$orig->( \@_ );
263             1;
264             }
265             \$coderef->( \@_ );
266             wantarray ? \@r : \$r[0];
267             }
268             1;
269             AFTER
270             }
271 0         0 return;
272             }
273              
274             # Usage: $me, $caller, $caller_kind, @around_args
275             sub HANDLE_around {
276 1     1 0 4 my ( $me, $caller, $kind ) = ( shift, shift, shift );
277 1         3 my ( $names, $coderef ) = &_parse_mm_args;
278 1   33     4 $kind ||= $caller->$_kind;
279 1 50       12 if ( $kind eq 'role' ) {
280 0         0 push @{"$caller\::METHOD_MODIFIERS"},
  0         0  
281             [ around => $names, $coderef ];
282 0         0 return;
283             }
284 1         4 for my $name ( @$names ) {
285 1         4 my $orig = _get_orig_method( $caller, $name );
286 1         2 local $@;
287 1 50   1   89 eval <<"AROUND" or die $@;
  1         6  
  1         2  
  1         65  
288             package $caller;
289             no warnings 'redefine';
290             sub $name {
291             \$coderef->( \$orig, \@_ );
292             }
293             1;
294             AROUND
295             }
296 1         4 return;
297             }
298             }
299              
300             # Usage: $me, $caller, $caller_kind, @signature_for_args
301             sub HANDLE_signature_for {
302 3     3 0 34 my ( $me, $caller, $kind, $name ) = @_;
303 0         0 $name =~ s/^\+//;
304 0         0 $me->HANDLE_around( $caller, $kind, $name, ${"$caller\::SIGNATURE_FOR"}{$name} );
  0         0  
305 0         0 return;
306             }
307              
308             1;