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