File Coverage

blib/lib/Specio/Constraint/Structurable.pm
Criterion Covered Total %
statement 52 55 94.5
branch 10 22 45.4
condition n/a
subroutine 15 15 100.0
pod 1 2 50.0
total 78 94 82.9


line stmt bran cond sub pod time code
1             package Specio::Constraint::Structurable;
2              
3 4     4   30 use strict;
  4         7  
  4         119  
4 4     4   19 use warnings;
  4         19  
  4         176  
5              
6             our $VERSION = '0.47';
7              
8 4     4   26 use Carp qw( confess );
  4         11  
  4         187  
9 4     4   24 use Role::Tiny::With;
  4         10  
  4         284  
10 4     4   37 use Scalar::Util qw( blessed );
  4         10  
  4         266  
11 4     4   26 use Specio::DeclaredAt;
  4         18  
  4         97  
12 4     4   22 use Specio::OO;
  4         11  
  4         304  
13 4     4   1850 use Specio::Constraint::Structured;
  4         10  
  4         171  
14 4     4   31 use Specio::TypeChecks qw( does_role isa_class );
  4         8  
  4         204  
15              
16 4     4   25 use Specio::Constraint::Role::Interface;
  4         8  
  4         1946  
17             with 'Specio::Constraint::Role::Interface';
18              
19             {
20             ## no critic (Subroutines::ProtectPrivateSubs)
21             my $role_attrs = Specio::Constraint::Role::Interface::_attrs();
22             ## use critic
23              
24             my $attrs = {
25             %{$role_attrs},
26             _parameterization_args_builder => {
27             isa => 'CodeRef',
28             init_arg => 'parameterization_args_builder',
29             required => 1,
30             },
31             _name_builder => {
32             isa => 'CodeRef',
33             init_arg => 'name_builder',
34             required => 1,
35             },
36             _structured_constraint_generator => {
37             isa => 'CodeRef',
38             init_arg => 'structured_constraint_generator',
39             predicate => '_has_structured_constraint_generator',
40             },
41             _structured_inline_generator => {
42             isa => 'CodeRef',
43             init_arg => 'structured_inline_generator',
44             predicate => '_has_structured_inline_generator',
45             },
46             };
47              
48             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
49             sub _attrs {
50 20     20   44 return $attrs;
51             }
52             }
53              
54             sub BUILD {
55 12     12 0 295 my $self = shift;
56              
57 12 50       43 if ( $self->_has_constraint ) {
58 0 0       0 die
59             'A structurable constraint with a constraint parameter must also have a structured_constraint_generator'
60             unless $self->_has_structured_constraint_generator;
61             }
62              
63 12 50       80 if ( $self->_has_inline_generator ) {
64 12 50       70 die
65             'A structurable constraint with an inline_generator parameter must also have a structured_inline_generator'
66             unless $self->_has_structured_inline_generator;
67             }
68              
69 12         64 return;
70             }
71              
72             sub parameterize {
73 11     11 1 139 my $self = shift;
74 11         39 my %args = @_;
75              
76 11         28 my $declared_at = $args{declared_at};
77              
78 11 50       37 if ($declared_at) {
79 11 50       40 isa_class( $declared_at, 'Specio::DeclaredAt' )
80             or confess
81             q{The "declared_at" parameter passed to ->parameterize must be a Specio::DeclaredAt object};
82             }
83              
84             my %parameters
85 11         61 = $self->_parameterization_args_builder->( $self, $args{of} );
86              
87 11 50       51 $declared_at = Specio::DeclaredAt->new_from_caller(1)
88             unless defined $declared_at;
89              
90 11         171 my %new_p = (
91             parent => $self,
92             parameters => \%parameters,
93             declared_at => $declared_at,
94             name => $self->_name_builder->( $self, \%parameters ),
95             );
96              
97 11 50       69 if ( $self->_has_structured_constraint_generator ) {
98             $new_p{constraint}
99 0         0 = $self->_structured_constraint_generator->(%parameters);
100             }
101             else {
102 11         71 for my $p (
103             grep {
104 14 100       74 blessed($_)
105             && does_role('Specio::Constraint::Role::Interface')
106             } values %parameters
107             ) {
108              
109 0 0       0 confess
110             q{Any type objects passed to ->parameterize must be inlinable constraints if the structurable type has an inline_generator}
111             unless $p->can_be_inlined;
112             }
113              
114 11         42 my $ig = $self->_structured_inline_generator;
115             $new_p{inline_generator}
116 11     24   79 = sub { $ig->( shift, shift, %parameters, @_ ) };
  24         201  
117             }
118              
119 11         81 return Specio::Constraint::Structured->new(%new_p);
120             }
121              
122             ## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
123             sub _name_or_anon {
124 30 50   30   93 return $_[1]->_has_name ? $_[1]->name : 'ANON';
125             }
126             ## use critic
127              
128             __PACKAGE__->_ooify;
129              
130             1;
131              
132             # ABSTRACT: A class which represents structurable constraints
133              
134             __END__
135              
136             =pod
137              
138             =encoding UTF-8
139              
140             =head1 NAME
141              
142             Specio::Constraint::Structurable - A class which represents structurable constraints
143              
144             =head1 VERSION
145              
146             version 0.47
147              
148             =head1 SYNOPSIS
149              
150             my $tuple = t('Tuple');
151              
152             my $tuple_of_str_int = $tuple->parameterize( of => [ t('Str'), t('Int') ] );
153              
154             =head1 DESCRIPTION
155              
156             This class implements the API for structurable types like C<Dict>, C<Map>< and
157             C<Tuple>.
158              
159             =for Pod::Coverage BUILD
160              
161             =head1 API
162              
163             This class implements the same API as L<Specio::Constraint::Simple>, with a few
164             additions.
165              
166             =head2 Specio::Constraint::Structurable->new(...)
167              
168             This class's constructor accepts two additional parameters:
169              
170             =over 4
171              
172             =item * parameterization_args_builder
173              
174             This is a subroutine that takes the values passed to C<of> and returns a hash
175             of named arguments. These arguments will then be passed into the
176             C<structured_constraint_generator> or C<structured_inline_generator>.
177              
178             This should also do argument checking to make sure that the argument passed are
179             valid. For example, the C<Tuple> type turns the arrayref passed to C<of> into a
180             hash, along the way checking that the caller did not do things like interleave
181             optional and required elements or mix optional and slurpy together in the
182             definition.
183              
184             This parameter is required.
185              
186             =item * name_builder
187              
188             This is a subroutine that is called to generate a name for the structured type
189             when it is created. This will be called as a method on the
190             C<Specio::Constraint::Structurable> object. It will be passed the hash of
191             arguments returned by the C<parameterization_args_builder>.
192              
193             This parameter is required.
194              
195             =item * structured_constraint_generator
196              
197             This is a subroutine that generates a new constraint subroutine when the type
198             is structured.
199              
200             It will be called as a method on the type and will be passed the hash of
201             arguments returned by the C<parameterization_args_builder>.
202              
203             This parameter is mutually exclusive with the C<structured_inline_generator>
204             parameter.
205              
206             This parameter or the C<structured_inline_generator> parameter is required.
207              
208             =item * structured_inline_generator
209              
210             This is a subroutine that generates a new inline generator subroutine when the
211             type is structured.
212              
213             It will be called as a method on the L<Specio::Constraint::Structured> object
214             when that object needs to generate an inline constraint. It will receive the
215             type parameter as the first argument and the variable name as a string as the
216             second.
217              
218             The remaining arguments will be the parameter hash returned by the
219             C<parameterization_args_builder>.
220              
221             This probably seems fairly confusing, so looking at the examples in the
222             L<Specio::Library::Structured::*> code may be helpful.
223              
224             This parameter is mutually exclusive with the
225             C<structured_constraint_generator> parameter.
226              
227             This parameter or the C<structured_constraint_generator> parameter is required.
228              
229             =back
230              
231             =head2 $type->parameterize(...)
232              
233             This method takes two arguments. The C<of> argument should be an object which
234             does the L<Specio::Constraint::Role::Interface> role, and is required.
235              
236             The other argument, C<declared_at>, is optional. If it is not given, then a new
237             L<Specio::DeclaredAt> object is creating using a call stack depth of 1.
238              
239             This method returns a new L<Specio::Constraint::Structured> object.
240              
241             =head1 SUPPORT
242              
243             Bugs may be submitted at L<https://github.com/houseabsolute/Specio/issues>.
244              
245             I am also usually active on IRC as 'autarch' on C<irc://irc.perl.org>.
246              
247             =head1 SOURCE
248              
249             The source code repository for Specio can be found at L<https://github.com/houseabsolute/Specio>.
250              
251             =head1 AUTHOR
252              
253             Dave Rolsky <autarch@urth.org>
254              
255             =head1 COPYRIGHT AND LICENSE
256              
257             This software is Copyright (c) 2012 - 2021 by Dave Rolsky.
258              
259             This is free software, licensed under:
260              
261             The Artistic License 2.0 (GPL Compatible)
262              
263             The full text of the license can be found in the
264             F<LICENSE> file included with this distribution.
265              
266             =cut