File Coverage

blib/lib/Type/Tie.pm
Criterion Covered Total %
statement 128 154 83.1
branch 26 40 65.0
condition 9 22 40.9
subroutine 31 35 88.5
pod 1 1 100.0
total 195 252 77.3


line stmt bran cond sub pod time code
1 5     5   2314 use 5.008005;
  5         18  
2 5     5   27 use strict;
  5         11  
  5         101  
3 5     5   22 use warnings;
  5         10  
  5         124  
4              
5 5     5   22 use Carp ();
  5         9  
  5         92  
6 5     5   947 use Exporter::Tiny ();
  5         6510  
  5         85  
7 5     5   2514 use Tie::Array ();
  5         6077  
  5         147  
8 5     5   2448 use Tie::Hash ();
  5         4746  
  5         138  
9 5     5   2336 use Tie::Scalar ();
  5         3046  
  5         1153  
10              
11             ++$Carp::CarpInternal{"Type::Tie::$_"} for qw( BASE SCALAR ARRAY HASH );
12              
13             BEGIN
14 0         0 {
15             package Type::Tie;
16 5     5   21 our $AUTHORITY = 'cpan:TOBYINK';
17 5         11 our $VERSION = '0.015';
18 5         90 our @ISA = qw( Exporter::Tiny );
19 5         701 our @EXPORT = qw( ttie );
20            
21             sub ttie (\[$@%]@)#>&%*/&<%\$[]^!@;@)
22             {
23 13     13 1 5657 my ($ref, $type, @vals) = @_;
24            
25 13 100       66 if (ref($ref) eq "HASH")
    100          
26             {
27 4         37 tie(%$ref, "Type::Tie::HASH", $type);
28 4 100       49 %$ref = @vals if @vals;
29             }
30             elsif (ref($ref) eq "ARRAY")
31             {
32 4         133 tie(@$ref, "Type::Tie::ARRAY", $type);
33 4 100       33 @$ref = @vals if @vals;
34             }
35             else
36             {
37 5         40 tie($$ref, "Type::Tie::SCALAR", $type);
38 5 100       34 $$ref = $vals[-1] if @vals;
39             }
40 13         93 return $ref;
41             }
42             };
43              
44             BEGIN
45 0         0 {
46             package Type::Tie::BASE;
47 5     5   23 our $AUTHORITY = 'cpan:TOBYINK';
48 5         10 our $VERSION = '0.015';
49            
50             BEGIN {
51 5     5   17 my $impl;
52 5   33     35 $impl ||= eval { require Hash::FieldHash; 'Hash::FieldHash' };
  5         2866  
  5         7026  
53 5   33     20 $impl ||= eval { require Hash::Util::FieldHash; 'Hash::Util::FieldHash' };
  0         0  
  0         0  
54 5   33     20 $impl ||= do { require Hash::Util::FieldHash::Compat; 'Hash::Util::FieldHash::Compat' };
  0         0  
  0         0  
55 5         4173 $impl->import('fieldhash');
56             };
57            
58 5         26 fieldhash(my %TYPE);
59 5         15 fieldhash(my %COERCE);
60 5         18 fieldhash(my %CHECK);
61            
62             sub _set_type
63             {
64 16     16   29 my $self = shift;
65 16         30 my $type = $_[0];
66            
67 16         86 $TYPE{$self} = $type;
68            
69 16 100       91 if ($type->isa('Type::Tiny'))
70             {
71 10         122 $CHECK{$self} = $type->compiled_check;
72 10         108 $COERCE{$self} = undef;
73 10 100       30 $COERCE{$self} = $type->coercion->compiled_coercion
74             if $type->has_coercion;
75             }
76             else
77             {
78             $CHECK{$self} = $type->can('compiled_check')
79             ? $type->compiled_check
80 6 50   45   55 : sub { $type->check($_[0]) };
  45         101  
81 6         16 $COERCE{$self} = undef;
82 20     20   40 $COERCE{$self} = sub { $type->coerce($_[0]) }
83 6 50 66     45 if $type->can("has_coercion")
      66        
84             && $type->can("coerce")
85             && $type->has_coercion;
86             }
87             }
88            
89             sub type
90             {
91 0     0   0 my $self = shift;
92 0         0 $TYPE{$self};
93             }
94            
95             sub _dd
96             {
97 0 0   0   0 my $value = @_ ? $_[0] : $_;
98             !defined $value ? 'Undef' :
99             !ref $value ? sprintf('Value %s', B::perlstring($value)) :
100 0 0       0 do {
    0          
101 0         0 require Data::Dumper;
102 0         0 local $Data::Dumper::Indent = 0;
103 0         0 local $Data::Dumper::Useqq = 1;
104 0         0 local $Data::Dumper::Terse = 1;
105 0         0 local $Data::Dumper::Sortkeys = 1;
106 0         0 local $Data::Dumper::Maxdepth = 2;
107 0         0 Data::Dumper::Dumper($value)
108             }
109             }
110            
111             sub coerce_and_check_value
112             {
113 57     57   89 my $self = shift;
114 57         107 my $check = $CHECK{$self};
115 57         119 my $coerce = $COERCE{$self};
116            
117             my @vals = map {
118 57 100       142 my $val = $coerce ? $coerce->($_) : $_;
  75         163  
119 75 100       428 if (not $check->($val)) {
120 18         90 my $type = $TYPE{$self};
121 18 50 33     86 Carp::croak(
      0        
122             $type && $type->can('get_message')
123             ? $type->get_message($val)
124             : sprintf("%s does not meet type constraint %s", _dd($_), $type||'Unknown')
125             );
126             }
127 57         334 $val;
128             } (my @cp = @_); # need to copy @_ for Perl < 5.14
129            
130 39 50       230 wantarray ? @vals : $vals[0];
131             }
132              
133             # store the $type for the exiting instances so the type can be set
134             # (uncloned) in the clone too. A clone process could be cloning several
135             # instances of this class, so use a hash to hold the types during
136             # cloning. These types are reference counted, so the last reference to
137             # a particular type deletes its key.
138 5         1860 my %tmp_clone_types;
139             sub STORABLE_freeze {
140             die "Scalar::Util is needed for cloning with Storage::dclone"
141 3 50   3   81 unless eval { require Scalar::Util };
  3         24  
142 3         8 my $self = shift;
143 3         4 my $cloning = shift;
144              
145 3 50       8 die "Storage::freeze only supported for dclone-ing"
146             unless $cloning;
147              
148 3         6 my $type = $TYPE{$self};
149 3         10 my $refaddr = Scalar::Util::refaddr($type);
150 3   50     23 $tmp_clone_types{$refaddr} ||= [ $type, 0 ];
151 3         31 ++$tmp_clone_types{$refaddr}[1];
152 3         233 return (pack('j', $refaddr), $self);
153             }
154              
155             sub STORABLE_thaw {
156 3     3   10 my $self = shift;
157 3         4 my $cloning = shift;
158 3         7 my $packedRefaddr = shift;
159 3         4 my $obj = shift;
160              
161 3 50       8 die "Storage::thaw only supported for dclone-ing"
162             unless $cloning;
163              
164 3         11 $self->_STORABLE_thaw_update_from_obj($obj);
165 3         11 my $refaddr = unpack('j', $packedRefaddr);
166 3         8 my $type = $tmp_clone_types{$refaddr}[0];
167             --$tmp_clone_types{$refaddr}[1]
168 3 50       14 or delete $tmp_clone_types{$refaddr};
169 3         7 $self->_set_type($type);
170             }
171             };
172              
173             BEGIN
174 0         0 {
175             package Type::Tie::ARRAY;
176 5     5   40 our $AUTHORITY = 'cpan:TOBYINK';
177 5         12 our $VERSION = '0.015';
178 5         1049 our @ISA = qw( Tie::StdArray Type::Tie::BASE );
179            
180             sub TIEARRAY
181             {
182 4     4   22 my $class = shift;
183 4         46 my $self = $class->SUPER::TIEARRAY;
184 4         42 $self->_set_type($_[0]);
185 4         662 return $self;
186             }
187            
188             sub STORE
189             {
190 17     17   484 my $self = shift;
191 17         46 $self->SUPER::STORE($_[0], $self->coerce_and_check_value($_[1]));
192             }
193            
194             sub PUSH
195             {
196 8     8   483 my $self = shift;
197 8         37 $self->SUPER::PUSH( $self->coerce_and_check_value(@_) );
198             }
199            
200             sub UNSHIFT
201             {
202 6     6   27 my $self = shift;
203 6         16 $self->SUPER::UNSHIFT( $self->coerce_and_check_value(@_) );
204             }
205              
206             sub SPLICE
207             {
208 0     0   0 my $self = shift;
209 0         0 my ($start, $len, @rest) = @_;
210 0         0 $self->SUPER::SPLICE($start, $len, $self->coerce_and_check_value(@rest) );
211             }
212              
213             sub _STORABLE_thaw_update_from_obj {
214 2     2   4 my $self = shift;
215 2         3 my $obj = shift;
216 2         5 @$self = @$obj;
217             }
218             };
219              
220             BEGIN
221 0         0 {
222             package Type::Tie::HASH;
223 5     5   20 our $AUTHORITY = 'cpan:TOBYINK';
224 5         9 our $VERSION = '0.015';
225 5         1008 our @ISA = qw( Tie::StdHash Type::Tie::BASE );
226            
227             sub TIEHASH
228             {
229 4     4   11 my $class = shift;
230 4         44 my $self = $class->SUPER::TIEHASH;
231 4         47 $self->_set_type($_[0]);
232 4         75 return $self;
233             }
234            
235             sub STORE
236             {
237 11     11   648 my $self = shift;
238 11         40 $self->SUPER::STORE($_[0], $self->coerce_and_check_value($_[1]));
239             }
240              
241             sub _STORABLE_thaw_update_from_obj {
242 1     1   2 my $self = shift;
243 1         3 my $obj = shift;
244 1         5 %$self = %$obj;
245             }
246             };
247              
248             BEGIN
249 0         0 {
250             package Type::Tie::SCALAR;
251 5     5   20 our $AUTHORITY = 'cpan:TOBYINK';
252 5         8 our $VERSION = '0.015';
253 5         253 our @ISA = qw( Tie::StdScalar Type::Tie::BASE );
254            
255             sub TIESCALAR
256             {
257 5     5   13 my $class = shift;
258 5         61 my $self = $class->SUPER::TIESCALAR;
259 5         87 $self->_set_type($_[0]);
260 5         1032 return $self;
261             }
262            
263             sub STORE
264             {
265 15     15   3680 my $self = shift;
266 15         58 $self->SUPER::STORE( $self->coerce_and_check_value($_[0]) );
267             }
268              
269             sub _STORABLE_thaw_update_from_obj {
270 0     0     my $self = shift;
271 0           my $obj = shift;
272 0           $self = $obj;
273             }
274             };
275              
276             1;
277              
278             __END__