File Coverage

blib/lib/Type/Tie/Aggregate/Base.pm
Criterion Covered Total %
statement 196 311 63.0
branch 58 174 33.3
condition 6 12 50.0
subroutine 16 21 76.1
pod 2 2 100.0
total 278 520 53.4


line stmt bran cond sub pod time code
1             # ABSTRACT: base class for tying variables
2              
3             ######################################################################
4             # Copyright (C) 2021 Asher Gordon #
5             # #
6             # This program is free software: you can redistribute it and/or #
7             # modify it under the terms of the GNU General Public License as #
8             # published by the Free Software Foundation, either version 3 of #
9             # the License, or (at your option) any later version. #
10             # #
11             # This program is distributed in the hope that it will be useful, #
12             # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU #
14             # General Public License for more details. #
15             # #
16             # You should have received a copy of the GNU General Public License #
17             # along with this program. If not, see #
18             # . #
19             ######################################################################
20              
21             package Type::Tie::Aggregate::Base;
22             $Type::Tie::Aggregate::Base::VERSION = '0.001';
23             #pod =head1 DESCRIPTION
24             #pod
25             #pod This class is inherited by the classes used to tie variables to
26             #pod types. This class is internal to
27             #pod L.
28             #pod
29             #pod The methods below are described in more detail in
30             #pod L.
31             #pod
32             #pod =cut
33              
34 5     5   2915 use v5.10.0;
  5         18  
35 5     5   26 use strict;
  5         11  
  5         146  
36 5     5   27 use warnings;
  5         10  
  5         165  
37 5     5   32 use namespace::autoclean;
  5         10  
  5         30  
38 5     5   297 use Carp;
  5         11  
  5         4931  
39              
40             require Type::Tie::Aggregate::Deep;
41             *_deep_tie = \&Type::Tie::Aggregate::Deep::deep_tie;
42              
43             our @CARP_NOT = qw(Type::Tie::Aggregate);
44              
45             sub _new {
46 19     19   64 my ($class, $type, @init) = @_;
47 19         57 my $self = bless {}, $class;
48              
49 19         110 $self->_type($type);
50              
51 19         42 my $check;
52 19 50       93 if ($type->can('compiled_check')) {
    0          
53 19         281 $check = $type->compiled_check;
54 19 50       142 ref $check eq 'CODE' or croak 'Compiled check for ' .
55             "$type is not a CODE ref: $check";
56             }
57             elsif (my $check_method = $type->can('check')) {
58 0     0   0 $check = sub { $type->$check_method(@_) };
  0         0  
59             }
60             else {
61 0         0 croak "Type $type is not a valid type constraint";
62             }
63 19         103 $self->_compiled_check($check);
64              
65             # If there's no has_coercion() method, assume that it does have aq
66             # coercion.
67 19         74 my $has_coercion = $type->can('has_coercion');
68 19         163 my $coercion;
69 19 100 66     100 if (! $has_coercion || $type->$has_coercion) {
70 7 50       177 if ($type->can('coercion')) {
71 7         76 my $coercion_obj = $type->coercion;
72 7 50       61 if ($coercion_obj->can('compiled_coercion')) {
    0          
73 7         84 $coercion = $coercion_obj->compiled_coercion;
74 7 50       11803 ref $coercion eq 'CODE'
75             or croak "Compiled coercion for $coercion_obj " .
76             "(type: $type) is not a CODE ref: $coercion";
77             }
78             elsif (my $coerce_method = $coercion_obj->can('coerce')) {
79 0     0   0 $coercion = sub { $coercion_obj->$coerce_method(@_) };
  0         0  
80             }
81             else {
82 0         0 carp "Type $type provides a coercion object " .
83             "$coercion_obj, but it cannot coerce";
84             }
85             }
86 7 50       28 unless (defined $coercion) {
87 0 0       0 if (my $coerce_method = $type->can('coerce')) {
    0          
88 0     0   0 $coercion = sub { $type->$coerce_method(@_) };
  0         0  
89             }
90             elsif ($has_coercion) {
91 0         0 carp "Type $type falsely reports that it can coerce";
92             }
93             }
94             }
95 19         322 $self->_compiled_coercion($coercion);
96              
97 19         40 my $get_message;
98 19 50 33     67 if ($type->can('message') &&
    50          
99             ref (my $message = $type->message) eq 'CODE') {
100 0         0 $get_message = $message;
101             }
102             elsif (my $get_message_method = $type->can('get_message')) {
103 19     19   560 $get_message = sub { $type->$get_message_method(@_) };
  19         104  
104             }
105             else {
106 0         0 my $type_name = "$type";
107             $get_message = sub {
108 0     0   0 my ($value) = @_;
109 0         0 "$value did not pass type constraint $type";
110 0         0 };
111             }
112 19         104 $self->_message($get_message);
113              
114 19         99 $self->initialize(@init);
115 12         73 return $self;
116             }
117              
118             #pod =method initialize
119             #pod
120             #pod $obj->initialize(@init);
121             #pod
122             #pod Initialize C<$obj> from C<@init>.
123             #pod
124             #pod =cut
125              
126             sub initialize {
127 23     23 1 22282 my $self = shift;
128 23         116 $self->_initialize(@_);
129 23         158 $self->_check_and_retie;
130 16         34 return $self;
131             }
132              
133             sub _initialize {
134             # It is important that we use a copy for @init (rather than
135             # shifting and using @_), because we don't want to modify the
136             # original value(s) passed to _initialize().
137 37     37   105 my ($self, @init) = @_;
138 37         148 $self->_ref($self->_create_ref(@init));
139             }
140              
141             #pod =method type
142             #pod
143             #pod my $type = $obj->type;
144             #pod
145             #pod Return the type constraint associated with C<$obj>.
146             #pod
147             #pod =cut
148              
149             sub type {
150 0     0 1 0 my $self = shift;
151 0 0       0 croak 'The type constraint can only be read, not set' if @_;
152 0         0 $self->_type;
153             }
154              
155             # Install accessors for the following attributes, prefixed with an
156             # underscore. The '_value' accessor, which defaults to the same as
157             # '_ref', is overridden by Type::Tie::Aggregate::Scalar.
158             foreach (qw(type compiled_check compiled_coercion message),
159             [ref => 'value']) {
160             my ($key, @aliases) = ref eq 'ARRAY' ? @$_ : $_;
161             my $code = sub {
162 2625     2625   4189 my $self = shift;
163 2625 100       32376 return $self->{$key} unless @_;
164 125         400 ($self->{$key}) = @_;
165             };
166             foreach ($key, @aliases) {
167 5     5   62 no strict 'refs';
  5         12  
  5         2150  
168             *{"_$_"} = $code;
169             }
170             }
171              
172             # This is used to check values after coercion. It doesn't do any
173             # checking by default, but can be overridden by subclasses. It should
174             # return an error string on error, or undef on success.
175       11     sub _check_value { }
176              
177             # Perform a type check on the type, croaking on error.
178             sub _check_and_retie {
179 257     257   531 my ($self) = @_;
180 257         775 my ($value, $check, $coerce) = map $self->$_, qw(
181             _value _compiled_check _compiled_coercion
182             );
183              
184 257 100       601 if (defined $coerce) {
185 24         68 $value = $coerce->($value);
186 24         712 my $err = $self->_check_value($value);
187 24 100       80 croak "Coerced to invalid value: $err" if defined $err;
188 23         60 $self->_value($value);
189             }
190              
191 256 100       666 $check->($value) or croak $self->_message->($value);
192              
193 237         3395 $self->_deep_tie;
194              
195 136         3015 return $value;
196             }
197              
198             # Install methods, and also make the methods retie and recheck the
199             # object if $opts->{mutates}.
200             sub _install_methods {
201 40     40   209 my ($class, $opts, @methods) = @_;
202              
203 40         93 my $mutates = $opts->{mutates};
204 40 100       210 require Type::Tie::Aggregate::Deep if $mutates;
205              
206 40         157 while (my ($method, $code) = splice @methods, 0, 2) {
207 185         346 my $callback;
208             my $statement; # $code in a single statement
209 185 100       428 if (ref $code eq 'CODE') {
210 5         12 $callback = $code;
211 5         14 $statement = $code = '$self->$callback(@_)';
212             }
213             else {
214 180         369 $statement = "do { $code }";
215             }
216              
217 185 100 66     500 if ($callback && ! $mutates) {
218             # Optimize this case.
219 5         12 $code = $callback;
220             }
221             else {
222 180 50 33     766 $code = q{
    100          
223             my $self = shift;
224             } . (
225             ! $callback || $mutates ? q{
226             my $ref = $self->_ref;
227             } : '',
228             ) . (
229             $mutates ? qq{
230             wantarray ? my \@ret = $statement :
231             defined wantarray ? my \$ret = $statement :
232             $statement;
233             } . q{
234             $self->_check_and_retie;
235             return wantarray ? @ret : $ret;
236             } : $code,
237             );
238              
239 180 50   7   22821 $code = eval "sub { $code }" or die;
  7 0       1597  
  7 0       26  
  7 0       27  
  40 0       1379  
  35 0       82  
  35 0       109  
  0 0       0  
  0 0       0  
  0 0       0  
  0 50       0  
  0 50       0  
  11 0       1075  
  11 50       40  
  11 50       37  
  0 50       0  
  0 50       0  
  1 50       32  
  1 50       5  
  1 0       5  
  0 0       0  
  0 0       0  
  0 50       0  
  34 50       991  
  34 50       84  
  34 50       110  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 50       0  
  0 0       0  
  1 0       483  
  1 0       5  
  1 0       10  
  0 0       0  
  209 0       561  
  208 50       519  
  209 50       470  
  208 50       4445  
  0 0       0  
  0 0       0  
  3 0       181  
  3 0       8  
  3 0       11  
  0 0       0  
  0 0       0  
  0 0       0  
  2 0       1275  
  2 0       7  
  2 0       11  
  3 0       2037  
  5 0       18  
  3 0       15  
  2 0       8  
  4 100       17  
  0 50       0  
  3 50       14  
  160 50       6646  
  158 50       349  
  158 50       715  
  0 50       0  
  0 50       0  
  0 50       0  
  5         112  
  17         9317  
  17         57  
  25         387  
  13         34  
  13         47  
  12         4032  
  12         41  
  12         47  
  0         0  
  4         14  
  0         0  
  4         17  
  2         11  
  206         445  
  206         473  
  206         4385  
  0         0  
  0         0  
  0         0  
  8         1058  
  8         21  
  8         25  
  2         10702  
  2         11  
  2         12  
  0         0  
  9         25  
  7         21  
  9         46  
  1         6  
  7         25  
  0         0  
  7         52  
  3         20  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  213         3067  
  213         468  
  213         1087  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  3         809  
  3         10  
  3         8  
  3         68  
  99         225  
  99         249  
  99         304  
  0         0  
  0         0  
  99         222  
  99         604  
  98         366  
  153         3553  
  153         318  
  162         5023  
  17         2259  
  17         246  
  8         142  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  8         262  
  8         23  
  8         32  
  10         674  
  10         29  
  10         111  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  5         12  
  10         143  
  10         35  
  5         19  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  2         819  
  106         923  
  106         263  
  104         358  
  3         350  
  6         19  
  107         254  
  111         371  
  12         877  
  3         1080  
  8         43  
  2         11  
  8         199  
  5         32  
  2         9  
  2         14  
  1         7  
  3         232  
  3         11  
  3         15  
  0         0  
  3         11  
  0         0  
  3         12  
  2         46  
240             }
241              
242 5     5   41 no strict 'refs';
  5         11  
  5         503  
243 185         497 *{"$class\::$method"} = $code;
  185         2151  
244             }
245             }
246              
247             #pod =head1 SEE ALSO
248             #pod
249             #pod =for :list
250             #pod * L
251             #pod
252             #pod =cut
253              
254             1;
255              
256             __END__