File Coverage

lib/Type/Registry.pm
Criterion Covered Total %
statement 161 162 100.0
branch 43 54 79.6
condition 22 39 56.4
subroutine 34 35 97.1
pod 17 17 100.0
total 277 307 90.5


line stmt bran cond sub pod time code
1             package Type::Registry;
2              
3 31     31   21313 use 5.008001;
  31         125  
4 31     31   177 use strict;
  31         60  
  31         741  
5 31     31   167 use warnings;
  31         68  
  31         1418  
6              
7             BEGIN {
8 31     31   138 $Type::Registry::AUTHORITY = 'cpan:TOBYINK';
9 31         1250 $Type::Registry::VERSION = '2.003_000';
10             }
11              
12             $Type::Registry::VERSION =~ tr/_//d;
13              
14 31     31   199 use Exporter::Tiny qw( mkopt );
  31         98  
  31         345  
15 31     31   10080 use Scalar::Util qw( refaddr );
  31         111  
  31         1811  
16 31     31   12574 use Type::Parser qw( eval_type );
  31         88  
  31         316  
17 31     31   8090 use Types::TypeTiny ();
  31         88  
  31         14295  
18              
19             our @ISA = 'Exporter::Tiny';
20             our @EXPORT_OK = qw(t);
21              
22 4     4   34 sub _croak ($;@) { require Error::TypeTiny; goto \&Error::TypeTiny::croak }
  4         26  
23              
24             sub _generate_t {
25 12     12   1719 my $class = shift;
26 12         33 my ( $name, $value, $globals ) = @_;
27            
28 12         31 my $caller = $globals->{into};
29 12 100       141 my $reg = $class->for_class(
30             ref( $caller ) ? sprintf( 'HASH(0x%08X)', refaddr( $caller ) ) : $caller );
31            
32 12 100   58   97 sub (;$) { @_ ? $reg->lookup( @_ ) : $reg };
  58         12008  
33             } #/ sub _generate_t
34              
35             sub new {
36 100     100 1 1877 my $class = shift;
37 100 50       282 ref( $class ) and _croak( "Not an object method" );
38 100         605 bless {}, $class;
39             }
40              
41             {
42             my %registries;
43            
44             sub for_class {
45 806     806 1 1978 my $class = shift;
46 806         1515 my ( $for ) = @_;
47 806   66     3438 $registries{$for} ||= $class->new;
48             }
49            
50             sub for_me {
51 8     8 1 92 my $class = shift;
52 8         22 my $for = caller;
53 8   33     78 $registries{$for} ||= $class->new;
54             }
55             }
56              
57             sub add_types {
58 17     17 1 74 my $self = shift;
59 17         98 my $opts = mkopt( \@_ );
60 17         420 for my $opt ( @$opts ) {
61 18         70 my ( $library, $types ) = @$opt;
62 18         84 $library =~ s/^-/Types::/;
63            
64             {
65 18     1   35 local $SIG{__DIE__} = sub { };
  18         154  
66 18         1072 eval "require $library";
67             };
68            
69 18         65165 my %hash;
70            
71 18 100 66     329 if ( $library->isa( "Type::Library" ) or $library eq 'Types::TypeTiny' ) {
    100 66        
    100          
    100          
72 14   100     141 $types ||= [qw/-types/];
73 14 50       175 Types::TypeTiny::is_ArrayLike( $types )
74             or _croak(
75             "Expected arrayref following '%s'; got %s", $library,
76             $types
77             );
78            
79 14         158 $library->import( { into => \%hash }, @$types );
80 14         753 $hash{$_} = &{ $hash{$_} }() for keys %hash;
  527         4515  
81             } #/ if ( $library->isa( "Type::Library"...))
82             elsif ( $library->isa( "Exporter" )
83 31     31   271 and my $type_tag = do { no strict 'refs'; ${"$library\::EXPORT_TAGS"}{'types'} } ) {
  31         84  
  31         51334  
  1         2  
  1         7  
84 1   33     5 $types ||= $type_tag;
85 1         6 $hash{$_} = $library->$_ for @$types;
86             }
87             elsif ( $library->isa( "MooseX::Types::Base" ) ) {
88 1   50     11 $types ||= [];
89 1 50 33     15 Types::TypeTiny::is_ArrayLike( $types ) && ( @$types == 0 )
90             or _croak(
91             "Library '%s' is a MooseX::Types type constraint library. No import options currently supported",
92             $library
93             );
94            
95 1         10 require Moose::Util::TypeConstraints;
96 1         6 my $moosextypes = $library->type_storage;
97 1         24 for my $name ( sort keys %$moosextypes ) {
98             my $tt = Types::TypeTiny::to_TypeTiny(
99 9         32 Moose::Util::TypeConstraints::find_type_constraint( $moosextypes->{$name} ) );
100 9         24 $hash{$name} = $tt;
101             }
102             } #/ elsif ( $library->isa( "MooseX::Types::Base"...))
103             elsif ( $library->isa( "MouseX::Types::Base" ) ) {
104 1   50     7 $types ||= [];
105 1 50 33     8 Types::TypeTiny::is_ArrayLike( $types ) && ( @$types == 0 )
106             or _croak(
107             "Library '%s' is a MouseX::Types type constraint library. No import options currently supported",
108             $library
109             );
110            
111 1         6 require Mouse::Util::TypeConstraints;
112 1         4 my $moosextypes = $library->type_storage;
113 1         10 for my $name ( sort keys %$moosextypes ) {
114             my $tt = Types::TypeTiny::to_TypeTiny(
115 5         15 Mouse::Util::TypeConstraints::find_type_constraint( $moosextypes->{$name} ) );
116 5         12 $hash{$name} = $tt;
117             }
118             } #/ elsif ( $library->isa( "MouseX::Types::Base"...))
119             else {
120 1         6 _croak( "%s is not a type library", $library );
121             }
122            
123 17         386 for my $key ( sort keys %hash ) {
124             exists( $self->{$key} )
125             and $self->{$key}{uniq} != $hash{$key}{uniq}
126 544 50 66     1048 and _croak( "Duplicate type name: %s", $key );
127 544         957 $self->{$key} = $hash{$key};
128             }
129             } #/ for my $opt ( @$opts )
130 16         73 $self;
131             } #/ sub add_types
132              
133             sub add_type {
134 1727     1727 1 2250 my $self = shift;
135 1727         2938 my ( $type, $name ) = @_;
136 1727         3278 $type = Types::TypeTiny::to_TypeTiny( $type );
137 1727   100     3117 $name ||= do {
138 4 100       20 $type->is_anon
139             and
140             _croak( "Expected named type constraint; got anonymous type constraint" );
141 3         13 $type->name;
142             };
143            
144             exists( $self->{$name} )
145             and $self->{$name}{uniq} != $type->{uniq}
146 1726 50 66     3573 and _croak( "Duplicate type name: %s", $name );
147            
148 1726         2863 $self->{$name} = $type;
149 1726         2720 $self;
150             } #/ sub add_type
151              
152             sub alias_type {
153 5     5 1 14 my $self = shift;
154 5         16 my ( $old, @new ) = @_;
155 5 100       8 my $lookup = eval { $self->lookup( $old ) }
  5         26  
156             or _croak( "Expected existing type constraint name; got '$old'" );
157 4         16 $self->{$_} = $lookup for @new;
158 4         11 $self;
159             }
160              
161             sub simple_lookup {
162 359     359 1 611 my $self = shift;
163            
164 359         708 my ( $tc ) = @_;
165 359         1619 $tc =~ s/(^\s+|\s+$)//g;
166            
167 359 100       1259 if ( exists $self->{$tc} ) {
    100          
168 168         530 return $self->{$tc};
169             }
170             elsif ( $self->has_parent ) {
171 2         5 return $self->get_parent->simple_lookup( @_ );
172             }
173            
174 189         588 return;
175             } #/ sub simple_lookup
176              
177             sub set_parent {
178 1     1 1 3 my $self = shift;
179 1 50 33     8 $self->{'~~parent'} =
180             ref( $_[0] )
181             ? $_[0]
182             : ( ref( $self ) || $self )->for_class( $_[0] );
183 1         3 $self;
184             }
185              
186             sub clear_parent {
187 1     1 1 211 my $self = shift;
188 1         2 delete $self->{'~~parent'};
189 1         2 $self;
190             }
191              
192             sub has_parent {
193 191     191 1 758 !!ref( shift->{'~~parent'} );
194             }
195              
196             sub get_parent {
197 3     3 1 14 shift->{'~~parent'};
198             }
199              
200             sub foreign_lookup {
201 17     17 1 43 my $self = shift;
202            
203 17 50       98 return $_[1] ? () : $self->simple_lookup( $_[0], 1 )
    100          
204             unless $_[0] =~ /^(.+)::(\w+)$/;
205            
206 14         50 my $library = $1;
207 14         39 my $typename = $2;
208            
209             {
210 14     6   37 local $SIG{__DIE__} = sub { };
  14         119  
211 14         1052 eval "require $library;";
212             };
213            
214 14 100       300665 if ( $library->isa( 'MooseX::Types::Base' ) ) {
215 4         38 require Moose::Util::TypeConstraints;
216 4 50       28 my $type = Moose::Util::TypeConstraints::find_type_constraint(
217             $library->get_type( $typename ) )
218             or return;
219 4         605 return Types::TypeTiny::to_TypeTiny( $type );
220             }
221            
222 10 100       65 if ( $library->isa( 'MouseX::Types::Base' ) ) {
223 3         21 require Mouse::Util::TypeConstraints;
224 3 50       32 my $sub = $library->can( $typename ) or return;
225 3 50       20 my $type = Mouse::Util::TypeConstraints::find_type_constraint( $sub->() )
226             or return;
227 3         158 return Types::TypeTiny::to_TypeTiny( $type );
228             }
229            
230 7 100       62 if ( $library->can( "get_type" ) ) {
231 1         5 my $type = $library->get_type( $typename );
232 1         3 return Types::TypeTiny::to_TypeTiny( $type );
233             }
234            
235 6         23 return;
236             } #/ sub foreign_lookup
237              
238             sub lookup {
239 132     132 1 346 my $self = shift;
240            
241 132 100       505 $self->simple_lookup( @_ ) or eval_type( $_[0], $self );
242             }
243              
244             sub make_union {
245 1     1 1 2 my $self = shift;
246 1         3 my ( @types ) = @_;
247            
248 1         412 require Type::Tiny::Union;
249 1         10 return "Type::Tiny::Union"->new( type_constraints => \@types );
250             }
251              
252             sub _make_union_by_overload {
253 39     39   74 my $self = shift;
254 39         110 my ( @types ) = @_;
255            
256 39         1418 require Type::Tiny::Union;
257 39         361 return "Type::Tiny::Union"->new_by_overload( type_constraints => \@types );
258             }
259              
260             sub make_intersection {
261 1     1 1 2 my $self = shift;
262 1         3 my ( @types ) = @_;
263            
264 1         494 require Type::Tiny::Intersection;
265 1         6 return "Type::Tiny::Intersection"->new( type_constraints => \@types );
266             }
267              
268             sub _make_intersection_by_overload {
269 8     8   12 my $self = shift;
270 8         17 my ( @types ) = @_;
271            
272 8         41 require Type::Tiny::Intersection;
273 8         26 return "Type::Tiny::Intersection"->new_by_overload( type_constraints => \@types );
274             }
275              
276             sub make_class_type {
277 18     18 1 36 my $self = shift;
278 18         40 my ( $class ) = @_;
279            
280 18         86 require Types::Standard;
281 18         70 return Types::Standard::InstanceOf()->of( $class );
282             }
283              
284             sub make_role_type {
285 3     3 1 8 my $self = shift;
286 3         8 my ( $role ) = @_;
287            
288 3         13 require Types::Standard;
289 3         15 return Types::Standard::ConsumerOf()->of( $role );
290             }
291              
292             sub AUTOLOAD {
293 25     25   80 my $self = shift;
294 25         163 my ( $method ) = ( our $AUTOLOAD =~ /(\w+)$/ );
295 25         77 my $type = $self->simple_lookup( $method );
296 25 100       135 return $type if $type;
297 1         19 _croak(
298             q[Can't locate object method "%s" via package "%s"], $method,
299             ref( $self )
300             );
301             } #/ sub AUTOLOAD
302              
303             # Prevent AUTOLOAD being called for DESTROY!
304             sub DESTROY {
305 0     0     return; # uncoverable statement
306             }
307              
308             DELAYED: {
309             our %DELAYED;
310             for my $package ( sort keys %DELAYED ) {
311             my $reg = __PACKAGE__->for_class( $package );
312             my $types = $DELAYED{$package};
313            
314             for my $name ( sort keys %$types ) {
315             $reg->add_type( $types->{$name}, $name );
316             }
317             }
318             } #/ DELAYED:
319              
320             1;
321              
322             __END__
323              
324             =pod
325              
326             =encoding utf-8
327              
328             =for stopwords optlist
329              
330             =head1 NAME
331              
332             Type::Registry - a glorified hashref for looking up type constraints
333              
334             =head1 SYNOPSIS
335              
336             =for test_synopsis no warnings qw(misc);
337              
338             package Foo::Bar;
339            
340             use Type::Registry;
341            
342             my $reg = "Type::Registry"->for_me; # a registry for Foo::Bar
343            
344             # Register all types from Types::Standard
345             $reg->add_types(-Standard);
346            
347             # Register just one type from Types::XSD
348             $reg->add_types(-XSD => ["NonNegativeInteger"]);
349            
350             # Register all types from MyApp::Types
351             $reg->add_types("MyApp::Types");
352            
353             # Create a type alias
354             $reg->alias_type("NonNegativeInteger" => "Count");
355            
356             # Look up a type constraint
357             my $type = $reg->lookup("ArrayRef[Count]");
358            
359             $type->check([1, 2, 3.14159]); # croaks
360              
361             Alternatively:
362              
363             package Foo::Bar;
364            
365             use Type::Registry qw( t );
366            
367             # Register all types from Types::Standard
368             t->add_types(-Standard);
369            
370             # Register just one type from Types::XSD
371             t->add_types(-XSD => ["NonNegativeInteger"]);
372            
373             # Register all types from MyApp::Types
374             t->add_types("MyApp::Types");
375            
376             # Create a type alias
377             t->alias_type("NonNegativeInteger" => "Count");
378            
379             # Look up a type constraint
380             my $type = t("ArrayRef[Count]");
381            
382             $type->check([1, 2, 3.14159]); # croaks
383              
384             =head1 STATUS
385              
386             This module is covered by the
387             L<Type-Tiny stability policy|Type::Tiny::Manual::Policies/"STABILITY">.
388              
389             =head1 DESCRIPTION
390              
391             A type registry is basically just a hashref mapping type names to type
392             constraint objects.
393              
394             =head2 Constructors
395              
396             =over
397              
398             =item C<< new >>
399              
400             Create a new glorified hashref.
401              
402             =item C<< for_class($class) >>
403              
404             Create or return the existing glorified hashref associated with the given
405             class.
406              
407             Note that any type constraint you have imported from Type::Library-based
408             type libraries will be automatically available in your class' registry.
409              
410             =item C<< for_me >>
411              
412             Create or return the existing glorified hashref associated with the caller.
413              
414             =back
415              
416             =head2 Methods
417              
418             =over
419              
420             =item C<< add_types(@libraries) >>
421              
422             The libraries list is treated as an "optlist" (a la L<Data::OptList>).
423              
424             Strings are the names of type libraries; if the first character is a
425             hyphen, it is expanded to the "Types::" prefix. If followed by an
426             arrayref, this is the list of types to import from that library.
427             Otherwise, imports all types from the library.
428              
429             use Type::Registry qw(t);
430            
431             t->add_types(-Standard); # OR: t->add_types("Types::Standard");
432            
433             t->add_types(
434             -TypeTiny => ['HashLike'],
435             -Standard => ['HashRef' => { -as => 'RealHash' }],
436             );
437              
438             L<MooseX::Types> (and experimentally, L<MouseX::Types>) libraries can
439             also be added this way, but I<< cannot be followed by an arrayref of
440             types to import >>.
441              
442             =item C<< add_type($type, $name) >>
443              
444             The long-awaited singular form of C<add_types>. Given a type constraint
445             object, adds it to the registry with a given name. The name may be
446             omitted, in which case C<< $type->name >> is called, and Type::Registry
447             will throw an error if C<< $type >> is anonymous. If a name is explicitly
448             given, Type::Registry cares not one wit whether the type constraint is
449             anonymous.
450              
451             This method can even add L<MooseX::Types> and L<MouseX::Types> type
452             constraints; indeed anything that can be handled by L<Types::TypeTiny>'s
453             C<to_TypeTiny> function. (Bear in mind that to_TypeTiny I<always> results
454             in an anonymous type constraint, so C<< $name >> will be required.)
455              
456             =item C<< alias_type($oldname, $newname) >>
457              
458             Create an alias for an existing type.
459              
460             =item C<< simple_lookup($name) >>
461              
462             Look up a type in the registry by name.
463              
464             Returns undef if not found.
465              
466             =item C<< foreign_lookup($name) >>
467              
468             Like C<simple_lookup>, but if the type name contains "::", will attempt
469             to load it from a type library. (And will attempt to load that module.)
470              
471             =item C<< lookup($name) >>
472              
473             Look up by name, with a DSL.
474              
475             t->lookup("Int|ArrayRef[Int]")
476              
477             The DSL can be summed up as:
478              
479             X type from this registry
480             My::Lib::X type from a type library
481             ~X complementary type
482             X | Y union
483             X & Y intersection
484             X[...] parameterized type
485             slurpy X slurpy type
486             Foo::Bar:: class type
487              
488             Croaks if not found.
489              
490             =item C<< make_union(@constraints) >>,
491             C<< make_intersection(@constraints) >>,
492             C<< make_class_type($class) >>,
493             C<< make_role_type($role) >>
494              
495             Convenience methods for creating certain common type constraints.
496              
497             =item C<< AUTOLOAD >>
498              
499             Overloaded to call C<lookup>.
500              
501             $registry->Str; # like $registry->lookup("Str")
502              
503             =item C<get_parent>, C<< set_parent($reg) >>, C<< clear_parent >>, C<< has_parent >>
504              
505             Advanced stuff. Allows a registry to have a "parent" registry which it
506             inherits type constraints from.
507              
508             =back
509              
510             =head2 Functions
511              
512             =over
513              
514             =item C<< t >>
515              
516             This class can export a function C<< t >> which acts like
517             C<< "Type::Registry"->for_class($importing_class) >>.
518              
519             =back
520              
521             =head1 BUGS
522              
523             Please report any bugs to
524             L<https://github.com/tobyink/p5-type-tiny/issues>.
525              
526             =head1 SEE ALSO
527              
528             L<Type::Library>.
529              
530             =head1 AUTHOR
531              
532             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
533              
534             =head1 COPYRIGHT AND LICENCE
535              
536             This software is copyright (c) 2013-2014, 2017-2023 by Toby Inkster.
537              
538             This is free software; you can redistribute it and/or modify it under
539             the same terms as the Perl 5 programming language system itself.
540              
541             =head1 DISCLAIMER OF WARRANTIES
542              
543             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
544             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
545             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.