File Coverage

blib/lib/Type/Library.pm
Criterion Covered Total %
statement 220 220 100.0
branch 67 82 81.7
condition 45 59 76.2
subroutine 37 37 100.0
pod 11 11 100.0
total 380 409 92.9


line stmt bran cond sub pod time code
1             package Type::Library;
2              
3 285     285   861219 use 5.008001;
  285         1114  
4 285     285   1781 use strict;
  285         681  
  285         8352  
5 285     285   1504 use warnings;
  285         815  
  285         27069  
6              
7             BEGIN {
8 285     285   1088 $Type::Library::AUTHORITY = 'cpan:TOBYINK';
9 285         17243 $Type::Library::VERSION = '2.010001';
10             }
11              
12             $Type::Library::VERSION =~ tr/_//d;
13              
14 285     285   135205 use Eval::TypeTiny qw< eval_closure set_subname type_to_coderef NICE_PROTOTYPES >;
  285         931  
  285         2231  
15 285     285   240046 use Scalar::Util qw< blessed refaddr >;
  285         691  
  285         25624  
16 285     285   112029 use Type::Tiny ();
  285         1011  
  285         7381  
17 285     285   1895 use Types::TypeTiny ();
  285         568  
  285         370544  
18              
19             require Exporter::Tiny;
20             our @ISA = 'Exporter::Tiny';
21              
22 8     8   61 sub _croak ($;@) { require Error::TypeTiny; goto \&Error::TypeTiny::croak }
  8         53  
23              
24             ####
25             #### Hooks for Exporter::Tiny
26             ####
27              
28             # Handling for -base, -extends, and -utils tags.
29             #
30             sub _exporter_validate_opts {
31 1248     1248   2796552 my ( $class, $opts ) = ( shift, @_ );
32            
33 459         2941 $class->setup_type_library( @{$opts}{qw/ into utils extends /}, $opts )
34 1248 100 100     10924 if $_[0]{base} || $_[0]{extends};
35            
36 1248         7733 return $class->SUPER::_exporter_validate_opts( @_ );
37             }
38              
39             # In Exporter::Tiny, this method takes a sub name, a 'value' (i.e.
40             # potentially an options hashref for the export), and some global
41             # options, and returns a list of name+coderef pairs to actually
42             # export. We override it to provide some useful features.
43             #
44             sub _exporter_expand_sub {
45 22691     22691   1178237 my ( $class, $name, $value, $globals ) = ( shift, @_ );
46            
47             # Handle exporting '+Type'.
48             #
49             # Note that this recurses, so if used in conjunction with the other
50             # special cases handled by this method, will still work.
51             #
52 22691 100 66     68750 if ( $name =~ /^\+(.+)/ and $class->has_type( "$1" ) ) {
53 13         78 my $type = $class->get_type( "$1" );
54 13         101 my $exported = $type->exportables;
55             return map $class->_exporter_expand_sub(
56             $_->{name},
57 13 50       58 +{ %{ $value || {} } },
  58         21320  
58             $globals,
59             ), @$exported;
60             }
61            
62             # Is the function being exported one which is associated with a
63             # type constraint? If so, which one. If not, then forget the rest
64             # and just use the superclass method.
65             #
66 22678 100 100     52679 if ( my $f = $class->meta->{'functions'}{$name}
67             and defined $class->meta->{'functions'}{$name}{'type'} ) {
68            
69 21416         38865 my $type = $f->{type};
70 21416         50304 my $tag = $f->{tags}[0];
71 21416         65811 my $typename = $type->name;
72            
73             # If $value has `of` or `where` options, then this is a
74             # custom type.
75             #
76 21416         37132 my $custom_type = 0;
77 21416         39989 for my $param ( qw/ of where / ) {
78 42832 100       95472 exists $value->{$param} or next;
79 3 50       7 defined $value->{-as} or _croak( "Parameter '-as' not supplied" );
80 3         8 $type = $type->$param( $value->{$param} );
81 3         4 $name = $value->{-as};
82 3         5 ++$custom_type;
83             }
84            
85             # If we're exporting a type itself, then export a custom
86             # function if they customized the type or want a Moose/Mouse
87             # type constraint.
88             #
89 21416 100       46614 if ( $tag eq 'types' ) {
90 10790         16630 my $post_method = q();
91 10790 50       24150 $post_method = '->mouse_type' if $globals->{mouse};
92 10790 50       22444 $post_method = '->moose_type' if $globals->{moose};
93 10790 100 66     41268 return ( $name => type_to_coderef( $type, post_method => $post_method ) )
94             if $post_method || $custom_type;
95             }
96            
97             # If they're exporting some other type of function, like
98             # 'to', 'is', or 'assert', then find the correct exportable
99             # by tag name, and return that.
100             #
101             # XXX: this will fail for tags like 'constants' where there
102             # will be multiple exportables which match!
103             #
104 21414 100 66     58268 if ( $custom_type and $tag ne 'types' ) {
105 1         4 my $exportable = $type->exportables_by_tag( $tag, $typename );
106 1   33     7 return ( $value->{-as} || $exportable->{name}, $exportable->{code} );
107             }
108             }
109            
110             # In all other cases, the superclass method will work.
111             #
112 22675         76801 return $class->SUPER::_exporter_expand_sub( @_ );
113             }
114              
115             # Mostly just rely on superclass to do the actual export, but add
116             # a couple of useful behaviours.
117             #
118             sub _exporter_install_sub {
119 22676     22676   1043637 my ( $class, $name, $value, $globals, $sym ) = ( shift, @_ );
120            
121 22676         45256 my $into = $globals->{into};
122 22676         57891 my $type = $class->meta->{'functions'}{$name}{'type'};
123 22676         46554 my $tags = $class->meta->{'functions'}{$name}{'tags'};
124            
125             # Issue a warning if exporting a deprecated type constraint.
126             #
127             Exporter::Tiny::_carp(
128             "Exporting deprecated type %s to %s",
129             $type->qualified_name,
130             ref( $into ) ? "reference" : "package $into",
131 22676 50 100     84407 ) if ( defined $type and $type->deprecated and not $globals->{allow_deprecated} );
    100 100        
132            
133             # If exporting a type constraint into a real package, then
134             # add it to the package's type registry.
135             #
136 22676 100 66     160274 if ( !ref $into
      100        
      100        
137             and $into ne '-lexical'
138             and defined $type
139             and grep $_ eq 'types', @$tags ) {
140            
141             # If they're renaming it, figure out what name, and use that.
142             # XXX: `-as` can be a coderef, and can be in $globals in that case.
143 10088         36850 my ( $prefix ) = grep defined, $value->{-prefix}, $globals->{prefix}, q();
144 10088         26898 my ( $suffix ) = grep defined, $value->{-suffix}, $globals->{suffix}, q();
145 10088   66     34774 my $as = $prefix . ( $value->{-as} || $name ) . $suffix;
146            
147             $INC{'Type/Registry.pm'}
148             ? 'Type::Registry'->for_class( $into )->add_type( $type, $as )
149 10088 100       37642 : ( $Type::Registry::DELAYED{$into}{$as} = $type );
150             }
151            
152 22676         69093 $class->SUPER::_exporter_install_sub( @_ );
153             } #/ sub _exporter_install_sub
154              
155             sub _exporter_fail {
156 1110     1110   32521 my ( $class, $name, $value, $globals ) = ( shift, @_ );
157            
158             # Passing the `-declare` flag means that if a type isn't found, then
159             # we export a placeholder function instead of failing.
160 1110 100       2845 if ( $globals->{declare} ) {
161             return (
162             $name,
163             type_to_coderef(
164             undef,
165             type_name => $name,
166 1108   33     4160 type_library => $globals->{into} || _croak( "Parameter 'into' not supplied" ),
167             ),
168             );
169             } #/ if ( $globals->{declare...})
170            
171 2         9 return $class->SUPER::_exporter_fail( @_ );
172             } #/ sub _exporter_fail
173              
174             ####
175             #### Type library functionality
176             ####
177              
178             sub setup_type_library {
179 459     459 1 1506 my ( $class, $type_library, $install_utils, $extends, $opts ) = ( shift, @_ );
180            
181 459 50       2415 my @extends = ref( $extends ) ? @$extends : $extends ? $extends : ();
    100          
182 459 100       1740 unshift @extends, $class if $class ne __PACKAGE__;
183            
184 459 50       1692 if ( not ref $type_library ) {
185 285     285   3640 no strict "refs";
  285         1159  
  285         82303  
186 459         1141 push @{"$type_library\::ISA"}, $class;
  459         6783  
187 459         2973 ( my $file = $type_library ) =~ s{::}{/}g;
188 459   100     2842 $INC{"$file.pm"} ||= __FILE__;
189             }
190            
191 459 100       2166 if ( $install_utils ) {
192 4         1984 require Type::Utils;
193 4         84 'Type::Utils'->import(
194             { %$opts, into => $type_library },
195             '-default',
196             );
197             }
198            
199 459 100 66     14817 if ( @extends and not ref $type_library ) {
200 31         20079 require Type::Utils;
201 31         3075 my $wrapper = eval "sub { package $type_library; &Type::Utils::extends; }";
202 31         939 $wrapper->( @extends );
203             }
204             }
205              
206             sub meta {
207 285     285   2494 no strict "refs";
  285         905  
  285         13065  
208 285     285   1721 no warnings "once";
  285         659  
  285         129834  
209 142378 100   142378 1 638537 return $_[0] if blessed $_[0];
210 94686   100     135781 ${"$_[0]\::META"} ||= bless {}, $_[0];
  94686         458087  
211             }
212              
213             sub add_type {
214 14346     14346 1 57843 my $meta = shift->meta;
215 14346         30437 my $class = blessed( $meta ) ;
216            
217 14346 100       40009 _croak( 'Type library is immutable' ) if $meta->{immutable};
218            
219             my $type =
220             ref( $_[0] ) =~ /^Type::Tiny\b/ ? $_[0] :
221             blessed( $_[0] ) ? Types::TypeTiny::to_TypeTiny( $_[0] ) :
222 14342 100       70804 ref( $_[0] ) eq q(HASH) ? 'Type::Tiny'->new( library => $class, %{ $_[0] } ) :
  10397 50       69296  
    100          
223             "Type::Tiny"->new( library => $class, @_ );
224 14342         43022 my $name = $type->{name};
225            
226 14342 100       43521 if ( $meta->has_type( $name ) ) {
227 1         8 my $existing = $meta->get_type( $name );
228 1 50       5 return if $type->{uniq} == $existing->{uniq};
229 1         5 _croak( 'Type %s already exists in this library', $name );
230             }
231            
232 14341 50       41765 _croak( 'Type %s conflicts with coercion of same name', $name ) if $meta->has_coercion( $name );
233 14341 100       50252 _croak( 'Cannot add anonymous type to a library' ) if $type->is_anon;
234 14340   50     40012 $meta->{types} ||= {};
235 14340         39897 $meta->{types}{$name} = $type;
236            
237 285     285   3602 no strict "refs";
  285         869  
  285         15344  
238 285     285   1618 no warnings "redefine", "prototype";
  285         657  
  285         137536  
239            
240 14340         22113 for my $exportable ( @{ $type->exportables } ) {
  14340         42500  
241 57369         111622 my $name = $exportable->{name};
242 57369         84789 my $code = $exportable->{code};
243 57369         81200 my $tags = $exportable->{tags};
244             _croak( 'Function %s is provided by types %s and %s', $name, $meta->{'functions'}{$name}{'type'}->name, $type->name )
245 57369 100       151041 if $meta->{'functions'}{$name};
246 57368         175264 *{"$class\::$name"} = set_subname( "$class\::$name", $code );
  57368         318737  
247 57368         108785 push @{"$class\::EXPORT_OK"}, $name;
  57368         163706  
248 57368   100     113315 push @{ ${"$class\::EXPORT_TAGS"}{$_} ||= [] }, $name for @$tags;
  57368         78660  
  57368         258185  
249 57368         472333 $meta->{'functions'}{$name} = { type => $type, tags => $tags };
250             }
251            
252             $INC{'Type/Registry.pm'}
253             ? 'Type::Registry'->for_class( $class )->add_type( $type, $name )
254 14339 100       112082 : ( $Type::Registry::DELAYED{$class}{$name} = $type );
255            
256 14339         59685 return $type;
257             } #/ sub add_type
258              
259             # For Type::TinyX::Facets
260             # Only use this if you know what you're doing!
261             sub _remove_type {
262 1     1   14 my $meta = shift->meta;
263 1         6 my $type = $meta->get_type( $_[0] );
264 1         3 my $class = ref $meta;
265            
266 1 50       2 _croak( 'Type library is immutable' ) if $meta->{immutable};
267            
268 1         5 delete $meta->{types}{$type->name};
269            
270 285     285   2475 no strict "refs";
  285         886  
  285         14315  
271 285     285   1692 no warnings "redefine", "prototype";
  285         645  
  285         201233  
272            
273 1         3 my @clean;
274             my $_scrub = sub {
275 8     8   11 my ( $arr, $name ) = @_;
276 8         194 @$arr = grep $_ ne $name, @$arr;
277 1         4 };
278 1         2 for my $exportable ( @{ $type->exportables } ) {
  1         4  
279 4         5 my $name = $exportable->{name};
280 4         5 push @clean, $name;
281 4         5 &$_scrub( \@{"$class\::EXPORT_OK"}, $name );
  4         12  
282 4         5 for my $t ( @{ $exportable->{tags} } ) {
  4         5  
283 4   50     5 &$_scrub( ${"$class\::EXPORT_TAGS"}{$t} ||= [], $name );
  4         13  
284             }
285 4         12 delete $meta->{'functions'}{$name};
286             }
287 1         16 eval {
288 1         4 require namespace::clean;
289 1         8 'namespace::clean'->clean_subroutines( $class, @clean );
290             };
291            
292             delete 'Type::Registry'->for_class( $class )->{$type->name}
293 1 50       150 if $INC{'Type/Registry.pm'};
294 1         5 delete $Type::Registry::DELAYED{$class}{$type->name};
295            
296 1         6 return $type;
297             } #/ sub _remove_type
298              
299             sub get_type {
300 4436     4436 1 16412 my $meta = shift->meta;
301 4436         26184 $meta->{types}{ $_[0] };
302             }
303              
304             sub has_type {
305 15448     15448 1 38404 my $meta = shift->meta;
306 15448         58285 exists $meta->{types}{ $_[0] };
307             }
308              
309             sub type_names {
310 123     123 1 413913 my $meta = shift->meta;
311 123         255 keys %{ $meta->{types} };
  123         3071  
312             }
313              
314             sub add_coercion {
315 1019     1019 1 3178 my $meta = shift->meta;
316 1019         2555 my $class = blessed( $meta );
317            
318 1019 50       3697 _croak( 'Type library is immutable' ) if $meta->{immutable};
319            
320 1019         7315 require Type::Coercion;
321 1019 100       8203 my $c = blessed( $_[0] ) ? $_[0] : "Type::Coercion"->new( @_ );
322 1019         3277 my $name = $c->name;
323            
324 1019 50       3570 _croak( 'Coercion %s already exists in this library', $name ) if $meta->has_coercion( $name );
325 1019 100       3497 _croak( 'Coercion %s conflicts with type of same name', $name ) if $meta->has_type( $name );
326 1018 50       3263 _croak( 'Cannot add anonymous type to a library' ) if $c->is_anon;
327            
328 1018   50     3611 $meta->{coercions} ||= {};
329 1018         3341 $meta->{coercions}{$name} = $c;
330            
331 285     285   2564 no strict "refs";
  285         602  
  285         20186  
332 285     285   1686 no warnings "redefine", "prototype";
  285         643  
  285         104086  
333            
334 1018         3624 *{"$class\::$name"} = type_to_coderef( $c );
  1018         8121  
335 1018         1963 push @{"$class\::EXPORT_OK"}, $name;
  1018         4061  
336 1018   100     1796 push @{ ${"$class\::EXPORT_TAGS"}{'coercions'} ||= [] }, $name;
  1018         1620  
  1018         6494  
337 1018         13891 $meta->{'functions'}{$name} = { coercion => $c, tags => [ 'coercions' ] };
338              
339 1018         4585 return $c;
340             } #/ sub add_coercion
341              
342             sub get_coercion {
343 144     144 1 501 my $meta = shift->meta;
344 144         679 $meta->{coercions}{ $_[0] };
345             }
346              
347             sub has_coercion {
348 15360     15360 1 29573 my $meta = shift->meta;
349 15360         44011 exists $meta->{coercions}{ $_[0] };
350             }
351              
352             sub coercion_names {
353 123     123 1 420 my $meta = shift->meta;
354 123         290 keys %{ $meta->{coercions} };
  123         1379  
355             }
356              
357             sub make_immutable {
358 406     406 1 1678 my $meta = shift->meta;
359 406         1206 my $class = ref( $meta );
360            
361 285     285   2384 no strict "refs";
  285         761  
  285         13648  
362 285     285   1816 no warnings "redefine", "prototype";
  285         738  
  285         61603  
363            
364 406         886 for my $type ( values %{ $meta->{types} } ) {
  406         3606  
365 13404         38536 $type->coercion->freeze;
366 13404 100 66     33802 next unless $type->has_coercion && $type->coercion->frozen;
367 784         3837 for my $e ( $type->exportables_by_tag( 'to' ) ) {
368 784         2467 my $qualified_name = $class . '::' . $e->{name};
369 784         3477 *$qualified_name = set_subname( $qualified_name, $e->{code} );
370             }
371             }
372            
373 406         6201 $meta->{immutable} = 1;
374             }
375              
376             1;
377              
378             __END__