File Coverage

blib/lib/exact.pm
Criterion Covered Total %
statement 183 194 94.3
branch 50 74 67.5
condition 17 36 47.2
subroutine 35 37 94.5
pod 8 8 100.0
total 293 349 83.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 13     13   2771149 use 5.014;
  13         53  
5 13     13   78 use strict;
  13         28  
  13         413  
6 13     13   61 use warnings;
  13         25  
  13         676  
7 13     13   6708 use namespace::autoclean;
  13         283299  
  13         86  
8 13     13   1026 use B::Deparse;
  13         29  
  13         584  
9 13     13   6765 use Import::Into;
  13         8378  
  13         516  
10 13     13   133 use Sub::Util 'set_subname';
  13         34  
  13         978  
11 13     13   6582 use Syntax::Keyword::Defer;
  13         35177  
  13         103  
12 13     13   8432 use Syntax::Keyword::Try;
  13         20220  
  13         73  
13              
14             our $VERSION = '1.32'; # VERSION
15              
16 13     13   1566 use feature ();
  13         25  
  13         264  
17 13     13   738 use utf8 ();
  13         433  
  13         311  
18 13     13   600 use mro ();
  13         850  
  13         370  
19 13     13   54 use Carp qw( croak carp confess cluck );
  13         24  
  13         868  
20 13     13   7071 use IO::File ();
  13         166883  
  13         559  
21 13     13   135 use IO::Handle ();
  13         45  
  13         330  
22 13     13   80 use Try::Tiny ();
  13         49  
  13         303  
23 13     13   7696 use PerlX::Maybe ();
  13         46433  
  13         21654  
24              
25             my ($perl_version) = $^V =~ /^v5\.(\d+)/;
26              
27             my $features_available = ( %feature::feature_bundle and $feature::feature_bundle{all} )
28             ? $feature::feature_bundle{all}
29             : [ qw( say state switch unicode_strings ) ];
30              
31             my $functions_available = [ qw(
32             nostrict nowarnings
33             nofeatures nobundle noskipexperimentalwarnings
34             noutf8 noc3 nocarp notry trytiny nodefer nomaybe noautoclean
35             ) ];
36              
37             my $functions_deprecated = ['noexperiments'];
38              
39             my ( $no_parent, $late_parent );
40              
41             sub import {
42 20     20   3900 my ( $self, $caller ) = ( shift, caller() );
43              
44 20         61 my ( @features, @nofeatures, @functions, @bundles, @classes );
45 20         51 for (@_) {
46 7         22 ( my $opt = $_ ) =~ s/^\-//;
47              
48 7 50 66     46 if ( $opt eq 'class' ) {
    50          
    50          
    100          
    100          
    100          
    100          
49 0         0 push( @classes, $opt );
50             }
51             elsif ( $opt eq 'cor' ) {
52 0         0 push( @features, 'class' );
53             }
54             elsif ( $opt eq 'nocor' ) {
55 0         0 push( @nofeatures, 'class' );
56             }
57 182         400 elsif ( grep { $_ eq $opt } @$features_available ) {
58 1         3 push( @features, $opt );
59             }
60 156         385 elsif ( my ($nofeature) = grep { 'no' . $_ eq $opt } @$features_available ) {
61 1         4 push( @nofeatures, $nofeature );
62             }
63 70         195 elsif ( grep { $_ eq $opt } @$functions_available, @$functions_deprecated ) {
64 2 50       6 push( @functions, $opt ) if ( grep { $_ eq $opt } @$functions_available );
  26         50  
65             }
66             elsif ( $opt =~ /^:?v?5?\.?(\d+)/ and $1 >= 10 ) {
67 1         5 push( @bundles, $1 );
68             }
69             else {
70 2 50       10 push( @classes, $opt ) if ( $opt !~ /^no[a-z]{2}/ );
71             }
72             }
73              
74 20 50       196 strict ->import unless ( grep { $_ eq 'nostrict' } @functions );
  2         50  
75 20 50       454 warnings->import unless ( grep { $_ eq 'nowarnings' } @functions );
  2         73  
76              
77 20 100 66     120 if (@bundles) {
    100          
78 1         158 feature->import( ':5.' . $_ ) for (@bundles);
79             }
80             elsif (
81             not grep { $_ eq 'nofeatures' } @functions and
82             not grep { $_ eq 'nobundle' } @functions
83             ) {
84 18 50       2771 feature->import( $perl_version >= 16 ? ':all' : ':5.' . $perl_version );
85             }
86 20         305 feature->import($_) for (@features);
87 20         104 feature->unimport($_) for (@nofeatures);
88              
89 20 50       79 unless ( grep { $_ eq 'noutf8' } @functions ) {
  2         11  
90 20         164 utf8->import;
91 20         242 binmode( $_, ':utf8' ) for ( *STDIN, *STDERR, *STDOUT );
92 20         128 'open'->import::into( $caller, ':std', ':utf8' );
93             }
94              
95 20 50       37826 mro::set_mro( $caller, 'c3' ) unless ( grep { $_ eq 'noc3' } @functions );
  2         23  
96              
97             monkey_patch( $self, $caller,
98 80         96 ( map { $_ => \&{ 'Carp::' . $_ } } qw( croak carp confess cluck ) ),
  80         237  
99 40         55 ( map { $_ => \&{$_} } qw( deat deattry ) ),
  40         135  
100 20 50       63 ) unless ( grep { $_ eq 'nocarp' } @functions );
  2         10  
101              
102             feature->unimport('try') if (
103 520         905 grep { $_ eq 'try' } @$features_available and
104             (
105             grep { $_ eq 'notry' } @functions or
106 20 50 33     61 grep { $_ eq 'trytiny' } @functions
      33        
107             )
108             );
109             Syntax::Keyword::Try->import_into($caller) if (
110             $perl_version < 36 and
111 0         0 not grep { $_ eq 'notry' } @functions and
112 20 0 33     73 not grep { $_ eq 'trytiny' } @functions
  0   33     0  
113             );
114 20 50       83 Try::Tiny->import::into($caller) if ( grep { $_ eq 'trytiny' } @functions );
  2         9  
115              
116             Syntax::Keyword::Defer->import_into($caller) if (
117             $perl_version < 36 and
118 20 50 33     62 not grep { $_ eq 'nodefer' } @functions
  0         0  
119             );
120              
121 80         116 monkey_patch( $self, $caller, ( map { $_ => \&{ 'PerlX::Maybe::' . $_ } } qw(
  80         267  
122             maybe provided provided_deref provided_deref_with_maybe
123 20 50       60 ) ) ) unless ( grep { $_ eq 'nomaybe' } @functions );
  2         8  
124              
125 20         42 my @late_parents = ();
126             my $use = sub {
127 3     3   11 my ( $class, $pm, $caller, $params ) = @_;
128              
129 3         4 my $failed_require;
130             try {
131             require "$pm" unless ( do {
132 13     13   123 no strict 'refs';
  13         21  
  13         695  
133 13     13   89 no warnings 'once';
  13         21  
  13         3352  
134             ${"${caller}::INC"}{$pm};
135             } );
136             }
137 3         8 catch ($e) {
138             croak($e) unless ( index( $e, qq{Can't locate $pm in } ) == 0 );
139             return 0;
140             }
141              
142 2         5 ( $no_parent, $late_parent ) = ( undef, undef );
143              
144 2         4 my $is_exact_extension = 0;
145             {
146 13     13   101 no strict 'refs';
  13         23  
  13         11894  
  2         4  
147 2         5 $is_exact_extension = grep { index( $_, 'exact::' ) == 0 } $class, @{"${class}::ISA"};
  2         7  
  2         12  
148             }
149 2 100 66     17 $class->import( $params, $caller ) if ( $is_exact_extension and $class->can('import') );
150              
151 2 50 66     30 if ($late_parent) {
    100          
152 0         0 push( @late_parents, [ $class, $caller ] );
153             }
154             elsif ( not $no_parent and index( $class, 'exact::' ) != 0 ) {
155 1         4 $self->add_isa( $class, $caller );
156             }
157              
158 2         10 return 1;
159 20         116 };
160 20         64 for my $class (@classes) {
161 2 50       12 my $params = ( $class =~ s/\(([^\)]+)\)// ) ? $1 : undef;
162 2         10 ( my $pm = $class ) =~ s{::|'}{/}g;
163 2         5 $pm .= '.pm';
164              
165 2 50 66     8 $use->( 'exact::' . $class, 'exact/' . $pm, $caller, $params ) or
166             $use->( $class, $pm, $caller, $params ) or
167             croak(
168             "Can't locate exact/$pm or $pm in \@INC " .
169             "(you may need to install the exact::$class or $class module)" .
170             '(@INC contains: ' . join( ' ', @INC ) . ')'
171             );
172             }
173 20         40 $self->add_isa(@$_) for @late_parents;
174              
175             warnings->unimport('experimental')
176 20 50 33     541 unless ( $perl_version < 18 or grep { $_ eq 'noskipexperimentalwarnings' } @functions );
  2         68  
177              
178 20 100       161 namespace::autoclean->import( -cleanee => $caller ) unless ( grep { $_ eq 'noautoclean' } @functions );
  2         2705  
179             }
180              
181             sub monkey_patch {
182 45     45 1 190 my ( $self, $class, %patch ) = @_;
183             {
184 13     13   133 no strict 'refs';
  13         25  
  13         628  
  45         65  
185 13     13   66 no warnings 'redefine';
  13         20  
  13         3207  
186 45         459 *{"${class}::$_"} = set_subname( "${class}::$_", $patch{$_} ) for ( keys %patch );
  206         1389  
187             }
188 45         150 return;
189             }
190              
191             sub add_isa {
192 3     3 1 276041 my ( $self, $parent, $child ) = @_;
193             {
194 13     13   89 no strict 'refs';
  13         26  
  13         17514  
  3         4  
195 3 100       28 push( @{"${child}::ISA"}, $parent ) unless ( $child->isa($parent) );
  2         20  
196             }
197 3         8 return;
198             }
199              
200             sub no_parent {
201 0     0 1 0 $no_parent = 1;
202 0         0 return;
203             }
204              
205             sub late_parent {
206 0     0 1 0 $late_parent = 1;
207 0         0 return;
208             }
209              
210             sub _patch_import {
211 2     2   13 my ( $type, $self, @names ) = @_;
212              
213 2         59 my $target = ( caller(1) )[0];
214 2         36 my $original_import = $target->can('import');
215              
216 2         6 my %groups;
217 2 100       11 if ( $type eq 'provide' ) {
218 1         5 %groups = map { %$_ } grep { ref $_ eq 'HASH' } @names;
  1         5  
  3         8  
219 1         3 @names = grep { not ref $_ } @names;
  3         8  
220             }
221              
222             monkey_patch(
223             $self,
224             $target,
225             import => sub {
226 3     3   26 my ( $package, @exports ) = @_;
        3      
        3      
227              
228 3 50 33     31 if ( $original_import and ref $original_import eq 'CODE' ) {
229 3         3194 ( my $b_deparsed_sub = B::Deparse->new->coderef2text($original_import) ) =~ s/;//g;
230 3 100       85 $original_import->(@_) if ($b_deparsed_sub);
231             }
232              
233 3 100       23 if ( $type eq 'force' ) {
    50          
234 1         5 @exports = @names;
235             }
236             elsif ( $type eq 'provide' ) {
237 3         9 @exports = grep { defined } map {
238 2         7 my $name = $_;
  2         6  
239              
240 4         21 ( grep { $name eq $_ } @names ) ? $name :
241 2 50       6 ( exists $groups{$name} ) ? ( @{ $groups{$name} } ) : undef;
  1 100       5  
242             } @exports;
243             }
244              
245             monkey_patch(
246             $package,
247             ( caller(0) )[0],
248 3         67 map { $_ => \&{ $package . '::' . $_ } } @exports
  4         10  
  4         64  
249             );
250              
251 3         22 return;
252             },
253 2         25 );
254             }
255              
256             sub export {
257 1     1 1 318019 _patch_import( 'force', @_ );
258 1         3 return;
259             }
260              
261             sub exportable {
262 1     1 1 13690 _patch_import( 'provide', @_ );
263 1         2 return;
264             }
265              
266             sub deat ($) {
267 2     2 1 321097 ( my $e = reverse $_[0] ) =~ s/^\s*\.\d+\s+enil\s+.*?\s+ta\s+//;
268 2         19 return '' . reverse $e;
269             }
270              
271             sub deattry (&) {
272             try {
273             return $_[0]->();
274             }
275 2     2 1 1149 catch ($e) {
276             die deat $e, "\n";
277             }
278             }
279              
280             1;
281              
282             __END__