File Coverage

blib/lib/Specio/Declare.pm
Criterion Covered Total %
statement 129 132 97.7
branch 87 92 94.5
condition 3 5 60.0
subroutine 23 23 100.0
pod 12 12 100.0
total 254 264 96.2


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