File Coverage

blib/lib/Exporter/Almighty.pm
Criterion Covered Total %
statement 236 237 99.5
branch 35 40 87.5
condition 22 32 68.7
subroutine 39 39 100.0
pod 0 14 0.0
total 332 362 91.7


line stmt bran cond sub pod time code
1 4     4   899446 use 5.012;
  4         33  
2 4     4   25 use strict;
  4         9  
  4         76  
3 4     4   18 use warnings;
  4         8  
  4         261  
4              
5             package Exporter::Almighty;
6              
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.001004';
9              
10 4     4   1771 use parent qw( Exporter::Tiny );
  4         1172  
  4         21  
11              
12             my @builtins;
13 4     4   30445 BEGIN { @builtins = qw( is_bool created_as_string created_as_number ) };
14 4     4   2524 use if $] lt '5.036000', 'builtins::compat' => @builtins;
  4         69  
  4         30  
15 4     4   74670 use if $] ge '5.036000', 'builtin' => @builtins;
  4         9  
  4         29  
16 4     4   165 no if $] ge '5.036000', 'warnings' => qw( experimental::builtin );
  4         10  
  4         17  
17              
18 4     4   192 use B qw( perlstring );
  4         9  
  4         218  
19 4     4   28 use Carp qw( croak );
  4         10  
  4         187  
20 4     4   1935 use Eval::TypeTiny qw( eval_closure set_subname );
  4         10151  
  4         27  
21 4     4   2154 use Exporter::Tiny qw( mkopt );
  4         11  
  4         16  
22 4     4   2673 use Import::Into;
  4         1746  
  4         135  
23 4     4   26 use Module::Runtime qw( require_module module_notional_filename );
  4         10  
  4         25  
24 4     4   2078 use Type::Registry qw();
  4         76338  
  4         159  
25 4         45 use Types::Common qw(
26             -sigs
27             -types
28             assert_Ref is_Ref
29             assert_ArrayRef is_ArrayRef
30             assert_HashRef is_HashRef
31             is_NonEmptySimpleStr
32 4     4   1839 );
  4         916756  
33              
34             sub _exporter_validate_opts {
35 2     2   319 my ( $me, $options ) = @_;
36 2         6 my $into = $options->{into};
37 2         19 my $setup = $options->{setup};
38 2         18 strict->import::into( $into );
39 2         472 warnings->import::into( $into );
40 2         351 $me->setup_for( $into, $setup );
41             }
42              
43             # Subclasses may wish to provide a subclass of Exporter::Tiny here.
44             sub base_exporter {
45 8     8 0 15440 return 'Exporter::Tiny';
46             }
47              
48             sub standard_package_variables {
49 30     30 0 14037 my ( $me, $into ) = @_;
50 4     4   66573 no strict 'refs';
  4         12  
  4         2903  
51             return (
52 30         212 \@{"$into\::ISA"},
53 30         117 \@{"$into\::EXPORT"},
54 30         108 \@{"$into\::EXPORT_OK"},
55 30         48 \%{"$into\::EXPORT_TAGS"},
  30         140  
56             );
57             }
58              
59             signature_for setup_for => (
60             method => 1,
61             head => [ NonEmptySimpleStr ],
62             named => [
63             tag => Optional[HashRef],
64             also => Optional[ArrayRef],
65             enum => Optional[HashRef[ArrayRef]],
66             class => Optional[ArrayRef],
67             role => Optional[ArrayRef],
68             duck => Optional[HashRef[ArrayRef]],
69             type => Optional[ArrayRef],
70             const => Optional[HashRef],
71             ],
72             );
73              
74             sub setup_for {
75             my ( $me, $into, $setup ) = @_;
76             $INC{ module_notional_filename($into) } //= __FILE__;
77             my @steps = $me->steps( $into, $setup );
78             for my $step ( @steps ) {
79             $me->$step( $into, $setup );
80             }
81             return;
82             }
83              
84             # Subclasses can wrap this to easily add and remove steps.
85             sub steps {
86 16     16 0 171356 my ( $me, $into, $setup ) = @_;
87 16         30 my @steps;
88 16         31 push @steps, 'setup_exporter_for';
89 16 100       70 push @steps, 'setup_reexports_for' if $setup->{also};
90 16 100       49 push @steps, 'setup_enums_for' if $setup->{enum};
91 16 100       43 push @steps, 'setup_classes_for' if $setup->{class};
92 16 100       42 push @steps, 'setup_roles_for' if $setup->{role};
93 16 100       39 push @steps, 'setup_ducks_for' if $setup->{duck};
94 16 100       36 push @steps, 'setup_types_for' if $setup->{type};
95 16 100       43 push @steps, 'setup_constants_for' if $setup->{const};
96 16         31 push @steps, 'setup_readonly_vars_for';
97 16         28 push @steps, 'finalize_export_variables_for';
98 16         61 return @steps;
99             }
100              
101             sub setup_exporter_for {
102 7     7 0 34776 my ( $me, $into, $setup ) = @_;
103            
104 7         28 my ( $into_ISA, undef, undef, $into_EXPORT_TAGS ) =
105             $me->standard_package_variables( $into );
106            
107             # Set up @ISA in caller package.
108 7         29 my $base = $me->base_exporter( $into, $setup );
109 7 50       146 push @$into_ISA, $base unless $into->isa( $base );
110            
111             # Set up %EXPORT_TAGS in caller package.
112 7   100     26 my %tags = %{ $setup->{tag} // {} };
  7         49  
113 7         27 for my $tag_name ( keys %tags ) {
114 7         15 my @exports = @{ assert_ArrayRef $tags{$tag_name} };
  7         30  
115 7         59 $tag_name =~ s/^[-:]//;
116 7   50     15 push @{ $into_EXPORT_TAGS->{$tag_name} //= [] }, @exports;
  7         47  
117             }
118            
119 7         24 return;
120             }
121              
122             sub setup_reexports_for {
123 2     2 0 7487 my ( $me, $into, $setup ) = @_;
124            
125 2         27 my $next = $into->can( '_exporter_validate_opts' );
126            
127 2         14 my $optlist = mkopt( $setup->{also} );
128 2         64 require_module( $_->[0] ) for @$optlist;
129            
130 2         1654 my $method_name = "$into\::_exporter_validate_opts";
131             my $method_code = sub {
132 2     2   1760 my ( $class, $opts ) = @_;
        2      
133 2 50       16 is_NonEmptySimpleStr( my $caller = $opts->{into} ) or return;
134 2         92 for my $also ( @$optlist ) {
135 4         400 my ( $module, $args ) = @$also;
136 4   100     11 $module->import::into( $caller, @{ $args // [] } );
  4         40  
137             }
138 2 100       677 goto $next if $next;
139 2         16 };
140 4     4   35 no strict 'refs';
  4         10  
  4         5563  
141 2         12 *$method_name = set_subname $method_name => $method_code;
142             }
143              
144             sub setup_enums_for {
145 2     2 0 5916 my ( $me, $into, $setup ) = @_;
146            
147 2         1083 require Type::Tiny::Enum;
148 2         7560 my $reg = Type::Registry->for_class( $into );
149 2         49 $me->_ensure_isa_type_library( $into );
150            
151 2   50     8 my %tags = %{ assert_HashRef $setup->{enum} // {} };
  2         40  
152 2         26 for my $tag_name ( keys %tags ) {
153 2         5 my $values = $tags{$tag_name};
154 2         10 $tag_name =~ s/^[-:]//;
155 2         6 my $type_name = $tag_name;
156 2         6 $tag_name = lc $tag_name;
157            
158 2         19 Type::Tiny::Enum->import( { into => $into }, $type_name, $values );
159 2         37684 $into->add_type( $reg->lookup( $type_name ) );
160             }
161            
162 2         2131 return;
163             }
164              
165             sub setup_classes_for {
166 1     1 0 9271 my ( $me, $into, $setup ) = @_;
167 1         561 require Type::Tiny::Class;
168 1         3603 $me->_setup_classes_or_roles_for( $into, $setup, 'class', 'Type::Tiny::Class' );
169             }
170              
171             sub setup_roles_for {
172 1     1 0 13672 my ( $me, $into, $setup ) = @_;
173 1         484 require Type::Tiny::Role;
174 1         1226 $me->_setup_classes_or_roles_for( $into, $setup, 'role', 'Type::Tiny::Role' );
175             }
176              
177             sub _setup_classes_or_roles_for {
178 2     2   12 my ( $me, $into, $setup, $kind, $tt_class ) = @_;
179            
180 2         15 my $reg = Type::Registry->for_class( $into );
181 2         38 $me->_ensure_isa_type_library( $into );
182            
183 2         21 my $optlist = mkopt( $setup->{$kind} );
184 2         45 for my $dfn ( @$optlist ) {
185 2   100     25 ( my $pkg_name = ( $dfn->[1] //= {} )->{$kind} // $dfn->[0] );
      33        
186 2   50     19 ( my $type_name = ( $dfn->[1] //= {} )->{name} // $dfn->[0] ) =~ s/:://g;
      66        
187 2         26 $tt_class->import( { into => $into }, @$dfn );
188 2         3999 $into->add_type( $reg->lookup( $type_name ) );
189 2         1034 eval { require_module( $pkg_name ) };
  2         11  
190             }
191            
192 2         14949 return;
193             }
194              
195             sub setup_ducks_for {
196 1     1 0 11978 my ( $me, $into, $setup ) = @_;
197            
198 1         575 require Type::Tiny::Duck;
199 1         1717 my $reg = Type::Registry->for_class( $into );
200 1         53 $me->_ensure_isa_type_library( $into );
201            
202 1   50     4 my %types = %{ assert_HashRef $setup->{duck} // {} };
  1         11  
203 1         12 for my $type_name ( keys %types ) {
204 1         3 my $values = $types{$type_name};
205 1         15 Type::Tiny::Duck->import( { into => $into }, $type_name, $values );
206 1         1921 $into->add_type( $reg->lookup( $type_name ) );
207             }
208            
209 1         570 return;
210             }
211              
212             sub setup_types_for {
213 2     2 0 17753 my ( $me, $into, $setup ) = @_;
214            
215 2         20 my $reg = Type::Registry->for_class( $into );
216 2         39 $me->_ensure_isa_type_library( $into );
217            
218 2         16 my $optlist = mkopt( $setup->{type} );
219 2         46 my @extends = ();
220 2         5 for my $dfn ( @$optlist ) {
221 2         6 my ( $lib, $list ) = @$dfn;
222 2         4 eval { require_module( $lib ) };
  2         10  
223 2 100       81 if ( is_ArrayRef $list ) {
224 1         5 for my $type_name ( @$list ) {
225 1         9 $into->add_type( $lib->get_type( $type_name ) );
226             }
227             }
228             else {
229 1         4 push @extends, $lib;
230             }
231             }
232            
233 2 100       529 if ( @extends ) {
234 1         10 require Type::Utils;
235 1         104 my $wrapper = eval "sub { package $into; &Type::Utils::extends; }";
236 1         24 $wrapper->( @extends );
237             }
238            
239 2         6535 return;
240             }
241              
242             sub _ensure_isa_type_library {
243 7     7   26 my ( $me, $into ) = @_;
244 7 50       107 return if $into->isa( 'Type::Library' );
245 7         27 my ( $old_isa ) = $me->standard_package_variables( $into );
246 7         22 my $new_isa = [];
247 7         19 my $saw_exporter_tiny = 0;
248 7         20 for my $pkg ( @$old_isa ) {
249 1 50       8 if ( $pkg eq 'Exporter::Tiny' ) {
250 1         3 push @$new_isa, 'Type::Library';
251 1         3 $saw_exporter_tiny++;
252             }
253             else {
254 0         0 push @$new_isa, $pkg;
255             }
256             }
257 7 100       25 push @$new_isa, 'Type::Library' unless $saw_exporter_tiny;
258 7         153 @$old_isa = @$new_isa;
259             }
260              
261             sub setup_constants_for {
262 3     3 0 5725 my ( $me, $into, $setup ) = @_;
263            
264 3         15 my ( $into_ISA, undef, undef, $into_EXPORT_TAGS ) =
265             $me->standard_package_variables( $into );
266              
267 3   50     9 my %tags = %{ assert_HashRef $setup->{const} // {} };
  3         27  
268 3         36 for my $tag_name ( keys %tags ) {
269 4         25 my %exports = %{ assert_HashRef $tags{$tag_name} };
  4         13  
270 4         42 $tag_name =~ s/^[-:]//;
271 4         24 my @constant_names = sort keys %exports;
272 4   50     9 push @{ $into_EXPORT_TAGS->{$tag_name} //= [] }, @constant_names;
  4         33  
273 4   100     11 push @{ $into_EXPORT_TAGS->{'constants'} //= [] }, @constant_names;
  4         21  
274 4         19 $me->make_constant_subs( $into, \%exports );
275             }
276            
277 3         58 return;
278             }
279              
280             sub make_constant_subs {
281 5     5 0 10207 my ( $me, $into, $constants ) = @_;
282            
283 5         18 for my $key ( keys %$constants ) {
284 17         246 my $value = $constants->{$key};
285 17         49 my $full_name = "$into\::$key";
286            
287 17         29 my $coderef;
288 17 100       63 if ( is_Ref $value ) {
289 2         23 $coderef = eval_closure(
290             source => 'sub () { $value }',
291             environment => { '$value' => \$value },
292             );
293             }
294             else {
295 15 100       56 $coderef = eval sprintf(
    100          
296             'sub () { %s %s }',
297             is_bool( $value ) ? '!!' : ( created_as_number( $value ) ? '0+' : '' ),
298             perlstring( $value ),
299             );
300             }
301            
302 4     4   37 no strict 'refs';
  4         9  
  4         726  
303 17         561 *$full_name = set_subname $full_name => $coderef;
304             }
305             }
306              
307             sub setup_readonly_vars_for {
308 8     8 0 15083 my ( $me, $into, $setup ) = @_;
309            
310 8         29 my ( $into_ISA, $into_EXPORT, $into_EXPORT_OK, $into_EXPORT_TAGS ) =
311             $me->standard_package_variables( $into );
312            
313 8   100     19 my @constants = @{ $into_EXPORT_TAGS->{'constants'} // [] };
  8         47  
314 8         24 for my $name ( @constants ) {
315 4     4   31 no strict 'refs';
  4         25  
  4         1466  
316 9         56 my $full_name = "$into\::$name";
317 9         14 ${ $full_name } = &{ $full_name }();
  9         22  
  9         31  
318 9         16 Internals::SvREADONLY( ${ $full_name }, 1 );
  9         34  
319 9   100     12 push @{ $into_EXPORT_TAGS->{'ro_vars'} //= [] }, '$' . $name;
  9         51  
320             }
321            
322 8         23 return;
323             }
324              
325             sub finalize_export_variables_for {
326 3     3 0 7599 my ( $me, $into, $setup ) = @_;
327            
328 3         16 my ( $into_ISA, $into_EXPORT, $into_EXPORT_OK, $into_EXPORT_TAGS ) =
329             $me->standard_package_variables( $into );
330            
331 3         9 my %all_exports;
332 3   50     7 for my $list ( $into_EXPORT, $into_EXPORT_OK, values %{ $into_EXPORT_TAGS // {} } ) {
  3         19  
333 22 50       53 is_ArrayRef $list or next;
334 22         70 $all_exports{$_}++ for @$list;
335             }
336 3         26 @{ $into_EXPORT_OK } = sort keys %all_exports;
  3         10  
337            
338 3         8 my %default_exports;
339 3         11 for my $list ( $into_EXPORT, $into_EXPORT_TAGS->{default} ) {
340 6 100       55 is_ArrayRef $list or next;
341 4         17 $default_exports{$_}++ for @$list;
342             }
343 3         10 @{ $into_EXPORT } = sort keys %default_exports;
  3         26  
344            
345 3         14 return;
346             }
347              
348             1;
349              
350             __END__