File Coverage

blib/lib/Specio/Subs.pm
Criterion Covered Total %
statement 87 89 97.7
branch 9 12 75.0
condition 2 2 100.0
subroutine 20 20 100.0
pod 0 1 0.0
total 118 124 95.1


line stmt bran cond sub pod time code
1              
2             use strict;
3 2     2   1014 use warnings;
  2         4  
  2         56  
4 2     2   8  
  2         4  
  2         91  
5             our $VERSION = '0.48';
6              
7             use Carp qw( croak );
8 2     2   9 use Eval::Closure qw( eval_closure );
  2         4  
  2         111  
9 2     2   10 use Module::Runtime qw( use_package_optimistically );
  2         6  
  2         88  
10 2     2   850 use Specio::Library::Perl;
  2         3160  
  2         12  
11 2     2   606 use Specio::Registry qw( exportable_types_for_package );
  2         6  
  2         21  
12 2     2   11  
  2         4  
  2         1552  
13             shift;
14             my @libs = @_;
15 4     4   3278  
16 4         11 my $caller = caller();
17              
18 4         8 my $ident = t('Identifier');
19              
20 4         14 use_package_optimistically($_) for @libs;
21              
22 4         17 for my $types ( map { exportable_types_for_package($_) } @libs ) {
23             for my $name ( keys %{$types} ) {
24 4         198 croak
  6         16  
25 6         9 qq{Cannot use '$name' type to create a check sub. It results in an invalid Perl subroutine name}
  6         28  
26 50 100       167 unless $ident->check( 'is_' . $name );
27              
28             _export_subs( $name, $types->{$name}, $caller );
29             }
30 49         1819 }
31             }
32              
33             my $name = shift;
34             my $type = shift;
35             my $caller = shift;
36 49     49   63  
37 49         60 _export_validation_subs( $name, $type, $caller );
38 49         55  
39             return unless $type->has_coercions;
40 49         94  
41             _export_coercion_subs( $name, $type, $caller );
42 49 100       202 }
43              
44 1         4 my $name = shift;
45             my $type = shift;
46             my $caller = shift;
47              
48 49     49   60 my $is_name = 'is_' . $name;
49 49         51 my $assert_name = 'assert_' . $name;
50 49         54 if ( $type->can_be_inlined ) {
51             _make_sub(
52 49         76 $caller, $is_name,
53 49         81 $type->inline_check('$_[0]')
54 49 100       103 );
55 48         282 _make_sub(
56             $caller, $assert_name,
57             $type->inline_assert('$_[0]')
58             );
59 48         142 }
60             else {
61             _install_sub(
62             $caller, $is_name,
63             sub { $type->value_is_valid( $_[0] ) }
64             );
65             _install_sub(
66             $caller, $assert_name,
67 53     53   20650 sub { $type->validate_or_die( $_[0] ) }
        53      
68 1         16 );
69             }
70             }
71 53     53   2594  
72 1         3 my $name = shift;
73             my $type = shift;
74             my $caller = shift;
75              
76             my $to_name = 'to_' . $name;
77 1     54   2 if ( $type->can_inline_coercion ) {
78 1         1 _make_sub(
79 1         2 $caller, $to_name,
80             $type->inline_coercion('$_[0]')
81 1         3 );
82 1 50       4 }
83 0         0 else {
84             _install_sub(
85             $caller, $to_name,
86             sub { $type->coerce_value( $_[0] ) }
87             );
88             }
89              
90             my $force_name = 'force_' . $name;
91 3     3   484 if ( $type->can_inline_coercion_and_check ) {
92 1         9 _make_sub(
93             $caller, $force_name,
94             $type->inline_coercion_and_check('$_[0]')
95 1         5 );
96 1 50       4 }
97 0         0 else {
98             _install_sub(
99             $caller, $force_name,
100             sub {
101             my $val = $type->coerce_value( $_[0] );
102             $type->validate_or_die($val);
103             return $val;
104             }
105             );
106 3     6   1493 }
107 3         22 }
108 2         444  
109             my $caller = shift;
110 1         9 my $sub_name = shift;
111             my $source = shift;
112             my $env = shift;
113              
114             my $sub = eval_closure(
115 96     99   137 source => 'sub { ' . $source . ' }',
116 96         106 environment => $env,
117 96         103 description => $caller . '::'
118 96         100 . $sub_name
119             . ' generated by '
120 96         319 . __PACKAGE__,
121             );
122              
123             _install_sub( $caller, $sub_name, $sub );
124              
125             return;
126             }
127              
128             my $sub_namer = do {
129 96         32695 eval {
130             require Sub::Util;
131 96         144 Sub::Util->VERSION(1.40);
132             Sub::Util->can('set_subname');
133             } or eval {
134             require Sub::Name;
135             Sub::Name->can('subname');
136             }
137             or sub { return $_[1] };
138             };
139              
140             my %Installed;
141              
142             my $caller = shift;
143             my $sub_name = shift;
144             my $sub = shift;
145              
146             my $fq_name = $caller . '::' . $sub_name;
147              
148             {
149 100     100   237 ## no critic (TestingAndDebugging::ProhibitNoStrict)
150 100         112 no strict 'refs';
151 100         105 *{$fq_name} = $sub_namer->( $fq_name, $sub );
152             }
153 100         179  
154             $Installed{$caller} ||= [];
155             push @{ $Installed{$caller} }, $sub_name;
156              
157 2     2   15 return;
  2         4  
  2         283  
  100         109  
158 100         522 }
  100         391  
159              
160             my $package = shift;
161 100   100     249  
162 100         109 return @{ $Installed{$package} || [] };
  100         185  
163             }
164 100         144  
165             1;
166              
167             # ABSTRACT: Make validation and coercion subs from Specio types
168 1     1 0 8  
169              
170 1 50       3 =pod
  1         28  
171              
172             =encoding UTF-8
173              
174             =head1 NAME
175              
176             Specio::Subs - Make validation and coercion subs from Specio types
177              
178             =head1 VERSION
179              
180             version 0.48
181              
182             =head1 SYNOPSIS
183              
184             use Specio::Subs qw( Specio::Library::Builtins Specio::Library::Perl My::Lib );
185              
186             if ( is_PackageName($var) ) { ... }
187              
188             assert_Str($var);
189              
190             my $person1 = to_Person($var);
191             my $person2 = force_Person($var);
192              
193             =head1 DESCRIPTION
194              
195             This module generates a set of helpful validation and coercion subroutines for
196             all of the types defined in one or more libraries.
197              
198             To use it, simply import C<Specio::Subs> passing a list of one or more library
199             names. This module will load those libraries as needed.
200              
201             If any of the types in any libraries have names that do not work as part of a
202             Perl subroutine name, this module will throw an exception.
203              
204             If you have L<Sub::Util> or L<Sub::Name> installed, one of those will be used
205             to name the generated subroutines.
206              
207             =head1 "EXPORTS"
208              
209             The following subs are created in the importing package:
210              
211             =head2 is_$type($value)
212              
213             This subroutine returns a boolean indicating whether or not the C<$value> is
214             valid for the type.
215              
216             =head2 assert_$type($value)
217              
218             This subroutine dies if the C<$value> is not valid for the type.
219              
220             =head2 to_$type($value)
221              
222             This subroutine attempts to coerce C<$value> into the given type. If it cannot
223             be coerced it returns the original C<$value>.
224              
225             This is only created if the type has coercions.
226              
227             =head2 force_$type($value)
228              
229             This subroutine attempts to coerce C<$value> into the given type, and dies if
230             it cannot do so.
231              
232             This is only created if the type has coercions.
233              
234             =head1 ADDITIONAL API
235              
236             =for Pod::Coverage subs_installed_into
237              
238             This module has a subroutine named C<subs_installed_into>. It is not exported
239             but it can be called by its fully qualified name. It accepts a single argument,
240             a package name. It returns a list of subs that it generated and installed in
241             the given package, if any.
242              
243             This exists to make it easy to write a type library that combines other
244             libraries and generates helper subs for export all at once.
245              
246             See the L<Specio::Exporter> docs for more details.
247              
248             =head1 SUPPORT
249              
250             Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
251              
252             =head1 SOURCE
253              
254             The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
255              
256             =head1 AUTHOR
257              
258             Dave Rolsky <autarch@urth.org>
259              
260             =head1 COPYRIGHT AND LICENSE
261              
262             This software is Copyright (c) 2012 - 2022 by Dave Rolsky.
263              
264             This is free software, licensed under:
265              
266             The Artistic License 2.0 (GPL Compatible)
267              
268             The full text of the license can be found in the
269             F<LICENSE> file included with this distribution.
270              
271             =cut