File Coverage

blib/lib/Type/Tie.pm
Criterion Covered Total %
statement 134 134 100.0
branch 31 38 81.5
condition 9 16 56.2
subroutine 48 48 100.0
pod 1 1 100.0
total 223 237 94.0


line stmt bran cond sub pod time code
1 7     7   70994 use 5.008001;
  7         36  
2 7     7   38 use strict;
  7         12  
  7         168  
3 7     7   31 use warnings;
  7         17  
  7         206  
4              
5 7     7   47 use Carp ();
  7         14  
  7         109  
6 7     7   2829 use Exporter::Tiny ();
  7         26443  
  7         141  
7 7     7   49 use Scalar::Util ();
  7         17  
  7         16705  
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.004000';
15             our @ISA = qw( Exporter::Tiny );
16             our @EXPORT = qw( ttie );
17            
18             $VERSION =~ tr/_//d;
19            
20             sub ttie (\[$@%]@)#>&%*/&<%\$[]^!@;@)
21             {
22 14     14 1 1187 my ( $ref, $type, @vals ) = @_;
23            
24 14 100       75 if ( 'HASH' eq ref $ref ) {
    100          
25 4         35 tie %$ref, "Type::Tie::HASH", $type;
26 4 100       25 %$ref = @vals if @vals;
27             }
28             elsif ( 'ARRAY' eq ref $ref ) {
29 4         36 tie @$ref, "Type::Tie::ARRAY", $type;
30 4 100       34 @$ref = @vals if @vals;
31             }
32             else {
33 6         52 tie $$ref, "Type::Tie::SCALAR", $type;
34 6 100       44 $$ref = $vals[-1] if @vals;
35             }
36 14         46 return $ref;
37             }
38             };
39              
40             {
41             package Type::Tie::BASE;
42             our $AUTHORITY = 'cpan:TOBYINK';
43             our $VERSION = '2.004000';
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   16 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 20     20   35 my $self = shift;
78 20         36 my $type = $_[0];
79            
80 20         110 $self->_TYPE( $type );
81            
82 20 100 66     160 if ( Scalar::Util::blessed( $type ) and $type->isa( 'Type::Tiny' ) ) {
83 16         50 $self->_CHECK( $type->compiled_check );
84 16 100       50 $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 26     26   60 : sub { $type->check( $_[0] ) }
95 4 50       47 );
96             $self->_COERCE(
97             $type->can( 'has_coercion' ) && $type->can( 'coerce' ) && $type->has_coercion
98 20     20   47 ? sub { $type->coerce( $_[0] ) }
99             : undef
100 4 100 66     43 );
101             }
102             }
103            
104             # Only used if the type has no get_message method
105             sub _dd {
106 1     1   671 require Type::Tiny;
107 1         7 goto \&Type::Tiny::_dd;
108             }
109            
110             sub coerce_and_check_value {
111 71     71   116 my $self = shift;
112 71         142 my $check = $self->_CHECK;
113 71         107 my $coerce = $self->_COERCE;
114            
115             my @vals = map {
116 71 100       185 my $val = $coerce ? $coerce->( $_ ) : $_;
  90         179  
117 90 100       542 if ( not $check->( $val ) ) {
118 23         74 my $type = $self->_TYPE;
119 23 100 66     130 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 67         289 $val;
126             } ( my @cp = @_ ); # need to copy @_ for Perl < 5.14
127            
128 48 100       221 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   62 my ( $o, $cloning ) = @_;
139 3 50       10 Carp::croak( "Storable::freeze only supported for dclone-ing" )
140             unless $cloning;
141            
142 3         7 my $type = $o->_TYPE;
143 3         11 my $refaddr = Scalar::Util::refaddr( $type );
144 3   50     19 $tmp_clone_types{$refaddr} ||= [ $type, 0 ];
145 3         5 ++$tmp_clone_types{$refaddr}[1];
146            
147 3         216 return ( $refaddr, $o->_REF );
148             }
149            
150             sub STORABLE_thaw {
151 3     3   14 my ( $o, $cloning, $refaddr, $o2 ) = @_;
152 3 50       8 Carp::croak( "Storable::thaw only supported for dclone-ing" )
153             unless $cloning;
154            
155 3         11 $o->_THAW( $o2 ); # implement in child classes
156            
157 3         7 my $type = $tmp_clone_types{$refaddr}[0];
158             --$tmp_clone_types{$refaddr}[1]
159 3 50       11 or delete $tmp_clone_types{$refaddr};
160 3         7 $o->_set_type($type);
161             }
162             };
163              
164             {
165             package Type::Tie::ARRAY;
166             our $AUTHORITY = 'cpan:TOBYINK';
167             our $VERSION = '2.004000';
168             our @ISA = qw( Type::Tie::BASE );
169            
170             $VERSION =~ tr/_//d;
171            
172             sub TIEARRAY {
173 5     5   13 my $class = shift;
174 5         23 my $self = bless( [ $class->_DEFAULT ], $class );
175 5         37 $self->_set_type( $_[0] );
176 5         25 $self;
177             }
178 6     6   20 sub _DEFAULT { [] }
179 64     64   2777 sub FETCHSIZE { scalar @{ $_[0]->_REF } }
  64         151  
180 1     1   7 sub STORESIZE { $#{ $_[0]->_REF } = $_[1] }
  1         5  
181 17     17   104 sub STORE { $_[0]->_REF->[ $_[1] ] = $_[0]->coerce_and_check_value( $_[2] ) }
182 54     54   226 sub FETCH { $_[0]->_REF->[ $_[1] ] }
183 3     3   7 sub CLEAR { @{ $_[0]->_REF } = () }
  3         22  
184 1     1   6 sub POP { pop @{ $_[0]->_REF } }
  1         4  
185 9     9   340 sub PUSH { my $s = shift; push @{$s->_REF}, $s->coerce_and_check_value( @_ ) }
  9         17  
  9         76  
186 3     3   8 sub SHIFT { shift @{ $_[0]->_REF } }
  3         9  
187 7     7   33 sub UNSHIFT { my $s = shift; unshift @{$s->_REF}, $s->coerce_and_check_value( @_ ) }
  7         16  
  7         27  
188 1     1   411 sub EXISTS { exists $_[0]->_REF->[ $_[1] ] }
189 1     1   7 sub DELETE { delete $_[0]->_REF->[ $_[1] ] }
190       3     sub EXTEND {}
191             sub SPLICE {
192 2     2   9 my $o = shift;
193 2         4 my $sz = scalar @{$o->_REF};
  2         7  
194 2 50       7 my $off = @_ ? shift : 0;
195 2 50       6 $off += $sz if $off < 0;
196 2 50       7 my $len = @_ ? shift : $sz-$off;
197 2         3 splice @{$o->_REF}, $off, $len, $o->coerce_and_check_value( @_ );
  2         8  
198             }
199 1     1   2 sub _THAW { @{ $_[0]->_INIT_REF } = @{ $_[1] } }
  1         6  
  1         4  
200             };
201              
202             {
203             package Type::Tie::HASH;
204             our $AUTHORITY = 'cpan:TOBYINK';
205             our $VERSION = '2.004000';
206             our @ISA = qw( Type::Tie::BASE );
207            
208             $VERSION =~ tr/_//d;
209            
210             sub TIEHASH {
211 5     5   16 my $class = shift;
212 5         21 my $self = bless( [ $class->_DEFAULT ], $class );
213 5         41 $self->_set_type( $_[0] );
214 5         19 $self;
215             }
216 6     6   21 sub _DEFAULT { +{} }
217 14     14   637 sub STORE { $_[0]->_REF->{ $_[1] } = $_[0]->coerce_and_check_value( $_[2] ) }
218 11     11   64 sub FETCH { $_[0]->_REF->{ $_[1] } }
219 7     7   1624 sub FIRSTKEY { my $a = scalar keys %{ $_[0]->_REF }; each %{ $_[0]->_REF } }
  7         26  
  7         16  
  7         35  
220 11     11   21 sub NEXTKEY { each %{ $_[0]->_REF } }
  11         39  
221 11     11   147 sub EXISTS { exists $_[0]->_REF->{ $_[1] } }
222 3     3   25 sub DELETE { delete $_[0]->_REF->{ $_[1] } }
223 3     3   9 sub CLEAR { %{ $_[0]->_REF } = () }
  3         26  
224 1     1   406 sub SCALAR { scalar %{ $_[0]->_REF } }
  1         6  
225 1     1   2 sub _THAW { %{ $_[0]->_INIT_REF } = %{ $_[1] } }
  1         7  
  1         3  
226             };
227              
228             {
229             package Type::Tie::SCALAR;
230             our $AUTHORITY = 'cpan:TOBYINK';
231             our $VERSION = '2.004000';
232             our @ISA = qw( Type::Tie::BASE );
233            
234             $VERSION =~ tr/_//d;
235            
236             sub TIESCALAR {
237 7     7   22 my $class = shift;
238 7         35 my $self = bless( [ $class->_DEFAULT ], $class );
239 7         64 $self->_set_type($_[0]);
240 7         45 $self;
241             }
242 8     8   16 sub _DEFAULT { my $x; \$x }
  8         29  
243 22     22   3712 sub STORE { ${ $_[0]->_REF } = $_[0]->coerce_and_check_value( $_[1] ) }
  16         80  
244 14     14   1242 sub FETCH { ${ $_[0]->_REF } }
  14         60  
245 1     1   4 sub _THAW { ${ $_[0]->_INIT_REF } = ${ $_[1] } }
  1         5  
  1         3  
246             };
247              
248             1;
249              
250             __END__