File Coverage

blib/lib/Const/Exporter.pm
Criterion Covered Total %
statement 125 128 97.6
branch 31 36 86.1
condition 17 24 70.8
subroutine 16 17 94.1
pod n/a
total 189 205 92.2


line stmt bran cond sub pod time code
1             package Const::Exporter;
2              
3             # ABSTRACT: Declare constants for export.
4              
5 9     9   896183 use v5.10.0;
  9         111  
6              
7 9     9   49 use strict;
  9         19  
  9         212  
8 9     9   99 use warnings;
  9         20  
  9         475  
9              
10             our $VERSION = 'v1.2.1';
11              
12 9     9   61 use Carp;
  9         27  
  9         636  
13 9     9   4007 use Const::Fast;
  9         23120  
  9         60  
14 9     9   794 use Exporter ();
  9         26  
  9         260  
15 9     9   53 use List::Util '1.56' => qw/ pairs mesh /;
  9         18  
  9         1826  
16 9     9   4265 use Package::Stash;
  9         67072  
  9         368  
17 9     9   4471 use Ref::Util qw/ is_blessed_ref is_arrayref is_coderef is_hashref is_ref /;
  9         14710  
  9         11785  
18              
19             # RECOMMEND PREREQ: List::SomeUtils::XS
20             # RECOMMEND PREREQ: Package::Stash::XS
21             # RECOMMEND PREREQ: Ref::Util::XS
22             # RECOMMEND PREREQ: Storable
23              
24             sub import {
25 15     15   1026 my $pkg = shift;
26              
27 15         170 strict->import;
28 15         207 warnings->import;
29              
30 15         64 my $caller = caller;
31 15         649 my $stash = Package::Stash->new($caller);
32              
33             # Create @EXPORT, @EXPORT_OK, %EXPORT_TAGS and import if they
34             # don't yet exist.
35              
36 15         257 my $export = $stash->get_or_add_symbol('@EXPORT');
37              
38 15         90 my $export_ok = $stash->get_or_add_symbol('@EXPORT_OK');
39              
40 15         104 my $export_tags = $stash->get_or_add_symbol('%EXPORT_TAGS');
41              
42 15 100       174 $stash->add_symbol( '&import', \&Exporter::import )
43             unless ( $stash->has_symbol('&import') );
44              
45 15         91 $stash->add_symbol( '&const', \&Const::Fast::const );
46 15         51 _export_symbol( $stash, 'const' );
47              
48 15         136 foreach my $set ( pairs @_ ) {
49              
50 12         101 my $tag = $set->key;
51 12 100       66 croak "'${tag}' is reserved" if $tag eq 'all';
52              
53 11         40 my $defs = $set->value;
54              
55 11 50       37 croak "An array reference required for tag '${tag}'"
56             unless is_arrayref($defs);
57              
58 11         21 while ( my $item = shift @{$defs} ) {
  41         113  
59              
60 30         51 for ($item) {
61              
62             # Array reference means a list of enumerated symbols
63              
64 30 100       66 if ( is_arrayref($_) ) {
65              
66 8         11 my @enums = @{$item};
  8         23  
67 8         13 my $start = shift @{$defs};
  8         14  
68              
69 8 100       20 my @values = is_arrayref($start) ? @{$start} : ($start);
  6         11  
70              
71 8   50     29 my $last = $values[0] // 0;
72 8     8   37 my $fn = sub { $_[0] + 1 };
  8         23  
73              
74 8 100       31 if ( is_coderef $values[1] ) {
75 1         3 $fn = $values[1];
76 1         1 $values[1] = undef;
77             }
78              
79 8         61 foreach my $pair ( pairs mesh \@enums, \@values ) {
80 27   66     95 my $value = $pair->value // $fn->($last);
81 27         126 $last = $value;
82 27   100     74 my $symbol = $pair->key // next;
83              
84 26         60 _add_symbol( $stash, $symbol, $value );
85 26         281 _export_symbol( $stash, $symbol, $tag );
86              
87             }
88              
89 8         85 next;
90             }
91              
92             # A scalar is a name of a symbol
93              
94 22 50       46 if ( !is_ref($_) ) {
95              
96 22         36 my $symbol = $item;
97 22         40 my $sigil = _get_sigil($symbol);
98 22 100       99 my $norm =
99             ( $sigil eq '&' ) ? ( $sigil . $symbol ) : $symbol;
100              
101             # If the symbol is already defined, that we add it
102             # to the exports for that tag and assume no value
103             # is given for it.
104              
105 22 100       128 if ( $stash->has_symbol($norm) ) {
106              
107 4         14 my $ref = $stash->get_symbol($norm);
108              
109             # In case symbol is defined as `our`
110             # beforehand, ensure it is readonly.
111              
112 4         26 Const::Fast::_make_readonly( $ref => 1 );
113              
114 4         65 _export_symbol( $stash, $symbol, $tag );
115              
116 4         11 next;
117              
118             }
119              
120 18         36 my $value = shift @{$defs};
  18         32  
121              
122 18         50 _add_symbol( $stash, $symbol, $value );
123 18         259 _export_symbol( $stash, $symbol, $tag );
124              
125 18         51 next;
126             }
127              
128 0         0 croak "$_ is not supported";
129              
130             }
131              
132             }
133              
134             }
135              
136             # Now ensure @EXPORT, @EXPORT_OK and %EXPORT_TAGS contain unique
137             # symbols. This may not matter to Exporter, but we want to ensure
138             # the values are 'clean'. It also simplifies testing.
139              
140 14 100       98 push @{$export}, @{ $export_tags->{default} } if $export_tags->{default};
  8         17  
  8         45  
141 14         46 _uniq($export);
142              
143 14         39 _uniq($export_ok);
144              
145 14   50     46 $export_tags->{all} //= [];
146 14         23 push @{ $export_tags->{all} }, @{$export_ok};
  14         31  
  14         48  
147              
148 14         36 _uniq( $export_tags->{$_} ) for keys %{$export_tags};
  14         58  
149              
150             }
151              
152             # Add a symbol to the stash
153              
154             sub _check_sigil_against_value {
155 18     18   117 my ($sigil, $value) = @_;
156              
157 18 100 66     66 return 1 if $sigil eq '@' && is_arrayref($value);
158 16 100 66     54 return 1 if $sigil eq '%' && is_hashref($value);
159 15 50 33     29 return 1 if $sigil eq '&' && is_coderef($value);
160 15 50       40 return 1 if $sigil eq '$';
161              
162 0         0 return 0;
163             }
164              
165             sub _add_symbol {
166 44     44   78 my ( $stash, $symbol, $value ) = @_;
167              
168 44         78 my $sigil = _get_sigil($symbol);
169 44 100       96 if ( $sigil ne '&' ) {
170              
171 19 100       41 if ( is_blessed_ref $value) {
172              
173 1         6 $stash->add_symbol( $symbol, \$value );
174 1         7 Const::Fast::_make_readonly( $stash->get_symbol($symbol) => 1 );
175              
176             }
177             else {
178              
179 18 50       34 croak "Invalid type for $symbol"
180             unless _check_sigil_against_value($sigil, $value);
181              
182 18         116 $stash->add_symbol( $symbol, $value );
183 18         90 Const::Fast::_make_readonly( $stash->get_symbol($symbol) => 1 );
184             }
185              
186             }
187             else {
188              
189 25 100       98 const my $copy => is_coderef($value) ? $value->() : $value;
190 25     0   1230 $stash->add_symbol( '&' . $symbol, sub() { $copy } );
  0         0  
191              
192             }
193             }
194              
195             # Add a symbol to @EXPORT_OK and %EXPORT_TAGS
196              
197             sub _export_symbol {
198 63     63   145 my ( $stash, $symbol, $tag ) = @_;
199              
200 63         224 my $export_ok = $stash->get_symbol('@EXPORT_OK');
201 63         215 my $export_tags = $stash->get_symbol('%EXPORT_TAGS');
202              
203 63   100     212 $tag //= 'all';
204              
205 63   100     198 $export_tags->{$tag} //= [];
206              
207 63         92 push @{ $export_tags->{$tag} }, $symbol;
  63         143  
208 63         91 push @{$export_ok}, $symbol;
  63         149  
209             }
210              
211             # Function to get the sigil from a symbol. If no sigil, it assumes
212             # that it is a function reference.
213              
214             sub _get_sigil {
215 66     66   105 my ($symbol) = @_;
216 66         193 my ($sigil) = $symbol =~ /^(\W)/;
217 66   100     212 return $sigil // '&';
218             }
219              
220             # Function to take a list reference and prune duplicate elements from
221             # it.
222              
223             sub _uniq {
224 56     56   100 my ($listref) = @_;
225 56         75 my %seen;
226 56         76 while ( my $item = shift @{$listref} ) {
  658         1174  
227 602         901 $seen{$item} = 1;
228             }
229 56         75 push @{$listref}, keys %seen;
  56         12087  
230             }
231              
232             1;
233              
234             __END__