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 247     247   432146 use 5.008001;
  247         966  
4 247     247   1441 use strict;
  247         512  
  247         6214  
5 247     247   1319 use warnings;
  247         631  
  247         11741  
6              
7             BEGIN {
8 247     247   906 $Type::Library::AUTHORITY = 'cpan:TOBYINK';
9 247         11468 $Type::Library::VERSION = '2.002001';
10             }
11              
12             $Type::Library::VERSION =~ tr/_//d;
13              
14 247     247   96495 use Eval::TypeTiny qw< eval_closure set_subname type_to_coderef NICE_PROTOTYPES >;
  247         699  
  247         1935  
15 247     247   162170 use Scalar::Util qw< blessed refaddr >;
  247         545  
  247         14405  
16 247     247   62414 use Type::Tiny ();
  247         654  
  247         5234  
17 247     247   1441 use Types::TypeTiny ();
  247         494  
  247         229493  
18              
19             require Exporter::Tiny;
20             our @ISA = 'Exporter::Tiny';
21              
22 8     8   2128 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 983     983   1530717 my ( $class, $opts ) = ( shift, @_ );
32            
33 369         1952 $class->setup_type_library( @{$opts}{qw/ into utils extends /}, $opts )
34 983 100 100     7187 if $_[0]{base} || $_[0]{extends};
35            
36 983         5112 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 18175     18175   766396 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 18175 100 66     44147 if ( $name =~ /^\+(.+)/ and $class->has_type( "$1" ) ) {
53 9         43 my $type = $class->get_type( "$1" );
54 9         37 my $exported = $type->exportables;
55             return map $class->_exporter_expand_sub(
56             $_->{name},
57 9 50       64 +{ %{ $value || {} } },
  42         13327  
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 18166 100 100     33588 if ( my $f = $class->meta->{'functions'}{$name}
67             and defined $class->meta->{'functions'}{$name}{'type'} ) {
68            
69 17308         35126 my $type = $f->{type};
70 17308         40876 my $tag = $f->{tags}[0];
71 17308         39840 my $typename = $type->name;
72            
73             # If $value has `of` or `where` options, then this is a
74             # custom type.
75             #
76 17308         26302 my $custom_type = 0;
77 17308         27825 for my $param ( qw/ of where / ) {
78 34616 100       64798 exists $value->{$param} or next;
79 3 50       25 defined $value->{-as} or _croak( "Parameter '-as' not supplied" );
80 3         12 $type = $type->$param( $value->{$param} );
81 3         7 $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 17308 100       32672 if ( $tag eq 'types' ) {
90 8654         11374 my $post_method = q();
91 8654 50       15960 $post_method = '->mouse_type' if $globals->{mouse};
92 8654 50       15004 $post_method = '->moose_type' if $globals->{moose};
93 8654 100 66     26197 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 17306 100 66     37162 if ( $custom_type and $tag ne 'types' ) {
105 1         3 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 18163         49388 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 18164     18164   670193 my ( $class, $name, $value, $globals, $sym ) = ( shift, @_ );
120            
121 18164         28559 my $into = $globals->{into};
122 18164         32619 my $type = $class->meta->{'functions'}{$name}{'type'};
123 18164         33166 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 18164 50 100     55188 ) 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 18164 100 66     108507 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 8089         24006 my ( $prefix ) = grep defined, $value->{-prefix}, $globals->{prefix}, q();
144 8089         18835 my ( $suffix ) = grep defined, $value->{-suffix}, $globals->{suffix}, q();
145 8089   66     25705 my $as = $prefix . ( $value->{-as} || $name ) . $suffix;
146            
147             $INC{'Type/Registry.pm'}
148             ? 'Type::Registry'->for_class( $into )->add_type( $type, $as )
149 8089 100       24580 : ( $Type::Registry::DELAYED{$into}{$as} = $type );
150             }
151            
152 18164         45640 $class->SUPER::_exporter_install_sub( @_ );
153             } #/ sub _exporter_install_sub
154              
155             sub _exporter_fail {
156 720     720   12570 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 720 100       1601 if ( $globals->{declare} ) {
161             return (
162             $name,
163             type_to_coderef(
164             undef,
165             type_name => $name,
166 718   33     2333 type_library => $globals->{into} || _croak( "Parameter 'into' not supplied" ),
167             ),
168             );
169             } #/ if ( $globals->{declare...})
170            
171 2         11 return $class->SUPER::_exporter_fail( @_ );
172             } #/ sub _exporter_fail
173              
174             ####
175             #### Type library functionality
176             ####
177              
178             sub setup_type_library {
179 369     369 1 1011 my ( $class, $type_library, $install_utils, $extends, $opts ) = ( shift, @_ );
180            
181 369 50       1582 my @extends = ref( $extends ) ? @$extends : $extends ? $extends : ();
    100          
182 369 100       1216 unshift @extends, $class if $class ne __PACKAGE__;
183            
184 369 50       1141 if ( not ref $type_library ) {
185 247     247   2058 no strict "refs";
  247         713  
  247         54210  
186 369         662 push @{"$type_library\::ISA"}, $class;
  369         4031  
187 369         2013 ( my $file = $type_library ) =~ s{::}{/}g;
188 369   100     1879 $INC{"$file.pm"} ||= __FILE__;
189             }
190            
191 369 100       1032 if ( $install_utils ) {
192 4         1640 require Type::Utils;
193 4         67 'Type::Utils'->import(
194             { %$opts, into => $type_library },
195             '-default',
196             );
197             }
198            
199 369 100 66     9184 if ( @extends and not ref $type_library ) {
200 15         7664 require Type::Utils;
201 15         1051 my $wrapper = eval "sub { package $type_library; &Type::Utils::extends; }";
202 15         319 $wrapper->( @extends );
203             }
204             }
205              
206             sub meta {
207 247     247   2037 no strict "refs";
  247         656  
  247         9250  
208 247     247   1653 no warnings "once";
  247         625  
  247         79325  
209 113574 100   113574 1 290239 return $_[0] if blessed $_[0];
210 75510   100     90443 ${"$_[0]\::META"} ||= bless {}, $_[0];
  75510         295984  
211             }
212              
213             sub add_type {
214 11392     11392 1 35441 my $meta = shift->meta;
215 11392         28683 my $class = blessed( $meta ) ;
216            
217 11392 100       27247 _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 11388 100       50771 ref( $_[0] ) eq q(HASH) ? 'Type::Tiny'->new( library => $class, %{ $_[0] } ) :
  8917 50       51731  
    100          
223             "Type::Tiny"->new( library => $class, @_ );
224 11388         29100 my $name = $type->{name};
225            
226 11388 100       26936 if ( $meta->has_type( $name ) ) {
227 1         22 my $existing = $meta->get_type( $name );
228 1 50       4 return if $type->{uniq} == $existing->{uniq};
229 1         4 _croak( 'Type %s already exists in this library', $name );
230             }
231            
232 11387 50       26559 _croak( 'Type %s conflicts with coercion of same name', $name ) if $meta->has_coercion( $name );
233 11387 100       31133 _croak( 'Cannot add anonymous type to a library' ) if $type->is_anon;
234 11386   50     26691 $meta->{types} ||= {};
235 11386         28440 $meta->{types}{$name} = $type;
236            
237 247     247   2043 no strict "refs";
  247         627  
  247         10494  
238 247     247   1561 no warnings "redefine", "prototype";
  247         619  
  247         88536  
239            
240 11386         15346 for my $exportable ( @{ $type->exportables } ) {
  11386         25426  
241 45553         76755 my $name = $exportable->{name};
242 45553         58245 my $code = $exportable->{code};
243 45553         56832 my $tags = $exportable->{tags};
244             _croak( 'Function %s is provided by types %s and %s', $name, $meta->{'functions'}{$name}{'type'}->name, $type->name )
245 45553 100       92690 if $meta->{'functions'}{$name};
246 45552         117398 *{"$class\::$name"} = set_subname( "$class\::$name", $code );
  45552         199435  
247 45552         72215 push @{"$class\::EXPORT_OK"}, $name;
  45552         110041  
248 45552   100     78935 push @{ ${"$class\::EXPORT_TAGS"}{$_} ||= [] }, $name for @$tags;
  45552         52903  
  45552         169768  
249 45552         283075 $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 11385 100       58364 : ( $Type::Registry::DELAYED{$class}{$name} = $type );
255            
256 11385         34371 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   15 my $meta = shift->meta;
263 1         5 my $type = $meta->get_type( $_[0] );
264 1         4 my $class = ref $meta;
265            
266 1 50       5 _croak( 'Type library is immutable' ) if $meta->{immutable};
267            
268 1         6 delete $meta->{types}{$type->name};
269            
270 247     247   2082 no strict "refs";
  247         647  
  247         10396  
271 247     247   1959 no warnings "redefine", "prototype";
  247         552  
  247         139932  
272            
273 1         2 my @clean;
274             my $_scrub = sub {
275 8     8   14 my ( $arr, $name ) = @_;
276 8         193 @$arr = grep $_ ne $name, @$arr;
277 1         5 };
278 1         2 for my $exportable ( @{ $type->exportables } ) {
  1         4  
279 4         11 my $name = $exportable->{name};
280 4         5 push @clean, $name;
281 4         6 &$_scrub( \@{"$class\::EXPORT_OK"}, $name );
  4         16  
282 4         8 for my $t ( @{ $exportable->{tags} } ) {
  4         6  
283 4   50     9 &$_scrub( ${"$class\::EXPORT_TAGS"}{$t} ||= [], $name );
  4         19  
284             }
285 4         15 delete $meta->{'functions'}{$name};
286             }
287 1         16 eval {
288 1         6 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       235 if $INC{'Type/Registry.pm'};
294 1         4 delete $Type::Registry::DELAYED{$class}{$type->name};
295            
296 1         5 return $type;
297             } #/ sub _remove_type
298              
299             sub get_type {
300 3164     3164 1 9224 my $meta = shift->meta;
301 3164         14594 $meta->{types}{ $_[0] };
302             }
303              
304             sub has_type {
305 12318     12318 1 25429 my $meta = shift->meta;
306 12318         40147 exists $meta->{types}{ $_[0] };
307             }
308              
309             sub type_names {
310 75     75 1 357 my $meta = shift->meta;
311 75         152 keys %{ $meta->{types} };
  75         1309  
312             }
313              
314             sub add_coercion {
315 851     851 1 2439 my $meta = shift->meta;
316 851         2545 my $class = blessed( $meta );
317            
318 851 50       2611 _croak( 'Type library is immutable' ) if $meta->{immutable};
319            
320 851         4933 require Type::Coercion;
321 851 100       6377 my $c = blessed( $_[0] ) ? $_[0] : "Type::Coercion"->new( @_ );
322 851         2278 my $name = $c->name;
323            
324 851 50       2374 _croak( 'Coercion %s already exists in this library', $name ) if $meta->has_coercion( $name );
325 851 100       2481 _croak( 'Coercion %s conflicts with type of same name', $name ) if $meta->has_type( $name );
326 850 50       2634 _croak( 'Cannot add anonymous type to a library' ) if $c->is_anon;
327            
328 850   50     3877 $meta->{coercions} ||= {};
329 850         2370 $meta->{coercions}{$name} = $c;
330            
331 247     247   1948 no strict "refs";
  247         633  
  247         9895  
332 247     247   1613 no warnings "redefine", "prototype";
  247         643  
  247         65933  
333            
334 850         2389 *{"$class\::$name"} = type_to_coderef( $c );
  850         5776  
335 850         1787 push @{"$class\::EXPORT_OK"}, $name;
  850         2919  
336 850   100     1512 push @{ ${"$class\::EXPORT_TAGS"}{'coercions'} ||= [] }, $name;
  850         1288  
  850         5807  
337 850         8784 $meta->{'functions'}{$name} = { coercion => $c, tags => [ 'coercions' ] };
338              
339 850         3877 return $c;
340             } #/ sub add_coercion
341              
342             sub get_coercion {
343 96     96 1 277 my $meta = shift->meta;
344 96         405 $meta->{coercions}{ $_[0] };
345             }
346              
347             sub has_coercion {
348 12238     12238 1 20446 my $meta = shift->meta;
349 12238         33021 exists $meta->{coercions}{ $_[0] };
350             }
351              
352             sub coercion_names {
353 75     75 1 253 my $meta = shift->meta;
354 75         160 keys %{ $meta->{coercions} };
  75         758  
355             }
356              
357             sub make_immutable {
358 316     316 1 1098 my $meta = shift->meta;
359 316         1389 my $class = ref( $meta );
360            
361 247     247   2017 no strict "refs";
  247         629  
  247         10251  
362 247     247   1663 no warnings "redefine", "prototype";
  247         676  
  247         44988  
363            
364 316         724 for my $type ( values %{ $meta->{types} } ) {
  316         2346  
365 10450         24693 $type->coercion->freeze;
366 10450 100 66     21955 next unless $type->has_coercion && $type->coercion->frozen;
367 520         2931 for my $e ( $type->exportables_by_tag( 'to' ) ) {
368 520         2045 my $qualified_name = $class . '::' . $e->{name};
369 520         1772 *$qualified_name = set_subname( $qualified_name, $e->{code} );
370             }
371             }
372            
373 316         3916 $meta->{immutable} = 1;
374             }
375              
376             1;
377              
378             __END__