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   729560 use v5.10.0;
  9         97  
6              
7 9     9   39 use strict;
  9         15  
  9         181  
8 9     9   40 use warnings;
  9         20  
  9         345  
9              
10             our $VERSION = 'v1.2.2';
11              
12 9     9   43 use Carp;
  9         29  
  9         441  
13 9     9   3456 use Const::Fast;
  9         18502  
  9         45  
14 9     9   602 use Exporter 5.57 ();
  9         134  
  9         262  
15 9     9   46 use List::Util '1.56' => qw/ pairs mesh /;
  9         15  
  9         1418  
16 9     9   3525 use Package::Stash;
  9         56523  
  9         282  
17 9     9   3451 use Ref::Util qw/ is_blessed_ref is_arrayref is_coderef is_hashref is_ref /;
  9         14901  
  9         9931  
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   717 my $pkg = shift;
26              
27 15         81 strict->import;
28 15         156 warnings->import;
29              
30 15         53 my $caller = caller;
31 15         440 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         221 my $export = $stash->get_or_add_symbol('@EXPORT');
37              
38 15         74 my $export_ok = $stash->get_or_add_symbol('@EXPORT_OK');
39              
40 15         87 my $export_tags = $stash->get_or_add_symbol('%EXPORT_TAGS');
41              
42 15 100       124 $stash->add_symbol( '&import', \&Exporter::import )
43             unless ( $stash->has_symbol('&import') );
44              
45 15         71 $stash->add_symbol( '&const', \&Const::Fast::const );
46 15         41 _export_symbol( $stash, 'const' );
47              
48 15         93 foreach my $set ( pairs @_ ) {
49              
50 12         77 my $tag = $set->key;
51 12 100       53 croak "'${tag}' is reserved" if $tag eq 'all';
52              
53 11         34 my $defs = $set->value;
54              
55 11 50       29 croak "An array reference required for tag '${tag}'"
56             unless is_arrayref($defs);
57              
58 11         18 while ( my $item = shift @{$defs} ) {
  41         83  
59              
60 30         49 for ($item) {
61              
62             # Array reference means a list of enumerated symbols
63              
64 30 100       50 if ( is_arrayref($_) ) {
65              
66 8         10 my @enums = @{$item};
  8         17  
67 8         11 my $start = shift @{$defs};
  8         10  
68              
69 8 100       17 my @values = is_arrayref($start) ? @{$start} : ($start);
  6         10  
70              
71 8   50     22 my $last = $values[0] // 0;
72 8     8   29 my $fn = sub { $_[0] + 1 };
  8         18  
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         49 foreach my $pair ( pairs mesh \@enums, \@values ) {
80 27   66     86 my $value = $pair->value // $fn->($last);
81 27         41 $last = $value;
82 27   100     62 my $symbol = $pair->key // next;
83              
84 26         48 _add_symbol( $stash, $symbol, $value );
85 26         222 _export_symbol( $stash, $symbol, $tag );
86              
87             }
88              
89 8         67 next;
90             }
91              
92             # A scalar is a name of a symbol
93              
94 22 50       39 if ( !is_ref($_) ) {
95              
96 22         24 my $symbol = $item;
97 22         35 my $sigil = _get_sigil($symbol);
98 22 100       74 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       99 if ( $stash->has_symbol($norm) ) {
106              
107 4         13 my $ref = $stash->get_symbol($norm);
108              
109             # In case symbol is defined as `our`
110             # beforehand, ensure it is readonly.
111              
112 4         11 Const::Fast::_make_readonly( $ref => 1 );
113              
114 4         51 _export_symbol( $stash, $symbol, $tag );
115              
116 4         9 next;
117              
118             }
119              
120 18         31 my $value = shift @{$defs};
  18         26  
121              
122 18         38 _add_symbol( $stash, $symbol, $value );
123 18         195 _export_symbol( $stash, $symbol, $tag );
124              
125 18         43 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       77 push @{$export}, @{ $export_tags->{default} } if $export_tags->{default};
  8         13  
  8         27  
141 14         35 _uniq($export);
142              
143 14         33 _uniq($export_ok);
144              
145 14   50     41 $export_tags->{all} //= [];
146 14         19 push @{ $export_tags->{all} }, @{$export_ok};
  14         21  
  14         41  
147              
148 14         28 _uniq( $export_tags->{$_} ) for keys %{$export_tags};
  14         45  
149              
150             }
151              
152             # Add a symbol to the stash
153              
154             sub _check_sigil_against_value {
155 18     18   25 my ($sigil, $value) = @_;
156              
157 18 100 66     36 return 1 if $sigil eq '@' && is_arrayref($value);
158 16 100 66     31 return 1 if $sigil eq '%' && is_hashref($value);
159 15 50 33     21 return 1 if $sigil eq '&' && is_coderef($value);
160 15 50       43 return 1 if $sigil eq '$';
161              
162 0         0 return 0;
163             }
164              
165             sub _add_symbol {
166 44     44   64 my ( $stash, $symbol, $value ) = @_;
167              
168 44         66 my $sigil = _get_sigil($symbol);
169 44 100       75 if ( $sigil ne '&' ) {
170              
171 19 100       32 if ( is_blessed_ref $value) {
172              
173 1         5 $stash->add_symbol( $symbol, \$value );
174 1         6 Const::Fast::_make_readonly( $stash->get_symbol($symbol) => 1 );
175              
176             }
177             else {
178              
179 18 50       27 croak "Invalid type for $symbol"
180             unless _check_sigil_against_value($sigil, $value);
181              
182 18         91 $stash->add_symbol( $symbol, $value );
183 18         63 Const::Fast::_make_readonly( $stash->get_symbol($symbol) => 1 );
184             }
185              
186             }
187             else {
188              
189 25 100       83 const my $copy => is_coderef($value) ? $value->() : $value;
190 25     0   977 $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   100 my ( $stash, $symbol, $tag ) = @_;
199              
200 63         183 my $export_ok = $stash->get_symbol('@EXPORT_OK');
201 63         164 my $export_tags = $stash->get_symbol('%EXPORT_TAGS');
202              
203 63   100     177 $tag //= 'all';
204              
205 63   100     158 $export_tags->{$tag} //= [];
206              
207 63         71 push @{ $export_tags->{$tag} }, $symbol;
  63         126  
208 63         73 push @{$export_ok}, $symbol;
  63         177  
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   85 my ($symbol) = @_;
216 66         152 my ($sigil) = $symbol =~ /^(\W)/;
217 66   100     168 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   86 my ($listref) = @_;
225 56         59 my %seen;
226 56         61 while ( my $item = shift @{$listref} ) {
  658         904  
227 602         828 $seen{$item} = 1;
228             }
229 56         64 push @{$listref}, keys %seen;
  56         9458  
230             }
231              
232             1;
233              
234             __END__