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