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