File Coverage

blib/lib/Specio/Declare.pm
Criterion Covered Total %
statement 129 132 97.7
branch 84 92 91.3
condition 3 5 60.0
subroutine 23 23 100.0
pod 12 12 100.0
total 251 264 95.0


line stmt bran cond sub pod time code
1             package Specio::Declare;
2              
3 28     28   811748 use strict;
  28         728  
  28         822  
4 28     28   143 use warnings;
  28         44  
  28         717  
5              
6 28     28   3451 use parent 'Exporter';
  28         2208  
  28         167  
7              
8             our $VERSION = '0.46';
9              
10 28     28   1837 use Carp qw( croak );
  28         48  
  28         1223  
11 28     28   10978 use Specio::Coercion;
  28         68  
  28         871  
12 28     28   11052 use Specio::Constraint::Simple;
  28         65  
  28         856  
13 28     28   4295 use Specio::DeclaredAt;
  28         53  
  28         722  
14 28     28   143 use Specio::Helpers qw( install_t_sub _STRINGLIKE );
  28         50  
  28         1286  
15 28     28   3043 use Specio::Registry qw( internal_types_for_package register );
  28         52  
  28         38593  
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 92     92   8286 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 92 50 33     663 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 92         222 my $caller = caller();
66              
67 92         9406 $package->export_to_level( 1, $package, @_ );
68              
69 92         700 install_t_sub(
70             $caller,
71             internal_types_for_package($caller)
72             );
73              
74 92         7976 return;
75             }
76              
77             sub declare {
78 801 50   801 1 2071 my $name = _STRINGLIKE(shift)
79             or croak 'You must provide a name for declared types';
80 801         2105 my %p = @_;
81              
82 801         2016 my $tc = _make_tc( name => $name, %p );
83              
84 801         5403 register( scalar caller(), $name, $tc, 'exportable' );
85              
86 801         1848 return $tc;
87             }
88              
89             sub anon {
90 10     10 1 45 return _make_tc(@_);
91             }
92              
93             sub enum {
94 3     3 1 17880 my $name;
95 3 50       15 $name = shift if @_ % 2;
96 3         9 my %p = @_;
97              
98 3         1380 require Specio::Constraint::Enum;
99              
100             my $tc = _make_tc(
101             ( defined $name ? ( name => $name ) : () ),
102             values => $p{values},
103 3 50       25 type_class => 'Specio::Constraint::Enum',
104             );
105              
106 3 50       34 register( scalar caller(), $name, $tc, 'exportable' )
107             if defined $name;
108              
109 3         9 return $tc;
110             }
111              
112             sub object_can_type {
113 5     5 1 134592 my $name;
114 5 100       36 $name = shift if @_ % 2;
115 5         23 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         1007 require Specio::Constraint::ObjectCan;
120              
121             my $tc = _make_tc(
122             ( defined $name ? ( name => $name ) : () ),
123             methods => $p{methods},
124 5 100       38 type_class => 'Specio::Constraint::ObjectCan',
125             );
126              
127 5 100       58 register( scalar caller(), $name, $tc, 'exportable' )
128             if defined $name;
129              
130 5         20 return $tc;
131             }
132              
133             sub object_does_type {
134 7     7 1 49446 my $name;
135 7 100       32 $name = shift if @_ % 2;
136 7         22 my %p = @_;
137              
138 7         18 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 7 100       26 unless ( keys %p ) {
145 4 100       20 if ( my $exists = internal_types_for_package($caller)->{$name} ) {
146 2         10 return $exists;
147             }
148             }
149              
150 5         2091 require Specio::Constraint::ObjectDoes;
151              
152             my $tc = _make_tc(
153             ( defined $name ? ( name => $name ) : () ),
154 5 100       45 role => ( defined $p{role} ? $p{role} : $name ),
    100          
155             type_class => 'Specio::Constraint::ObjectDoes',
156             );
157              
158 5 100       52 register( scalar caller(), $name, $tc, 'exportable' )
159             if defined $name;
160              
161 5         18 return $tc;
162             }
163              
164             sub object_isa_type {
165 4     4 1 36951 my $name;
166 4 100       25 $name = shift if @_ % 2;
167 4         13 my %p = @_;
168              
169 4         11 my $caller = scalar caller();
170 4 100       18 unless ( keys %p ) {
171 3 100       17 if ( my $exists = internal_types_for_package($caller)->{$name} ) {
172 1         6 return $exists;
173             }
174             }
175              
176 3         1381 require Specio::Constraint::ObjectIsa;
177              
178             my $tc = _make_tc(
179             ( defined $name ? ( name => $name ) : () ),
180 3 100       30 class => ( defined $p{class} ? $p{class} : $name ),
    100          
181             type_class => 'Specio::Constraint::ObjectIsa',
182             );
183              
184 3 100       33 register( $caller, $name, $tc, 'exportable' )
185             if defined $name;
186              
187 3         10 return $tc;
188             }
189              
190             sub any_can_type {
191 2     2 1 23134 my $name;
192 2 100       170 $name = shift if @_ % 2;
193 2         8 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         1247 require Specio::Constraint::AnyCan;
198              
199             my $tc = _make_tc(
200             ( defined $name ? ( name => $name ) : () ),
201             methods => $p{methods},
202 2 100       17 type_class => 'Specio::Constraint::AnyCan',
203             );
204              
205 2 100       26 register( scalar caller(), $name, $tc, 'exportable' )
206             if defined $name;
207              
208 2         7 return $tc;
209             }
210              
211             sub any_does_type {
212 7     7 1 54878 my $name;
213 7 100       56 $name = shift if @_ % 2;
214 7         22 my %p = @_;
215              
216 7         19 my $caller = scalar caller();
217 7 100       24 unless ( keys %p ) {
218 3 50       11 if ( my $exists = internal_types_for_package($caller)->{$name} ) {
219 3         12 return $exists;
220             }
221             }
222              
223 4         1702 require Specio::Constraint::AnyDoes;
224              
225             my $tc = _make_tc(
226             ( defined $name ? ( name => $name ) : () ),
227 4 100       39 role => ( defined $p{role} ? $p{role} : $name ),
    50          
228             type_class => 'Specio::Constraint::AnyDoes',
229             );
230              
231 4 100       43 register( scalar caller(), $name, $tc, 'exportable' )
232             if defined $name;
233              
234 4         16 return $tc;
235             }
236              
237             sub any_isa_type {
238 5     5 1 66300 my $name;
239 5 100       30 $name = shift if @_ % 2;
240 5         24 my %p = @_;
241              
242 5         15 my $caller = scalar caller();
243 5 100       22 unless ( keys %p ) {
244 2 100       12 if ( my $exists = internal_types_for_package($caller)->{$name} ) {
245 1         6 return $exists;
246             }
247             }
248              
249 4         1798 require Specio::Constraint::AnyIsa;
250              
251             my $tc = _make_tc(
252             ( defined $name ? ( name => $name ) : () ),
253 4 100       39 class => ( defined $p{class} ? $p{class} : $name ),
    100          
254             type_class => 'Specio::Constraint::AnyIsa',
255             );
256              
257 4 100       48 register( scalar caller(), $name, $tc, 'exportable' )
258             if defined $name;
259              
260 4         15 return $tc;
261             }
262              
263             sub intersection {
264 3     3 1 14 my $name;
265 3 100       13 $name = shift if @_ % 2;
266 3         10 my %p = @_;
267              
268 3         30 require Specio::Constraint::Intersection;
269              
270 3 100       16 my $tc = _make_tc(
271             ( defined $name ? ( name => $name ) : () ),
272             %p,
273             type_class => 'Specio::Constraint::Intersection',
274             );
275              
276 3 100       51 register( scalar caller(), $name, $tc, 'exportable' )
277             if defined $name;
278              
279 3         11 return $tc;
280             }
281              
282             sub union {
283 4     4 1 15 my $name;
284 4 100       15 $name = shift if @_ % 2;
285 4         16 my %p = @_;
286              
287 4         416 require Specio::Constraint::Union;
288              
289 4 100       25 my $tc = _make_tc(
290             ( defined $name ? ( name => $name ) : () ),
291             %p,
292             type_class => 'Specio::Constraint::Union',
293             );
294              
295 4 100       37 register( scalar caller(), $name, $tc, 'exportable' )
296             if defined $name;
297              
298 4         12 return $tc;
299             }
300              
301             sub _make_tc {
302 844     844   2056 my %p = @_;
303              
304 844   100     2354 my $class = delete $p{type_class} || 'Specio::Constraint::Simple';
305              
306 844 100       1627 $p{constraint} = delete $p{where} if exists $p{where};
307 844 50       1471 $p{message_generator} = delete $p{message} if exists $p{message};
308 844 100       1706 $p{inline_generator} = delete $p{inline} if exists $p{inline};
309              
310 844         3001 return $class->new(
311             %p,
312             declared_at => Specio::DeclaredAt->new_from_caller(2),
313             );
314             }
315              
316             sub coerce {
317 16     16 1 25 my $to = shift;
318 16         48 my %p = @_;
319              
320 16 100       48 $p{coercion} = delete $p{using} if exists $p{using};
321 16 100       41 $p{inline_generator} = delete $p{inline} if exists $p{inline};
322              
323 16         88 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.46
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
472             allows the inlined code to be safely included in an C<if> statement, for
473             example. You can use C<do { }> blocks and ternaries to get everything into one
474             term. Do not assign to the variable you are testing. This single term should
475             evaluate to 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 (eval)
501             with value 1.1".
502              
503             You can override this to provide something more specific about the way the
504             type 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
525             be an object which does the L<Specio::Constraint::Role::Interface> role. This
526             can be either a named or anonymous type. This type is the type that the
527             coercion is 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
558             allows the inlined code to be safely included in an C<if> statement, for
559             example. You can use C<do { }> blocks and ternaries to get everything into one
560             term. This 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
571             object of the given class. The C<object_isa_type> helper creates a type
572             which only 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
586             object which does the given role. The C<object_does_type> helper creates a
587             type which 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
604             object with the given methods. The C<object_can_type> helper creates a type
605             which only 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,
609             which can be either a string or array reference of strings. These strings are
610             the 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
621             reference 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
632             reference of 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
643             reference of 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
650             parameters, 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
664             the 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 - 2020 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