File Coverage

blib/lib/Exporter/Almighty.pm
Criterion Covered Total %
statement 217 218 99.5
branch 35 40 87.5
condition 18 28 64.2
subroutine 37 37 100.0
pod 0 13 0.0
total 307 336 91.3


line stmt bran cond sub pod time code
1 3     3   705255 use 5.012;
  3         29  
2 3     3   22 use strict;
  3         5  
  3         64  
3 3     3   14 use warnings;
  3         9  
  3         194  
4              
5             package Exporter::Almighty;
6              
7             our $AUTHORITY = 'cpan:TOBYINK';
8             our $VERSION = '0.001003';
9              
10 3     3   1390 use parent qw( Exporter::Tiny );
  3         921  
  3         26  
11              
12             my @builtins;
13 3     3   23940 BEGIN { @builtins = qw( is_bool created_as_string created_as_number ) };
14 3     3   2020 use if $] lt '5.036000', 'builtins::compat' => @builtins;
  3         48  
  3         28  
15 3     3   59000 use if $] ge '5.036000', 'builtin' => @builtins;
  3         8  
  3         22  
16 3     3   129 no if $] ge '5.036000', 'warnings' => qw( experimental::builtin );
  3         7  
  3         15  
17              
18 3     3   145 use B qw( perlstring );
  3         6  
  3         172  
19 3     3   17 use Carp qw( croak );
  3         9  
  3         173  
20 3     3   1540 use Eval::TypeTiny qw( eval_closure set_subname );
  3         8067  
  3         18  
21 3     3   1701 use Exporter::Tiny qw( mkopt );
  3         11  
  3         20  
22 3     3   2048 use Import::Into;
  3         1397  
  3         106  
23 3     3   21 use Module::Runtime qw( require_module module_notional_filename );
  3         12  
  3         14  
24 3     3   1612 use Type::Registry qw();
  3         60091  
  3         136  
25 3         38 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 3     3   2416 );
  3         711136  
33              
34             sub _exporter_validate_opts {
35 1     1   147 my ( $me, $options ) = @_;
36 1         3 my $into = $options->{into};
37 1         17 my $setup = $options->{setup};
38 1         9 strict->import::into( $into );
39 1         226 warnings->import::into( $into );
40 1         189 $me->setup_for( $into, $setup );
41             }
42              
43             # Subclasses may wish to provide a subclass of Exporter::Tiny here.
44             sub base_exporter {
45 7     7 0 17592 return 'Exporter::Tiny';
46             }
47              
48             sub standard_package_variables {
49 19     19 0 131036 my ( $me, $into ) = @_;
50 3     3   51975 no strict 'refs';
  3         10  
  3         2349  
51             return (
52 19         139 \@{"$into\::ISA"},
53 19         73 \@{"$into\::EXPORT"},
54 19         78 \@{"$into\::EXPORT_OK"},
55 19         31 \%{"$into\::EXPORT_TAGS"},
  19         93  
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 15     15 0 57661 my ( $me, $into, $setup ) = @_;
87 15         27 my @steps;
88 15         30 push @steps, 'setup_exporter_for';
89 15 100       64 push @steps, 'setup_reexports_for' if $setup->{also};
90 15 100       49 push @steps, 'setup_enums_for' if $setup->{enum};
91 15 100       47 push @steps, 'setup_classes_for' if $setup->{class};
92 15 100       39 push @steps, 'setup_roles_for' if $setup->{role};
93 15 100       43 push @steps, 'setup_ducks_for' if $setup->{duck};
94 15 100       38 push @steps, 'setup_types_for' if $setup->{type};
95 15 100       39 push @steps, 'setup_constants_for' if $setup->{const};
96 15         29 push @steps, 'finalize_export_variables_for';
97 15         59 return @steps;
98             }
99              
100             sub setup_exporter_for {
101 6     6 0 47813 my ( $me, $into, $setup ) = @_;
102            
103 6         20 my ( $into_ISA, undef, undef, $into_EXPORT_TAGS ) =
104             $me->standard_package_variables( $into );
105            
106             # Set up @ISA in caller package.
107 6         22 my $base = $me->base_exporter( $into, $setup );
108 6 50       121 push @$into_ISA, $base unless $into->isa( $base );
109            
110             # Set up %EXPORT_TAGS in caller package.
111 6   100     20 my %tags = %{ $setup->{tag} // {} };
  6         39  
112 6         21 for my $tag_name ( keys %tags ) {
113 7         12 my @exports = @{ assert_ArrayRef $tags{$tag_name} };
  7         43  
114 7         61 $tag_name =~ s/^[-:]//;
115 7   50     14 push @{ $into_EXPORT_TAGS->{$tag_name} //= [] }, @exports;
  7         48  
116             }
117            
118 6         21 return;
119             }
120              
121             sub setup_reexports_for {
122 2     2 0 8904 my ( $me, $into, $setup ) = @_;
123            
124 2         28 my $next = $into->can( '_exporter_validate_opts' );
125            
126 2         13 my $optlist = mkopt( $setup->{also} );
127 2         64 require_module( $_->[0] ) for @$optlist;
128            
129 2         1638 my $method_name = "$into\::_exporter_validate_opts";
130             my $method_code = sub {
131 2     2   1700 my ( $class, $opts ) = @_;
        2      
132 2 50       18 is_NonEmptySimpleStr( my $caller = $opts->{into} ) or return;
133 2         32 for my $also ( @$optlist ) {
134 4         413 my ( $module, $args ) = @$also;
135 4   100     10 $module->import::into( $caller, @{ $args // [] } );
  4         41  
136             }
137 2 100       732 goto $next if $next;
138 2         14 };
139 3     3   30 no strict 'refs';
  3         8  
  3         4329  
140 2         10 *$method_name = set_subname $method_name => $method_code;
141             }
142              
143             sub setup_enums_for {
144 2     2 0 9138 my ( $me, $into, $setup ) = @_;
145            
146 2         1399 require Type::Tiny::Enum;
147 2         9493 my $reg = Type::Registry->for_class( $into );
148 2         46 $me->_ensure_isa_type_library( $into );
149            
150 2   50     9 my %tags = %{ assert_HashRef $setup->{enum} // {} };
  2         25  
151 2         25 for my $tag_name ( keys %tags ) {
152 2         7 my $values = $tags{$tag_name};
153 2         9 $tag_name =~ s/^[-:]//;
154 2         5 my $type_name = $tag_name;
155 2         7 $tag_name = lc $tag_name;
156            
157 2         28 Type::Tiny::Enum->import( { into => $into }, $type_name, $values );
158 2         47708 $into->add_type( $reg->lookup( $type_name ) );
159             }
160            
161 2         2129 return;
162             }
163              
164             sub setup_classes_for {
165 1     1 0 9036 my ( $me, $into, $setup ) = @_;
166 1         746 require Type::Tiny::Class;
167 1         3938 $me->_setup_classes_or_roles_for( $into, $setup, 'class', 'Type::Tiny::Class' );
168             }
169              
170             sub setup_roles_for {
171 1     1 0 7067 my ( $me, $into, $setup ) = @_;
172 1         547 require Type::Tiny::Role;
173 1         1399 $me->_setup_classes_or_roles_for( $into, $setup, 'role', 'Type::Tiny::Role' );
174             }
175              
176             sub _setup_classes_or_roles_for {
177 2     2   10 my ( $me, $into, $setup, $kind, $tt_class ) = @_;
178            
179 2         14 my $reg = Type::Registry->for_class( $into );
180 2         44 $me->_ensure_isa_type_library( $into );
181            
182 2         23 my $optlist = mkopt( $setup->{$kind} );
183 2         50 for my $dfn ( @$optlist ) {
184 2   100     29 ( my $pkg_name = ( $dfn->[1] //= {} )->{$kind} // $dfn->[0] );
      33        
185 2   50     32 ( my $type_name = ( $dfn->[1] //= {} )->{name} // $dfn->[0] ) =~ s/:://g;
      66        
186 2         34 $tt_class->import( { into => $into }, @$dfn );
187 2         4662 $into->add_type( $reg->lookup( $type_name ) );
188 2         1183 eval { require_module( $pkg_name ) };
  2         13  
189             }
190            
191 2         16248 return;
192             }
193              
194             sub setup_ducks_for {
195 1     1 0 12675 my ( $me, $into, $setup ) = @_;
196            
197 1         753 require Type::Tiny::Duck;
198 1         1993 my $reg = Type::Registry->for_class( $into );
199 1         38 $me->_ensure_isa_type_library( $into );
200            
201 1   50     5 my %types = %{ assert_HashRef $setup->{duck} // {} };
  1         11  
202 1         15 for my $type_name ( keys %types ) {
203 1         5 my $values = $types{$type_name};
204 1         17 Type::Tiny::Duck->import( { into => $into }, $type_name, $values );
205 1         2171 $into->add_type( $reg->lookup( $type_name ) );
206             }
207            
208 1         626 return;
209             }
210              
211             sub setup_types_for {
212 2     2 0 24599 my ( $me, $into, $setup ) = @_;
213            
214 2         18 my $reg = Type::Registry->for_class( $into );
215 2         40 $me->_ensure_isa_type_library( $into );
216            
217 2         18 my $optlist = mkopt( $setup->{type} );
218 2         53 my @extends = ();
219 2         6 for my $dfn ( @$optlist ) {
220 2         6 my ( $lib, $list ) = @$dfn;
221 2         4 eval { require_module( $lib ) };
  2         11  
222 2 100       84 if ( is_ArrayRef $list ) {
223 1         10 for my $type_name ( @$list ) {
224 1         10 $into->add_type( $lib->get_type( $type_name ) );
225             }
226             }
227             else {
228 1         4 push @extends, $lib;
229             }
230             }
231            
232 2 100       550 if ( @extends ) {
233 1         11 require Type::Utils;
234 1         147 my $wrapper = eval "sub { package $into; &Type::Utils::extends; }";
235 1         25 $wrapper->( @extends );
236             }
237            
238 2         6835 return;
239             }
240              
241             sub _ensure_isa_type_library {
242 7     7   25 my ( $me, $into ) = @_;
243 7 50       105 return if $into->isa( 'Type::Library' );
244 7         49 my ( $old_isa ) = $me->standard_package_variables( $into );
245 7         23 my $new_isa = [];
246 7         18 my $saw_exporter_tiny = 0;
247 7         24 for my $pkg ( @$old_isa ) {
248 1 50       5 if ( $pkg eq 'Exporter::Tiny' ) {
249 1         2 push @$new_isa, 'Type::Library';
250 1         3 $saw_exporter_tiny++;
251             }
252             else {
253 0         0 push @$new_isa, $pkg;
254             }
255             }
256 7 100       28 push @$new_isa, 'Type::Library' unless $saw_exporter_tiny;
257 7         161 @$old_isa = @$new_isa;
258             }
259              
260             sub setup_constants_for {
261 2     2 0 10611 my ( $me, $into, $setup ) = @_;
262            
263 2         11 my ( $into_ISA, undef, undef, $into_EXPORT_TAGS ) =
264             $me->standard_package_variables( $into );
265              
266 2   50     7 my %tags = %{ assert_HashRef $setup->{const} // {} };
  2         19  
267 2         45 for my $tag_name ( keys %tags ) {
268 3         25 my %exports = %{ assert_HashRef $tags{$tag_name} };
  3         11  
269 3         32 $tag_name =~ s/^[-:]//;
270 3   50     6 push @{ $into_EXPORT_TAGS->{$tag_name} //= [] }, sort keys %exports;
  3         37  
271 3   100     6 push @{ $into_EXPORT_TAGS->{'constants'} //= [] }, sort keys %exports;
  3         27  
272 3         14 $me->make_constant_subs( $into, \%exports );
273             }
274            
275 2         36 return;
276             }
277              
278             sub make_constant_subs {
279 4     4 0 12259 my ( $me, $into, $constants ) = @_;
280            
281 4         13 for my $key ( keys %$constants ) {
282 14         240 my $value = $constants->{$key};
283 14         39 my $full_name = "$into\::$key";
284            
285 14         22 my $coderef;
286 14 100       54 if ( is_Ref $value ) {
287 2         18 $coderef = eval_closure(
288             source => 'sub () { $value }',
289             environment => { '$value' => \$value },
290             );
291             }
292             else {
293 12 100       49 $coderef = eval sprintf(
    100          
294             'sub () { %s %s }',
295             is_bool( $value ) ? '!!' : ( created_as_number( $value ) ? '0+' : '' ),
296             perlstring( $value ),
297             );
298             }
299            
300 3     3   27 no strict 'refs';
  3         6  
  3         1071  
301 14         604 *$full_name = set_subname $full_name => $coderef;
302             }
303             }
304              
305             sub finalize_export_variables_for {
306 2     2 0 11406 my ( $me, $into, $setup ) = @_;
307            
308 2         8 my ( $into_ISA, $into_EXPORT, $into_EXPORT_OK, $into_EXPORT_TAGS ) =
309             $me->standard_package_variables( $into );
310            
311 2         14 my %all_exports;
312 2   50     10 for my $list ( $into_EXPORT, $into_EXPORT_OK, values %{ $into_EXPORT_TAGS // {} } ) {
  2         15  
313 16 50       35 is_ArrayRef $list or next;
314 16         52 $all_exports{$_}++ for @$list;
315             }
316 2         21 @{ $into_EXPORT_OK } = sort keys %all_exports;
  2         8  
317            
318 2         6 my %default_exports;
319 2         10 for my $list ( $into_EXPORT, $into_EXPORT_TAGS->{default} ) {
320 4 100       16 is_ArrayRef $list or next;
321 3         9 $default_exports{$_}++ for @$list;
322             }
323 2         8 @{ $into_EXPORT } = sort keys %default_exports;
  2         5  
324            
325 2         8 return;
326             }
327              
328             1;
329              
330             __END__