File Coverage

blib/lib/Type/Tie.pm
Criterion Covered Total %
statement 134 134 100.0
branch 31 38 81.5
condition 10 16 62.5
subroutine 48 48 100.0
pod 1 1 100.0
total 224 237 94.5


line stmt bran cond sub pod time code
1 8     8   73630 use 5.008001;
  8         35  
2 8     8   43 use strict;
  8         15  
  8         160  
3 8     8   37 use warnings;
  8         14  
  8         193  
4              
5 8     8   42 use Carp ();
  8         18  
  8         144  
6 8     8   3718 use Exporter::Tiny ();
  8         32769  
  8         236  
7 8     8   56 use Scalar::Util ();
  8         28  
  8         20078  
8              
9             ++$Carp::CarpInternal{"Type::Tie::$_"} for qw( BASE SCALAR ARRAY HASH );
10              
11             {
12             package Type::Tie;
13             our $AUTHORITY = 'cpan:TOBYINK';
14             our $VERSION = '2.003_000';
15             our @ISA = qw( Exporter::Tiny );
16             our @EXPORT = qw( ttie );
17            
18             $VERSION =~ tr/_//d;
19            
20             sub ttie (\[$@%]@)#>&%*/&<%\$[]^!@;@)
21             {
22 17     17 1 6213 my ( $ref, $type, @vals ) = @_;
23            
24 17 100       92 if ( 'HASH' eq ref $ref ) {
    100          
25 5         54 tie %$ref, "Type::Tie::HASH", $type;
26 5 100       44 %$ref = @vals if @vals;
27             }
28             elsif ( 'ARRAY' eq ref $ref ) {
29 5         35 tie @$ref, "Type::Tie::ARRAY", $type;
30 5 100       42 @$ref = @vals if @vals;
31             }
32             else {
33 7         52 tie $$ref, "Type::Tie::SCALAR", $type;
34 7 100       64 $$ref = $vals[-1] if @vals;
35             }
36 17         63 return $ref;
37             }
38             };
39              
40             {
41             package Type::Tie::BASE;
42             our $AUTHORITY = 'cpan:TOBYINK';
43             our $VERSION = '2.003_000';
44            
45             $VERSION =~ tr/_//d;
46            
47             # Type::Tie::BASE is an array-based object. If you need to subclass it
48             # and store more attributes, use $yourclass->SUPER::_NEXT_SLOT to find
49             # the next available slot, then override _NEXT_SLOT so that other people
50             # can subclass your class too.
51             #
52             sub _REF { $_[0][0] } # ro
53             sub _TYPE { ( @_ == 2 ) ? ( $_[0][1] = $_[1] ) : $_[0][1] } # rw
54             sub _CHECK { ( @_ == 2 ) ? ( $_[0][2] = $_[1] ) : $_[0][2] } # rw
55             sub _COERCE { ( @_ == 2 ) ? ( $_[0][3] = $_[1] ) : $_[0][3] } # rw
56 1     1   5 sub _NEXT_SLOT { 4 }
57            
58             sub type { shift->_TYPE }
59 3   33 3   20 sub _INIT_REF { $_[0][0] ||= $_[0]->_DEFAULT }
60            
61             {
62             my $try_xs =
63             exists( $ENV{PERL_TYPE_TINY_XS} ) ? !!$ENV{PERL_TYPE_TINY_XS} :
64             exists( $ENV{PERL_ONLY} ) ? !$ENV{PERL_ONLY} :
65             !!1;
66             eval {
67             require Class::XSAccessor::Array;
68             'Class::XSAccessor::Array'->import(
69             replace => !!1,
70             getters => { _REF => 0, type => 1 },
71             accessors => { _TYPE => 1, _CHECK => 2, _COERCE => 3 },
72             );
73             } if $try_xs;
74             }
75            
76             sub _set_type {
77 23     23   50 my $self = shift;
78 23         44 my $type = $_[0];
79            
80 23         132 $self->_TYPE( $type );
81            
82 23 100 66     202 if ( Scalar::Util::blessed( $type ) and $type->isa( 'Type::Tiny' ) ) {
83 16         59 $self->_CHECK( $type->compiled_check );
84 16 100       52 $self->_COERCE(
85             $type->has_coercion
86             ? $type->coercion->compiled_coercion
87             : undef
88             );
89             }
90             else {
91             $self->_CHECK(
92             $type->can( 'compiled_check' )
93             ? $type->compiled_check
94 49     49   120 : sub { $type->check( $_[0] ) }
95 7 50       732 );
96             $self->_COERCE(
97             $type->can( 'has_coercion' ) && $type->can( 'coerce' ) && $type->has_coercion
98 40     40   98 ? sub { $type->coerce( $_[0] ) }
99             : undef
100 7 100 100     775 );
101             }
102             }
103            
104             # Only used if the type has no get_message method
105             sub _dd {
106 1     1   697 require Type::Tiny;
107 1         27 goto \&Type::Tiny::_dd;
108             }
109            
110             sub coerce_and_check_value {
111 88     88   151 my $self = shift;
112 88         178 my $check = $self->_CHECK;
113 88         152 my $coerce = $self->_COERCE;
114            
115             my @vals = map {
116 88 100       217 my $val = $coerce ? $coerce->( $_ ) : $_;
  113         254  
117 113 100       3659 if ( not $check->( $val ) ) {
118 28         1078 my $type = $self->_TYPE;
119 28 100 66     155 Carp::croak(
      50        
120             $type && $type->can( 'get_message' )
121             ? $type->get_message( $val )
122             : sprintf( '%s does not meet type constraint %s', _dd($_), $type || 'Unknown' )
123             );
124             }
125 85         2918 $val;
126             } ( my @cp = @_ ); # need to copy @_ for Perl < 5.14
127            
128 60 100       305 wantarray ? @vals : $vals[0];
129             }
130            
131             # store the $type for the exiting instances so the type can be set
132             # (uncloned) in the clone too. A clone process could be cloning several
133             # instances of this class, so use a hash to hold the types during
134             # cloning. These types are reference counted, so the last reference to
135             # a particular type deletes its key.
136             my %tmp_clone_types;
137             sub STORABLE_freeze {
138 3     3   50 my ( $o, $cloning ) = @_;
139 3 50       8 Carp::croak( "Storable::freeze only supported for dclone-ing" )
140             unless $cloning;
141            
142 3         9 my $type = $o->_TYPE;
143 3         8 my $refaddr = Scalar::Util::refaddr( $type );
144 3   50     18 $tmp_clone_types{$refaddr} ||= [ $type, 0 ];
145 3         6 ++$tmp_clone_types{$refaddr}[1];
146            
147 3         254 return ( $refaddr, $o->_REF );
148             }
149            
150             sub STORABLE_thaw {
151 3     3   14 my ( $o, $cloning, $refaddr, $o2 ) = @_;
152 3 50       9 Carp::croak( "Storable::thaw only supported for dclone-ing" )
153             unless $cloning;
154            
155 3         12 $o->_THAW( $o2 ); # implement in child classes
156            
157 3         8 my $type = $tmp_clone_types{$refaddr}[0];
158             --$tmp_clone_types{$refaddr}[1]
159 3 50       18 or delete $tmp_clone_types{$refaddr};
160 3         8 $o->_set_type($type);
161             }
162             };
163              
164             {
165             package Type::Tie::ARRAY;
166             our $AUTHORITY = 'cpan:TOBYINK';
167             our $VERSION = '2.003_000';
168             our @ISA = qw( Type::Tie::BASE );
169            
170             $VERSION =~ tr/_//d;
171            
172             sub TIEARRAY {
173 6     6   19 my $class = shift;
174 6         23 my $self = bless( [ $class->_DEFAULT ], $class );
175 6         41 $self->_set_type( $_[0] );
176 6         39 $self;
177             }
178 7     7   27 sub _DEFAULT { [] }
179 79     79   3472 sub FETCHSIZE { scalar @{ $_[0]->_REF } }
  79         193  
180 1     1   7 sub STORESIZE { $#{ $_[0]->_REF } = $_[1] }
  1         5  
181 22     22   88 sub STORE { $_[0]->_REF->[ $_[1] ] = $_[0]->coerce_and_check_value( $_[2] ) }
182 67     67   284 sub FETCH { $_[0]->_REF->[ $_[1] ] }
183 4     4   12 sub CLEAR { @{ $_[0]->_REF } = () }
  4         31  
184 1     1   7 sub POP { pop @{ $_[0]->_REF } }
  1         3  
185 11     11   356 sub PUSH { my $s = shift; push @{$s->_REF}, $s->coerce_and_check_value( @_ ) }
  11         28  
  11         46  
186 3     3   17 sub SHIFT { shift @{ $_[0]->_REF } }
  3         20  
187 9     9   53 sub UNSHIFT { my $s = shift; unshift @{$s->_REF}, $s->coerce_and_check_value( @_ ) }
  9         16  
  9         38  
188 1     1   316 sub EXISTS { exists $_[0]->_REF->[ $_[1] ] }
189 1     1   9 sub DELETE { delete $_[0]->_REF->[ $_[1] ] }
190       4     sub EXTEND {}
191             sub SPLICE {
192 2     2   7 my $o = shift;
193 2         6 my $sz = scalar @{$o->_REF};
  2         6  
194 2 50       6 my $off = @_ ? shift : 0;
195 2 50       5 $off += $sz if $off < 0;
196 2 50       5 my $len = @_ ? shift : $sz-$off;
197 2         3 splice @{$o->_REF}, $off, $len, $o->coerce_and_check_value( @_ );
  2         8  
198             }
199 1     1   3 sub _THAW { @{ $_[0]->_INIT_REF } = @{ $_[1] } }
  1         5  
  1         3  
200             };
201              
202             {
203             package Type::Tie::HASH;
204             our $AUTHORITY = 'cpan:TOBYINK';
205             our $VERSION = '2.003_000';
206             our @ISA = qw( Type::Tie::BASE );
207            
208             $VERSION =~ tr/_//d;
209            
210             sub TIEHASH {
211 6     6   26 my $class = shift;
212 6         25 my $self = bless( [ $class->_DEFAULT ], $class );
213 6         46 $self->_set_type( $_[0] );
214 6         1966 $self;
215             }
216 7     7   26 sub _DEFAULT { +{} }
217 17     17   700 sub STORE { $_[0]->_REF->{ $_[1] } = $_[0]->coerce_and_check_value( $_[2] ) }
218 14     14   80 sub FETCH { $_[0]->_REF->{ $_[1] } }
219 9     9   2100 sub FIRSTKEY { my $a = scalar keys %{ $_[0]->_REF }; each %{ $_[0]->_REF } }
  9         40  
  9         18  
  9         45  
220 14     14   21 sub NEXTKEY { each %{ $_[0]->_REF } }
  14         65  
221 14     14   195 sub EXISTS { exists $_[0]->_REF->{ $_[1] } }
222 4     4   37 sub DELETE { delete $_[0]->_REF->{ $_[1] } }
223 4     4   14 sub CLEAR { %{ $_[0]->_REF } = () }
  4         40  
224 1     1   304 sub SCALAR { scalar %{ $_[0]->_REF } }
  1         8  
225 1     1   2 sub _THAW { %{ $_[0]->_INIT_REF } = %{ $_[1] } }
  1         8  
  1         3  
226             };
227              
228             {
229             package Type::Tie::SCALAR;
230             our $AUTHORITY = 'cpan:TOBYINK';
231             our $VERSION = '2.003_000';
232             our @ISA = qw( Type::Tie::BASE );
233            
234             $VERSION =~ tr/_//d;
235            
236             sub TIESCALAR {
237 8     8   30 my $class = shift;
238 8         52 my $self = bless( [ $class->_DEFAULT ], $class );
239 8         87 $self->_set_type($_[0]);
240 8         62 $self;
241             }
242 9     9   18 sub _DEFAULT { my $x; \$x }
  9         47  
243 27     27   4993 sub STORE { ${ $_[0]->_REF } = $_[0]->coerce_and_check_value( $_[1] ) }
  20         94  
244 18     18   903 sub FETCH { ${ $_[0]->_REF } }
  18         94  
245 1     1   4 sub _THAW { ${ $_[0]->_INIT_REF } = ${ $_[1] } }
  1         5  
  1         7  
246             };
247              
248             1;
249              
250             __END__
251              
252             =pod
253              
254             =encoding utf-8
255              
256             =head1 NAME
257              
258             Type::Tie - tie a variable to a type constraint
259              
260             =head1 SYNOPSIS
261              
262             Type::Tie is a response to this sort of problem...
263              
264             use strict;
265             use warnings;
266            
267             {
268             package Local::Testing;
269             use Moose;
270             has numbers => ( is => "ro", isa => "ArrayRef[Num]" );
271             }
272            
273             # Nice list of numbers.
274             my @N = ( 1, 2, 3, 3.14159 );
275            
276             # Create an object with a reference to that list.
277             my $object = Local::Testing->new(numbers => \@N);
278            
279             # Everything OK so far...
280            
281             # Now watch this!
282             push @N, "Monkey!";
283             print $object->dump;
284            
285             # Houston, we have a problem!
286              
287             Just declare C<< @N >> like this:
288              
289             use Type::Tie;
290             use Types::Standard qw( Num );
291            
292             ttie my @N, Num, ( 1, 2, 3, 3.14159 );
293              
294             Now any attempt to add a non-numeric value to C<< @N >> will die.
295              
296             =head1 DESCRIPTION
297              
298             This module exports a single function: C<ttie>. C<ttie> ties a variable
299             to a type constraint, ensuring that whatever values stored in the variable
300             will conform to the type constraint. If the type constraint has coercions,
301             these will be used if necessary to ensure values assigned to the variable
302             conform.
303              
304             use Type::Tie;
305             use Types::Standard qw( Int Num );
306            
307             ttie my $count, Int->plus_coercions(Num, 'int $_'), 0;
308            
309             print tied($count)->type, "\n"; # 'Int'
310            
311             $count++; # ok
312             $count = 2; # ok
313             $count = 3.14159; # ok, coerced to 3
314             $count = "Monkey!"; # dies
315              
316             While the examples in documentation (and the test suite) show type
317             constraints from L<Types::Standard>, any type constraint objects
318             supporting the L<Type::API> interfaces should work. This includes:
319              
320             =over
321              
322             =item *
323              
324             L<Moose::Meta::TypeConstraint> / L<MooseX::Types>
325              
326             =item *
327              
328             L<Mouse::Meta::TypeConstraint> / L<MouseX::Types>
329              
330             =item *
331              
332             L<Specio>
333              
334             =item *
335              
336             L<Type::Tiny|Type::Tiny::Manual>
337              
338             =back
339              
340             However, with Type::Tiny, you don't even need to C<< use Type::Tie >>.
341              
342             use Types::Standard qw( Int Num );
343            
344             tie my $count, Int->plus_coercions(Num, 'int $_'), 0;
345            
346             print tied($count)->type, "\n"; # 'Int'
347            
348             $count++; # ok
349             $count = 2; # ok
350             $count = 3.14159; # ok, coerced to 3
351             $count = "Monkey!"; # dies
352              
353             =head2 Cloning tied variables
354              
355             If you clone tied variables with C<dclone> from L<Storable>, the clone
356             will also be tied. The L<Clone> module is also able to successfully clone
357             tied variables. With other cloning techniques, your level of success may vary.
358              
359             =begin trustme
360              
361             =item ttie
362              
363             =end trustme
364              
365             =head1 BUGS
366              
367             Please report any bugs to
368             L<http://rt.cpan.org/Dist/Display.html?Queue=Type-Tiny>.
369              
370             =head1 SEE ALSO
371              
372             L<Type::API>,
373             L<Type::Tiny>,
374             L<Type::Utils>,
375             L<Moose::Manual::Types>,
376             L<MooseX::Lexical::Types>.
377              
378             =head1 AUTHOR
379              
380             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
381              
382             =head1 COPYRIGHT AND LICENCE
383              
384             This software is copyright (c) 2013-2014, 2018-2019, 2022-2023 by Toby Inkster.
385              
386             This is free software; you can redistribute it and/or modify it under
387             the same terms as the Perl 5 programming language system itself.
388              
389             =head1 DISCLAIMER OF WARRANTIES
390              
391             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
392             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
393             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
394