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   917075 use 5.012;
  4         37  
2 4     4   22 use strict;
  4         10  
  4         89  
3 4     4   17 use warnings;
  4         8  
  4         283  
4              
5             package Exporter::Almighty;
6              
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.001005';
9              
10 4     4   1845 use parent qw( Exporter::Tiny );
  4         1202  
  4         23  
11              
12             my @builtins;
13 4     4   34699 BEGIN { @builtins = qw( is_bool created_as_string created_as_number ) };
14 4     4   2659 use if $] lt '5.036000', 'builtins::compat' => @builtins;
  4         55  
  4         25  
15 4     4   74721 use if $] ge '5.036000', 'builtin' => @builtins;
  4         11  
  4         41  
16 4     4   189 no if $] ge '5.036000', 'warnings' => qw( experimental::builtin );
  4         8  
  4         21  
17              
18 4     4   156 use B qw( perlstring );
  4         12  
  4         211  
19 4     4   23 use Carp qw( croak );
  4         10  
  4         201  
20 4     4   2008 use Eval::TypeTiny qw( eval_closure set_subname );
  4         10308  
  4         26  
21 4     4   2251 use Exporter::Tiny qw( mkopt );
  4         9  
  4         16  
22 4     4   2685 use Import::Into;
  4         1821  
  4         130  
23 4     4   42 use Module::Runtime qw( require_module module_notional_filename );
  4         14  
  4         20  
24 4     4   2126 use Type::Registry qw();
  4         76384  
  4         155  
25 4         48 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   1842 );
  4         926107  
33              
34             sub _exporter_validate_opts {
35 2     2   307 my ( $me, $options ) = @_;
36 2         7 my $into = $options->{into};
37 2         21 my $setup = $options->{setup};
38 2         16 strict->import::into( $into );
39 2         579 warnings->import::into( $into );
40 2         407 $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 12827 return 'Exporter::Tiny';
46             }
47              
48             sub standard_package_variables {
49 30     30 0 10883 my ( $me, $into ) = @_;
50 4     4   69002 no strict 'refs';
  4         13  
  4         3221  
51             return (
52 30         199 \@{"$into\::ISA"},
53 30         118 \@{"$into\::EXPORT"},
54 30         146 \@{"$into\::EXPORT_OK"},
55 30         46 \%{"$into\::EXPORT_TAGS"},
  30         145  
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 56846 my ( $me, $into, $setup ) = @_;
87 16         29 my @steps;
88 16         29 push @steps, 'setup_exporter_for';
89 16 100       70 push @steps, 'setup_reexports_for' if $setup->{also};
90 16 100       44 push @steps, 'setup_enums_for' if $setup->{enum};
91 16 100       41 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       38 push @steps, 'setup_types_for' if $setup->{type};
95 16 100       42 push @steps, 'setup_constants_for' if $setup->{const};
96 16         26 push @steps, 'setup_readonly_vars_for';
97 16         30 push @steps, 'finalize_export_variables_for';
98 16         60 return @steps;
99             }
100              
101             sub setup_exporter_for {
102 7     7 0 35700 my ( $me, $into, $setup ) = @_;
103            
104 7         27 my ( $into_ISA, undef, undef, $into_EXPORT_TAGS ) =
105             $me->standard_package_variables( $into );
106            
107             # Set up @ISA in caller package.
108 7         34 my $base = $me->base_exporter( $into, $setup );
109 7 50       145 push @$into_ISA, $base unless $into->isa( $base );
110            
111             # Set up %EXPORT_TAGS in caller package.
112 7   100     33 my %tags = %{ $setup->{tag} // {} };
  7         51  
113 7         24 for my $tag_name ( keys %tags ) {
114 7         17 my @exports = @{ assert_ArrayRef $tags{$tag_name} };
  7         32  
115 7         61 $tag_name =~ s/^[-:]//;
116 7   50     14 push @{ $into_EXPORT_TAGS->{$tag_name} //= [] }, @exports;
  7         45  
117             }
118            
119 7         25 return;
120             }
121              
122             sub setup_reexports_for {
123 2     2 0 10365 my ( $me, $into, $setup ) = @_;
124            
125 2         26 my $next = $into->can( '_exporter_validate_opts' );
126            
127 2         9 my $optlist = mkopt( $setup->{also} );
128 2         73 require_module( $_->[0] ) for @$optlist;
129            
130 2         1522 my $method_name = "$into\::_exporter_validate_opts";
131             my $method_code = sub {
132 2     2   1812 my ( $class, $opts ) = @_;
        2      
133 2 50       20 is_NonEmptySimpleStr( my $caller = $opts->{into} ) or return;
134 2         28 for my $also ( @$optlist ) {
135 4         402 my ( $module, $args ) = @$also;
136 4   100     9 $module->import::into( $caller, @{ $args // [] } );
  4         36  
137             }
138 2 100       642 goto $next if $next;
139 2         21 };
140 4     4   44 no strict 'refs';
  4         12  
  4         5599  
141 2         9 *$method_name = set_subname $method_name => $method_code;
142             }
143              
144             sub setup_enums_for {
145 2     2 0 9611 my ( $me, $into, $setup ) = @_;
146            
147 2         1088 require Type::Tiny::Enum;
148 2         7997 my $reg = Type::Registry->for_class( $into );
149 2         45 $me->_ensure_isa_type_library( $into );
150            
151 2   50     25 my %tags = %{ assert_HashRef $setup->{enum} // {} };
  2         24  
152 2         25 for my $tag_name ( keys %tags ) {
153 2         5 my $values = $tags{$tag_name};
154 2         8 $tag_name =~ s/^[-:]//;
155 2         5 my $type_name = $tag_name;
156 2         6 $tag_name = lc $tag_name;
157            
158 2         24 Type::Tiny::Enum->import( { into => $into }, $type_name, $values );
159 2         38432 $into->add_type( $reg->lookup( $type_name ) );
160             }
161            
162 2         2291 return;
163             }
164              
165             sub setup_classes_for {
166 1     1 0 19803 my ( $me, $into, $setup ) = @_;
167 1         610 require Type::Tiny::Class;
168 1         2706 $me->_setup_classes_or_roles_for( $into, $setup, 'class', 'Type::Tiny::Class' );
169             }
170              
171             sub setup_roles_for {
172 1     1 0 7622 my ( $me, $into, $setup ) = @_;
173 1         651 require Type::Tiny::Role;
174 1         2799 $me->_setup_classes_or_roles_for( $into, $setup, 'role', 'Type::Tiny::Role' );
175             }
176              
177             sub _setup_classes_or_roles_for {
178 2     2   11 my ( $me, $into, $setup, $kind, $tt_class ) = @_;
179            
180 2         15 my $reg = Type::Registry->for_class( $into );
181 2         44 $me->_ensure_isa_type_library( $into );
182            
183 2         23 my $optlist = mkopt( $setup->{$kind} );
184 2         54 for my $dfn ( @$optlist ) {
185 2   100     24 ( my $pkg_name = ( $dfn->[1] //= {} )->{$kind} // $dfn->[0] );
      33        
186 2   50     26 ( my $type_name = ( $dfn->[1] //= {} )->{name} // $dfn->[0] ) =~ s/:://g;
      66        
187 2         28 $tt_class->import( { into => $into }, @$dfn );
188 2         4530 $into->add_type( $reg->lookup( $type_name ) );
189 2         1146 eval { require_module( $pkg_name ) };
  2         12  
190             }
191            
192 2         15598 return;
193             }
194              
195             sub setup_ducks_for {
196 1     1 0 5616 my ( $me, $into, $setup ) = @_;
197            
198 1         592 require Type::Tiny::Duck;
199 1         1865 my $reg = Type::Registry->for_class( $into );
200 1         26 $me->_ensure_isa_type_library( $into );
201            
202 1   50     8 my %types = %{ assert_HashRef $setup->{duck} // {} };
  1         20  
203 1         16 for my $type_name ( keys %types ) {
204 1         3 my $values = $types{$type_name};
205 1         16 Type::Tiny::Duck->import( { into => $into }, $type_name, $values );
206 1         2023 $into->add_type( $reg->lookup( $type_name ) );
207             }
208            
209 1         589 return;
210             }
211              
212             sub setup_types_for {
213 2     2 0 21316 my ( $me, $into, $setup ) = @_;
214            
215 2         17 my $reg = Type::Registry->for_class( $into );
216 2         37 $me->_ensure_isa_type_library( $into );
217            
218 2         19 my $optlist = mkopt( $setup->{type} );
219 2         45 my @extends = ();
220 2         8 for my $dfn ( @$optlist ) {
221 2         9 my ( $lib, $list ) = @$dfn;
222 2         5 eval { require_module( $lib ) };
  2         9  
223 2 100       79 if ( is_ArrayRef $list ) {
224 1         6 for my $type_name ( @$list ) {
225 1         8 $into->add_type( $lib->get_type( $type_name ) );
226             }
227             }
228             else {
229 1         3 push @extends, $lib;
230             }
231             }
232            
233 2 100       548 if ( @extends ) {
234 1         10 require Type::Utils;
235 1         85 my $wrapper = eval "sub { package $into; &Type::Utils::extends; }";
236 1         31 $wrapper->( @extends );
237             }
238            
239 2         7068 return;
240             }
241              
242             sub _ensure_isa_type_library {
243 7     7   24 my ( $me, $into ) = @_;
244 7 50       104 return if $into->isa( 'Type::Library' );
245 7         30 my ( $old_isa ) = $me->standard_package_variables( $into );
246 7         25 my $new_isa = [];
247 7         22 my $saw_exporter_tiny = 0;
248 7         30 for my $pkg ( @$old_isa ) {
249 1 50       5 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       30 push @$new_isa, 'Type::Library' unless $saw_exporter_tiny;
258 7         145 @$old_isa = @$new_isa;
259             }
260              
261             sub setup_constants_for {
262 3     3 0 134544 my ( $me, $into, $setup ) = @_;
263            
264 3         14 my ( $into_ISA, undef, undef, $into_EXPORT_TAGS ) =
265             $me->standard_package_variables( $into );
266              
267 3   50     10 my %tags = %{ assert_HashRef $setup->{const} // {} };
  3         31  
268 3         36 for my $tag_name ( keys %tags ) {
269 4         25 my %exports = %{ assert_HashRef $tags{$tag_name} };
  4         15  
270 4         39 $tag_name =~ s/^[-:]//;
271 4         37 my @constant_names = sort keys %exports;
272 4   50     12 push @{ $into_EXPORT_TAGS->{$tag_name} //= [] }, @constant_names;
  4         36  
273 4   100     12 push @{ $into_EXPORT_TAGS->{'constants'} //= [] }, @constant_names;
  4         22  
274 4         21 $me->make_constant_subs( $into, \%exports );
275             }
276            
277 3         56 return;
278             }
279              
280             sub make_constant_subs {
281 5     5 0 8068 my ( $me, $into, $constants ) = @_;
282            
283 5         29 for my $key ( keys %$constants ) {
284 17         230 my $value = $constants->{$key};
285 17         47 my $full_name = "$into\::$key";
286            
287 17         25 my $coderef;
288 17 100       59 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       53 $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   32 no strict 'refs';
  4         37  
  4         769  
303 17         651 *$full_name = set_subname $full_name => $coderef;
304             }
305             }
306              
307             sub setup_readonly_vars_for {
308 8     8 0 12386 my ( $me, $into, $setup ) = @_;
309            
310 8         36 my ( $into_ISA, $into_EXPORT, $into_EXPORT_OK, $into_EXPORT_TAGS ) =
311             $me->standard_package_variables( $into );
312            
313 8   100     26 my @constants = @{ $into_EXPORT_TAGS->{'constants'} // [] };
  8         50  
314 8         20 for my $name ( @constants ) {
315 4     4   44 no strict 'refs';
  4         16  
  4         1495  
316 9         31 my $full_name = "$into\::$name";
317 9         14 ${ $full_name } = &{ $full_name }();
  9         24  
  9         29  
318 9         26 Internals::SvREADONLY( ${ $full_name }, 1 );
  9         32  
319 9   100     11 push @{ $into_EXPORT_TAGS->{'ro_vars'} //= [] }, '$' . $name;
  9         45  
320             }
321            
322 8         21 return;
323             }
324              
325             sub finalize_export_variables_for {
326 3     3 0 9295 my ( $me, $into, $setup ) = @_;
327            
328 3         13 my ( $into_ISA, $into_EXPORT, $into_EXPORT_OK, $into_EXPORT_TAGS ) =
329             $me->standard_package_variables( $into );
330            
331 3         16 my %all_exports;
332 3   50     16 for my $list ( $into_EXPORT, $into_EXPORT_OK, values %{ $into_EXPORT_TAGS // {} } ) {
  3         21  
333 22 50       60 is_ArrayRef $list or next;
334 22         71 $all_exports{$_}++ for @$list;
335             }
336 3         27 @{ $into_EXPORT_OK } = sort keys %all_exports;
  3         25  
337            
338 3         11 my %default_exports;
339 3         12 for my $list ( $into_EXPORT, $into_EXPORT_TAGS->{default} ) {
340 6 100       54 is_ArrayRef $list or next;
341 4         15 $default_exports{$_}++ for @$list;
342             }
343 3         12 @{ $into_EXPORT } = sort keys %default_exports;
  3         23  
344            
345 3         31 return;
346             }
347              
348             1;
349              
350             __END__