File Coverage

blib/lib/exact.pm
Criterion Covered Total %
statement 171 179 95.5
branch 48 70 68.5
condition 14 30 46.6
subroutine 31 33 93.9
pod 6 6 100.0
total 270 318 84.9


line stmt bran cond sub pod time code
1             package exact;
2             # ABSTRACT: Perl pseudo pragma to enable strict, warnings, features, mro, filehandle methods
3              
4 10     10   2426443 use 5.014;
  10         107  
5 10     10   51 use strict;
  10         18  
  10         206  
6 10     10   52 use warnings;
  10         17  
  10         229  
7 10     10   4797 use namespace::autoclean;
  10         186318  
  10         45  
8 10     10   5507 use Import::Into;
  10         5449  
  10         349  
9 10     10   70 use Sub::Util 'set_subname';
  10         34  
  10         638  
10 10     10   5283 use Syntax::Keyword::Try;
  10         24521  
  10         60  
11              
12             our $VERSION = '1.23'; # VERSION
13              
14 10     10   1100 use feature ();
  10         34  
  10         172  
15 10     10   47 use utf8 ();
  10         20  
  10         202  
16 10     10   85 use mro ();
  10         21  
  10         280  
17 10     10   47 use Carp qw( croak carp confess cluck );
  10         21  
  10         617  
18 10     10   4884 use IO::File ();
  10         89971  
  10         271  
19 10     10   64 use IO::Handle ();
  10         27  
  10         140  
20 10     10   58 use Try::Tiny ();
  10         18  
  10         126  
21 10     10   4863 use PerlX::Maybe ();
  10         25154  
  10         9091  
22              
23             my ($perl_version) = $^V =~ /^v5\.(\d+)/;
24              
25             my $features_available = ( %feature::feature_bundle and $feature::feature_bundle{all} )
26             ? $feature::feature_bundle{all}
27             : [ qw( say state switch unicode_strings ) ];
28              
29             my $functions_available = [ qw(
30             nostrict nowarnings
31             nofeatures nobundle noskipexperimentalwarnings
32             noutf8 noc3 nocarp notry trytiny nomaybe noautoclean
33             ) ];
34              
35             my $functions_deprecated = ['noexperiments'];
36              
37             my ( $no_parent, $late_parent );
38              
39             sub import {
40 18     18   3468 my ( $self, $caller ) = ( shift, caller() );
41              
42 18         47 my ( @features, @nofeatures, @functions, @bundles, @classes );
43 18         45 for (@_) {
44 8         23 ( my $opt = $_ ) =~ s/^\-//;
45              
46 8 50 66     44 if ( $opt eq 'class' ) {
    50          
    50          
    100          
    100          
    100          
    100          
47 0         0 push( @classes, $opt );
48             }
49             elsif ( $opt eq 'cor' ) {
50 0         0 push( @features, 'class' );
51             }
52             elsif ( $opt eq 'nocor' ) {
53 0         0 push( @nofeatures, 'class' );
54             }
55 112         171 elsif ( grep { $_ eq $opt } @$features_available ) {
56 2         4 push( @features, $opt );
57             }
58 84         167 elsif ( my ($nofeature) = grep { 'no' . $_ eq $opt } @$features_available ) {
59 1         5 push( @nofeatures, $nofeature );
60             }
61 65         121 elsif ( grep { $_ eq $opt } @$functions_available, @$functions_deprecated ) {
62 2 50       3 push( @functions, $opt ) if ( grep { $_ eq $opt } @$functions_available );
  24         40  
63             }
64             elsif ( $opt =~ /^:?v?5?\.?(\d+)/ and $1 >= 10 ) {
65 1         4 push( @bundles, $1 );
66             }
67             else {
68 2 50       11 push( @classes, $opt ) if ( $opt !~ /^no[a-z]{2}/ );
69             }
70             }
71              
72 18 50       122 strict ->import unless ( grep { $_ eq 'nostrict' } @functions );
  2         15  
73 18 50       224 warnings->import unless ( grep { $_ eq 'nowarnings' } @functions );
  2         32  
74              
75 18 100 66     133 if (@bundles) {
    100          
76 1         111 feature->import( ':5.' . $_ ) for (@bundles);
77             }
78             elsif (
79             not grep { $_ eq 'nofeatures' } @functions and
80             not grep { $_ eq 'nobundle' } @functions
81             ) {
82 16 50       1612 feature->import( $perl_version >= 16 ? ':all' : ':5.' . $perl_version );
83             }
84 18         186 feature->import($_) for (@features);
85 18         45 feature->unimport($_) for (@nofeatures);
86              
87 18 50       59 unless ( grep { $_ eq 'noutf8' } @functions ) {
  2         8  
88 18         110 utf8->import;
89 18         213 binmode( $_, ':utf8' ) for ( *STDIN, *STDERR, *STDOUT );
90 18         94 'open'->import::into( $caller, ':std', ':utf8' );
91             }
92              
93 18 50       23924 mro::set_mro( $caller, 'c3' ) unless ( grep { $_ eq 'noc3' } @functions );
  2         19  
94              
95 72         93 monkey_patch( $self, $caller, ( map { $_ => \&{ 'Carp::' . $_ } } qw( croak carp confess cluck ) ) )
  72         233  
96 18 50       64 unless ( grep { $_ eq 'nocarp' } @functions );
  2         8  
97              
98             feature->unimport('try') if (
99 252         403 grep { $_ eq 'try' } @$features_available and
100             (
101             grep { $_ eq 'notry' } @functions or
102 18 0 0     50 grep { $_ eq 'trytiny' } @functions
      33        
103             )
104             );
105             Syntax::Keyword::Try->import_into($caller) if (
106             $perl_version < 36 and
107 2         12 not grep { $_ eq 'notry' } @functions and
108 18 50 33     241 not grep { $_ eq 'trytiny' } @functions
  2   33     15  
109             );
110 18 50       823 Try::Tiny->import::into($caller) if ( grep { $_ eq 'trytiny' } @functions );
  2         10  
111              
112 72         85 monkey_patch( $self, $caller, ( map { $_ => \&{ 'PerlX::Maybe::' . $_ } } qw(
  72         227  
113             maybe provided provided_deref provided_deref_with_maybe
114 18 50       57 ) ) ) unless ( grep { $_ eq 'nomaybe' } @functions );
  2         13  
115              
116 18         37 my @late_parents = ();
117             my $use = sub {
118 3     3   8 my ( $class, $pm, $caller, $params ) = @_;
119              
120 3         5 my $failed_require;
121             try {
122             require "$pm" unless ( do {
123 10     10   119 no strict 'refs';
  10         38  
  10         433  
124 10     10   78 no warnings 'once';
  10         21  
  10         1633  
125             ${"${caller}::INC"}{$pm};
126             } );
127             }
128 3         8 catch ($e) {
129             croak($e) unless ( index( $e, qq{Can't locate $pm in } ) == 0 );
130             return 0;
131             }
132              
133 2         5 ( $no_parent, $late_parent ) = ( undef, undef );
134              
135 2         4 my $is_exact_extension = 0;
136             {
137 10     10   85 no strict 'refs';
  10         26  
  10         5261  
  2         4  
138 2         10 $is_exact_extension = grep { index( $_, 'exact::' ) == 0 } $class, @{"${class}::ISA"};
  2         13  
  2         12  
139             }
140 2 100 66     18 $class->import( $params, $caller ) if ( $is_exact_extension and $class->can('import') );
141              
142 2 50 66     39 if ($late_parent) {
    100          
143 0         0 push( @late_parents, [ $class, $caller ] );
144             }
145             elsif ( not $no_parent and index( $class, 'exact::' ) != 0 ) {
146 1         5 $self->add_isa( $class, $caller );
147             }
148              
149 2         13 return 1;
150 18         81 };
151 18         45 for my $class (@classes) {
152 2 50       9 my $params = ( $class =~ s/\(([^\)]+)\)// ) ? $1 : undef;
153 2         9 ( my $pm = $class ) =~ s{::|'}{/}g;
154 2         5 $pm .= '.pm';
155              
156 2 50 66     10 $use->( 'exact::' . $class, 'exact/' . $pm, $caller, $params ) or
157             $use->( $class, $pm, $caller, $params ) or
158             croak(
159             "Can't locate exact/$pm or $pm in \@INC " .
160             "(you may need to install the exact::$class or $class module)" .
161             '(@INC contains: ' . join( ' ', @INC ) . ')'
162             );
163             }
164 18         36 $self->add_isa(@$_) for @late_parents;
165              
166             warnings->unimport('experimental')
167 18 50 33     329 unless ( $perl_version < 18 or grep { $_ eq 'noskipexperimentalwarnings' } @functions );
  2         43  
168              
169 18 100       109 namespace::autoclean->import( -cleanee => $caller ) unless ( grep { $_ eq 'noautoclean' } @functions );
  2         2214  
170             }
171              
172             sub monkey_patch {
173 41     41 1 154 my ( $self, $class, %patch ) = @_;
174             {
175 10     10   86 no strict 'refs';
  10         32  
  10         443  
  41         76  
176 10     10   74 no warnings 'redefine';
  10         21  
  10         1611  
177 41         346 *{"${class}::$_"} = set_subname( "${class}::$_", $patch{$_} ) for ( keys %patch );
  150         1036  
178             }
179 41         117 return;
180             }
181              
182             sub add_isa {
183 3     3 1 7723 my ( $self, $parent, $child ) = @_;
184             {
185 10     10   82 no strict 'refs';
  10         229  
  10         5931  
  3         4  
186 3 100       37 push( @{"${child}::ISA"}, $parent ) unless ( $child->isa($parent) );
  2         39  
187             }
188 3         10 return;
189             }
190              
191             sub no_parent {
192 0     0 1 0 $no_parent = 1;
193 0         0 return;
194             }
195              
196             sub late_parent {
197 0     0 1 0 $late_parent = 1;
198 0         0 return;
199             }
200              
201             sub _patch_import {
202 2     2   9 my ( $type, $self, @names ) = @_;
203              
204 2         24 my $target = ( caller(1) )[0];
205 2         29 my $original_import = $target->can('import');
206              
207 2         5 my %groups;
208 2 100       9 if ( $type eq 'provide' ) {
209 1         5 %groups = map { %$_ } grep { ref $_ eq 'HASH' } @names;
  1         9  
  3         8  
210 1         3 @names = grep { not ref $_ } @names;
  3         7  
211             }
212              
213             monkey_patch(
214             $self,
215             $target,
216             import => sub {
217 3     3   20 my ( $package, @exports ) = @_;
        3      
        3      
218              
219 3 100       10 $original_import->(@_) if ($original_import);
220              
221 3 100       18 if ( $type eq 'force' ) {
    50          
222 1         3 @exports = @names;
223             }
224             elsif ( $type eq 'provide' ) {
225 3         9 @exports = grep { defined } map {
226 2         4 my $name = $_;
  2         4  
227              
228 4         15 ( grep { $name eq $_ } @names ) ? $name :
229 2 50       4 ( exists $groups{$name} ) ? ( @{ $groups{$name} } ) : undef;
  1 100       3  
230             } @exports;
231             }
232              
233             monkey_patch(
234             $package,
235             ( caller(0) )[0],
236 3         27 map { $_ => \&{ $package . '::' . $_ } } @exports
  4         8  
  4         16  
237             );
238              
239 3         10 return;
240             },
241 2         14 );
242             }
243              
244             sub export {
245 1     1 1 111 _patch_import( 'force', @_ );
246 1         3 return;
247             }
248              
249             sub exportable {
250 1     1 1 7615 _patch_import( 'provide', @_ );
251 1         3 return;
252             }
253              
254             1;
255              
256             __END__