File Coverage

blib/lib/Specio/Declare.pm
Criterion Covered Total %
statement 128 128 100.0
branch 86 90 95.5
condition 2 2 100.0
subroutine 23 23 100.0
pod 12 12 100.0
total 251 255 98.4


line stmt bran cond sub pod time code
1              
2             use strict;
3 30     30   553396 use warnings;
  30         115  
  30         778  
4 30     30   131  
  30         44  
  30         758  
5             use parent 'Exporter';
6 30     30   3237  
  30         2244  
  30         146  
7             our $VERSION = '0.48';
8              
9             use Carp qw( croak );
10 30     30   1844 use Specio::Coercion;
  30         49  
  30         1707  
11 30     30   10114 use Specio::Constraint::Simple;
  30         70  
  30         893  
12 30     30   10799 use Specio::DeclaredAt;
  30         72  
  30         882  
13 30     30   3850 use Specio::Helpers qw( install_t_sub _STRINGLIKE );
  30         52  
  30         717  
14 30     30   144 use Specio::Registry qw( internal_types_for_package register );
  30         50  
  30         1342  
15 30     30   2910  
  30         56  
  30         35730  
16             ## no critic (Modules::ProhibitAutomaticExportation)
17             our @EXPORT = qw(
18             anon
19             any_can_type
20             any_does_type
21             any_isa_type
22             coerce
23             declare
24             enum
25             intersection
26             object_can_type
27             object_does_type
28             object_isa_type
29             union
30             );
31             ## use critic
32              
33             my $package = shift;
34              
35 96     96   7035 my $caller = caller();
36              
37 96         184 $package->export_to_level( 1, $package, @_ );
38              
39 96         9124 install_t_sub(
40             $caller,
41 96         437 internal_types_for_package($caller)
42             );
43              
44             return;
45             }
46 96         7233  
47             my $name = _STRINGLIKE(shift)
48             or croak 'You must provide a name for declared types';
49             my %p = @_;
50 844 50   844 1 2122  
51             my $tc = _make_tc( name => $name, %p );
52 844         2072  
53             register( scalar caller(), $name, $tc, 'exportable' );
54 844         2025  
55             return $tc;
56 844         5160 }
57              
58 844         1736 return _make_tc(@_);
59             }
60              
61             my $name;
62 15     15 1 73 $name = shift if @_ % 2;
63             my %p = @_;
64              
65             require Specio::Constraint::Enum;
66 5     5 1 5248  
67 5 100       25 my $tc = _make_tc(
68 5         18 ( defined $name ? ( name => $name ) : () ),
69             values => $p{values},
70 5         1631 type_class => 'Specio::Constraint::Enum',
71             );
72              
73             register( scalar caller(), $name, $tc, 'exportable' )
74             if defined $name;
75 5 100       31  
76             return $tc;
77             }
78 5 100       51  
79             my $name;
80             $name = shift if @_ % 2;
81 5         14 my %p = @_;
82              
83             # This cannot be loaded earlier, since it loads Specio::Library::Builtins,
84             # which in turn wants to load Specio::Declare (the current module).
85 5     5 1 19308 require Specio::Constraint::ObjectCan;
86 5 100       28  
87 5         16 my $tc = _make_tc(
88             ( defined $name ? ( name => $name ) : () ),
89             methods => $p{methods},
90             type_class => 'Specio::Constraint::ObjectCan',
91 5         724 );
92              
93             register( scalar caller(), $name, $tc, 'exportable' )
94             if defined $name;
95              
96 5 100       34 return $tc;
97             }
98              
99 5 100       42 my $name;
100             $name = shift if @_ % 2;
101             my %p = @_;
102 5         14  
103             my $caller = scalar caller();
104              
105             # If we are being called repeatedly with a single argument, then we don't
106 8     8 1 8010 # want to blow up because the type has already been declared. This would
107 8 100       27 # force the user to use t() for all calls but the first, making their code
108 8         20 # pointlessly more complicated.
109             unless ( keys %p ) {
110 8         16 if ( my $exists = internal_types_for_package($caller)->{$name} ) {
111             return $exists;
112             }
113             }
114              
115             require Specio::Constraint::ObjectDoes;
116 8 100       31  
117 5 100       17 my $tc = _make_tc(
118 2         9 ( defined $name ? ( name => $name ) : () ),
119             role => ( defined $p{role} ? $p{role} : $name ),
120             type_class => 'Specio::Constraint::ObjectDoes',
121             );
122 6         1677  
123             register( scalar caller(), $name, $tc, 'exportable' )
124             if defined $name;
125              
126 6 100       36 return $tc;
    100          
127             }
128              
129             my $name;
130 6 100       49 $name = shift if @_ % 2;
131             my %p = @_;
132              
133 6         18 my $caller = scalar caller();
134             unless ( keys %p ) {
135             if ( my $exists = internal_types_for_package($caller)->{$name} ) {
136             return $exists;
137 5     5 1 8255 }
138 5 100       87 }
139 5         13  
140             require Specio::Constraint::ObjectIsa;
141 5         13  
142 5 100       16 my $tc = _make_tc(
143 4 100       14 ( defined $name ? ( name => $name ) : () ),
144 1         5 class => ( defined $p{class} ? $p{class} : $name ),
145             type_class => 'Specio::Constraint::ObjectIsa',
146             );
147              
148 4         1599 register( $caller, $name, $tc, 'exportable' )
149             if defined $name;
150              
151             return $tc;
152 4 100       28 }
    100          
153              
154             my $name;
155             $name = shift if @_ % 2;
156 4 100       32 my %p = @_;
157              
158             # This cannot be loaded earlier, since it loads Specio::Library::Builtins,
159 4         12 # which in turn wants to load Specio::Declare (the current module).
160             require Specio::Constraint::AnyCan;
161              
162             my $tc = _make_tc(
163 2     2 1 6501 ( defined $name ? ( name => $name ) : () ),
164 2 100       9 methods => $p{methods},
165 2         5 type_class => 'Specio::Constraint::AnyCan',
166             );
167              
168             register( scalar caller(), $name, $tc, 'exportable' )
169 2         1011 if defined $name;
170              
171             return $tc;
172             }
173              
174 2 100       14 my $name;
175             $name = shift if @_ % 2;
176             my %p = @_;
177 2 100       17  
178             my $caller = scalar caller();
179             unless ( keys %p ) {
180 2         120 if ( my $exists = internal_types_for_package($caller)->{$name} ) {
181             return $exists;
182             }
183             }
184 8     8 1 36579  
185 8 100       31 require Specio::Constraint::AnyDoes;
186 8         22  
187             my $tc = _make_tc(
188 8         17 ( defined $name ? ( name => $name ) : () ),
189 8 100       24 role => ( defined $p{role} ? $p{role} : $name ),
190 4 50       13 type_class => 'Specio::Constraint::AnyDoes',
191 4         14 );
192              
193             register( scalar caller(), $name, $tc, 'exportable' )
194             if defined $name;
195 4         1416  
196             return $tc;
197             }
198              
199 4 100       37 my $name;
    50          
200             $name = shift if @_ % 2;
201             my %p = @_;
202              
203 4 100       43 my $caller = scalar caller();
204             unless ( keys %p ) {
205             if ( my $exists = internal_types_for_package($caller)->{$name} ) {
206 4         12 return $exists;
207             }
208             }
209              
210 6     6 1 11551 require Specio::Constraint::AnyIsa;
211 6 100       29  
212 6         19 my $tc = _make_tc(
213             ( defined $name ? ( name => $name ) : () ),
214 6         16 class => ( defined $p{class} ? $p{class} : $name ),
215 6 100       22 type_class => 'Specio::Constraint::AnyIsa',
216 3 100       17 );
217 2         8  
218             register( scalar caller(), $name, $tc, 'exportable' )
219             if defined $name;
220              
221 4         1690 return $tc;
222             }
223              
224             my $name;
225 4 100       37 $name = shift if @_ % 2;
    100          
226             my %p = @_;
227              
228             require Specio::Constraint::Intersection;
229 4 100       40  
230             my $tc = _make_tc(
231             ( defined $name ? ( name => $name ) : () ),
232 4         13 %p,
233             type_class => 'Specio::Constraint::Intersection',
234             );
235              
236 4     4 1 59 register( scalar caller(), $name, $tc, 'exportable' )
237 4 100       18 if defined $name;
238 4         15  
239             return $tc;
240 4         382 }
241              
242 4 100       26 my $name;
243             $name = shift if @_ % 2;
244             my %p = @_;
245              
246             require Specio::Constraint::Union;
247              
248 4 100       38 my $tc = _make_tc(
249             ( defined $name ? ( name => $name ) : () ),
250             %p,
251 4         15 type_class => 'Specio::Constraint::Union',
252             );
253              
254             register( scalar caller(), $name, $tc, 'exportable' )
255 5     5 1 17 if defined $name;
256 5 100       17  
257 5         14 return $tc;
258             }
259 5         718  
260             my %p = @_;
261 5 100       28  
262             my $class = delete $p{type_class} || 'Specio::Constraint::Simple';
263              
264             $p{constraint} = delete $p{where} if exists $p{where};
265             $p{message_generator} = delete $p{message} if exists $p{message};
266             $p{inline_generator} = delete $p{inline} if exists $p{inline};
267 5 100       40  
268             return $class->new(
269             %p,
270 5         16 declared_at => Specio::DeclaredAt->new_from_caller(2),
271             );
272             }
273              
274 898     898   2004 my $to = shift;
275             my %p = @_;
276 898   100     2313  
277             $p{coercion} = delete $p{using} if exists $p{using};
278 898 100       1547 $p{inline_generator} = delete $p{inline} if exists $p{inline};
279 898 50       1474  
280 898 100       1734 return $to->add_coercion(
281             Specio::Coercion->new(
282 898         3010 to => $to,
283             %p,
284             declared_at => Specio::DeclaredAt->new_from_caller(1),
285             )
286             );
287             }
288              
289 16     16 1 21 1;
290 16         40  
291             # ABSTRACT: Specio declaration subroutines
292 16 100       39  
293 16 100       32  
294             =pod
295 16         60  
296             =encoding UTF-8
297              
298             =head1 NAME
299              
300             Specio::Declare - Specio declaration subroutines
301              
302             =head1 VERSION
303              
304             version 0.48
305              
306             =head1 SYNOPSIS
307              
308             package MyApp::Type::Library;
309              
310             use parent 'Specio::Exporter';
311              
312             use Specio::Declare;
313             use Specio::Library::Builtins;
314              
315             declare(
316             'Foo',
317             parent => t('Str'),
318             where => sub { $_[0] =~ /foo/i },
319             );
320              
321             declare(
322             'ArrayRefOfInt',
323             parent => t( 'ArrayRef', of => t('Int') ),
324             );
325              
326             my $even = anon(
327             parent => t('Int'),
328             inline => sub {
329             my $type = shift;
330             my $value_var = shift;
331              
332             return $value_var . ' % 2 == 0';
333             },
334             );
335              
336             coerce(
337             t('ArrayRef'),
338             from => t('Foo'),
339             using => sub { [ $_[0] ] },
340             );
341              
342             coerce(
343             $even,
344             from => t('Int'),
345             using => sub { $_[0] % 2 ? $_[0] + 1 : $_[0] },
346             );
347              
348             # Specio name is DateTime
349             any_isa_type('DateTime');
350              
351             # Specio name is DateTimeObject
352             object_isa_type( 'DateTimeObject', class => 'DateTime' );
353              
354             any_can_type(
355             'Duck',
356             methods => [ 'duck_walk', 'quack' ],
357             );
358              
359             object_can_type(
360             'DuckObject',
361             methods => [ 'duck_walk', 'quack' ],
362             );
363              
364             enum(
365             'Colors',
366             values => [qw( blue green red )],
367             );
368              
369             intersection(
370             'HashRefAndArrayRef',
371             of => [ t('HashRef'), t('ArrayRef') ],
372             );
373              
374             union(
375             'IntOrArrayRef',
376             of => [ t('Int'), t('ArrayRef') ],
377             );
378              
379             =head1 DESCRIPTION
380              
381             This package exports a set of type declaration helpers. Importing this package
382             also causes it to create a C<t> subroutine in the calling package.
383              
384             =head1 SUBROUTINES
385              
386             This module exports the following subroutines.
387              
388             =head2 t('name')
389              
390             This subroutine lets you access any types you have declared so far, as well as
391             any types you imported from another type library.
392              
393             If you pass an unknown name, it throws an exception.
394              
395             =head2 declare(...)
396              
397             This subroutine declares a named type. The first argument is the type name,
398             followed by a set of key/value parameters:
399              
400             =over 4
401              
402             =item * parent => $type
403              
404             The parent should be another type object. Specifically, it can be anything
405             which does the L<Specio::Constraint::Role::Interface> role. The parent can be a
406             named or anonymous type.
407              
408             =item * where => sub { ... }
409              
410             This is a subroutine which defines the type constraint. It will be passed a
411             single argument, the value to check, and it should return true or false to
412             indicate whether or not the value is valid for the type.
413              
414             This parameter is mutually exclusive with the C<inline> parameter.
415              
416             =item * inline => sub { ... }
417              
418             This is a subroutine that is called to generate inline code to validate the
419             type. Inlining can be I<much> faster than simply providing a subroutine with
420             the C<where> parameter, but is often more complicated to get right.
421              
422             The inline generator is called as a method on the type with one argument. This
423             argument is a I<string> containing the variable name to use in the generated
424             code. Typically this is something like C<'$_[0]'> or C<'$value'>.
425              
426             The inline generator subroutine should return a I<string> of code representing
427             a single term, and it I<should not> be terminated with a semicolon. This allows
428             the inlined code to be safely included in an C<if> statement, for example. You
429             can use C<do { }> blocks and ternaries to get everything into one term. Do not
430             assign to the variable you are testing. This single term should evaluate to
431             true or false.
432              
433             The inline generator is expected to include code to implement both the current
434             type and all its parents. Typically, the easiest way to do this is to write a
435             subroutine something like this:
436              
437             sub {
438             my $self = shift;
439             my $var = shift;
440              
441             return $self->parent->inline_check($var)
442             . ' and more checking code goes here';
443             }
444              
445             Or, more concisely:
446              
447             sub { $_[0]->parent->inline_check( $_[1] ) . 'more code that checks $_[1]' }
448              
449             The C<inline> parameter is mutually exclusive with the C<where> parameter.
450              
451             =item * message_generator => sub { ... }
452              
453             A subroutine to generate an error message when the type check fails. The
454             default message says something like "Validation failed for type named Int
455             declared in package Specio::Library::Builtins
456             (.../Specio/blib/lib/Specio/Library/Builtins.pm) at line 147 in sub named
457             (eval) with value 1.1".
458              
459             You can override this to provide something more specific about the way the type
460             failed.
461              
462             The subroutine you provide will be called as a method on the type with two
463             arguments. The first is the description of the type (the bit in the message
464             above that starts with "type named Int ..." and ends with "... in sub named
465             (eval)". This description says what the thing is and where it was defined.
466              
467             The second argument is the value that failed the type check, after any
468             coercions that might have been applied.
469              
470             =back
471              
472             =head2 anon(...)
473              
474             This subroutine declares an anonymous type. It is identical to C<declare>
475             except that it expects a list of key/value parameters without a type name as
476             the first parameter.
477              
478             =head2 coerce(...)
479              
480             This declares a coercion from one type to another. The first argument should be
481             an object which does the L<Specio::Constraint::Role::Interface> role. This can
482             be either a named or anonymous type. This type is the type that the coercion is
483             I<to>.
484              
485             The remaining arguments are key/value parameters:
486              
487             =over 4
488              
489             =item * from => $type
490              
491             This must be an object which does the L<Specio::Constraint::Role::Interface>
492             role. This is type that we are coercing I<from>. Again, this can be either a
493             named or anonymous type.
494              
495             =item * using => sub { ... }
496              
497             This is a subroutine which defines the type coercion. It will be passed a
498             single argument, the value to coerce. It should return a new value of the type
499             this coercion is to.
500              
501             This parameter is mutually exclusive with the C<inline> parameter.
502              
503             =item * inline => sub { ... }
504              
505             This is a subroutine that is called to generate inline code to perform the
506             coercion.
507              
508             The inline generator is called as a method on the type with one argument. This
509             argument is a I<string> containing the variable name to use in the generated
510             code. Typically this is something like C<'$_[0]'> or C<'$value'>.
511              
512             The inline generator subroutine should return a I<string> of code representing
513             a single term, and it I<should not> be terminated with a semicolon. This allows
514             the inlined code to be safely included in an C<if> statement, for example. You
515             can use C<do { }> blocks and ternaries to get everything into one term. This
516             single term should evaluate to the new value.
517              
518             =back
519              
520             =head1 DECLARATION HELPERS
521              
522             This module also exports some helper subs for declaring certain kinds of types:
523              
524             =head2 any_isa_type, object_isa_type
525              
526             The C<any_isa_type> helper creates a type which accepts a class name or object
527             of the given class. The C<object_isa_type> helper creates a type which only
528             accepts an object of the given class.
529              
530             These subroutines take a type name as the first argument. The remaining
531             arguments are key/value pairs. Currently this is just the C<class> key, which
532             should be a class name. This is the class that the type requires.
533              
534             The type name argument can be omitted to create an anonymous type.
535              
536             You can also pass just a single argument, in which case that will be used as
537             both the type's name and the class for the constraint to check.
538              
539             =head2 any_does_type, object_does_type
540              
541             The C<any_does_type> helper creates a type which accepts a class name or object
542             which does the given role. The C<object_does_type> helper creates a type which
543             only accepts an object which does the given role.
544              
545             These subroutines take a type name as the first argument. The remaining
546             arguments are key/value pairs. Currently this is just the C<role> key, which
547             should be a role name. This is the class that the type requires.
548              
549             This should just work (I hope) with roles created by L<Moose>, L<Mouse>, and
550             L<Moo> (using L<Role::Tiny>).
551              
552             The type name argument can be omitted to create an anonymous type.
553              
554             You can also pass just a single argument, in which case that will be used as
555             both the type's name and the role for the constraint to check.
556              
557             =head2 any_can_type, object_can_type
558              
559             The C<any_can_type> helper creates a type which accepts a class name or object
560             with the given methods. The C<object_can_type> helper creates a type which only
561             accepts an object with the given methods.
562              
563             These subroutines take a type name as the first argument. The remaining
564             arguments are key/value pairs. Currently this is just the C<methods> key, which
565             can be either a string or array reference of strings. These strings are the
566             required methods for the type.
567              
568             The type name argument can be omitted to create an anonymous type.
569              
570             =head2 enum
571              
572             This creates a type which accepts a string matching a given list of acceptable
573             values.
574              
575             The first argument is the type name. The remaining arguments are key/value
576             pairs. Currently this is just the C<values> key. This should an array reference
577             of acceptable string values.
578              
579             The type name argument can be omitted to create an anonymous type.
580              
581             =head2 intersection
582              
583             This creates a type which is the intersection of two or more other types. A
584             union only accepts values which match all of its underlying types.
585              
586             The first argument is the type name. The remaining arguments are key/value
587             pairs. Currently this is just the C<of> key. This should an array reference of
588             types.
589              
590             The type name argument can be omitted to create an anonymous type.
591              
592             =head2 union
593              
594             This creates a type which is the union of two or more other types. A union
595             accepts any of its underlying types.
596              
597             The first argument is the type name. The remaining arguments are key/value
598             pairs. Currently this is just the C<of> key. This should an array reference of
599             types.
600              
601             The type name argument can be omitted to create an anonymous type.
602              
603             =head1 PARAMETERIZED TYPES
604              
605             You can create a parameterized type by calling C<t> with additional parameters,
606             like this:
607              
608             my $arrayref_of_int = t( 'ArrayRef', of => t('Int') );
609              
610             my $arrayref_of_hashref_of_int = t(
611             'ArrayRef',
612             of => t(
613             'HashRef',
614             of => t('Int'),
615             ),
616             );
617              
618             The C<t> subroutine assumes that if it receives more than one argument, it
619             should look up the named type and call C<< $type->parameterize(...) >> with the
620             additional arguments.
621              
622             If the named type cannot be parameterized, it throws an error.
623              
624             You can also call C<< $type->parameterize >> directly if needed. See
625             L<Specio::Constraint::Parameterizable> for details.
626              
627             =head1 SUPPORT
628              
629             Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
630              
631             =head1 SOURCE
632              
633             The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
634              
635             =head1 AUTHOR
636              
637             Dave Rolsky <autarch@urth.org>
638              
639             =head1 COPYRIGHT AND LICENSE
640              
641             This software is Copyright (c) 2012 - 2022 by Dave Rolsky.
642              
643             This is free software, licensed under:
644              
645             The Artistic License 2.0 (GPL Compatible)
646              
647             The full text of the license can be found in the
648             F<LICENSE> file included with this distribution.
649              
650             =cut