File Coverage

blib/lib/Hash/Typed.pm
Criterion Covered Total %
statement 112 112 100.0
branch 36 36 100.0
condition 17 17 100.0
subroutine 17 17 100.0
pod 0 3 0.0
total 182 185 98.3


line stmt bran cond sub pod time code
1             package Hash::Typed;
2 6     6   735667 use strict; use warnings; our $VERSION = '0.06';
  6     6   14  
  6         244  
  6         55  
  6         19  
  6         475  
3 6     6   37 use Carp qw/croak/; use Tie::Hash; our (@ISA);
  6     6   13  
  6         373  
  6         7037  
  6         6894  
  6         325  
4              
5             BEGIN {
6 6     6   11510 @ISA = qw(Tie::Hash);
7             }
8              
9             sub new {
10 6     6 0 777934 my ($package) = (shift);
11              
12 6         11 my $self = { };
13            
14 6         10 tie %{$self}, 'Hash::Typed', @_;
  6         26  
15              
16 5         25 bless $self, $package;
17             }
18              
19             sub TIEHASH {
20 47     47   1180900 my ($pkg) = shift;
21 47         90 my($self) = [];
22 47         56 push @{$self}, {}, [], [], 0;
  47         196  
23 47         78 $self = bless $self, $pkg;
24 47 100       109 if (ref $_[0]) {
25 16         67 my $spec = $self->PARSE(shift);
26 15         19 push @{$self}, $spec;
  15         27  
27             }
28 46         97 while (@_) {
29 50         92 $self->STORE(shift, shift);
30             }
31 43 100 100     136 if ($self->[4] && $self->[4]->{required}) {
32 4 100       9 if (ref $self->[4]->{required}) {
33 3         21 $self->REQUIRED($self->[4]->{required});
34             } else {
35 1         2 $self->REQUIRED([keys %{$self->[4]->{ordered_keys}}]);
  1         3  
36             }
37             }
38 42         102 return $self;
39             }
40              
41             sub FETCH {
42 387     387   661 my($self, $key) = (shift, shift);
43 387 100       1479 return exists( $self->[0]{$key} ) ? $self->[2][ $self->[0]{$key} ] : undef;
44             }
45              
46             sub STORE {
47 147     147   3449 my ($self, $key, $value) = @_;
48              
49 147 100       270 if ($self->[4]) {
50 56         164 my $described = $self->[4]->{keys}->{$key};
51              
52 56 100 100     151 if ($self->[4]->{strict} && !$described) {
53 3         848 croak "Strict mode enabled and passed key \"${key}\" does not exist in the specification.";
54             }
55              
56 53 100       226 $value = $described->($value)
57             if ($described);
58             }
59              
60 141 100 100     1426 if (exists $self->[0]{$key}) {
    100 100        
61 3         8 my($i) = $self->[0]{$key};
62 3         8 $self->[1][$i] = $key;
63 3         8 $self->[2][$i] = $value;
64 3         13 $self->[0]{$key} = $i;
65 44         98 } elsif ($self->[4] && defined $self->[4]{ordered_keys}{$key} && $self->[4]{ordered_keys}{$key} <= scalar @{$self->[1]}) {
66 37         76 my $i = $self->[4]{ordered_keys}{$key};
67 37         85 my $before = $self->[1]->[$i - 1];
68 37 100 100     84 $i = $i == 0 ? $i : --$i if ($before && ($self->[4]{ordered_keys}{$before} || -1) >= $i);
    100 100        
69 37         52 splice(@{$self->[1]}, $i, 0, $key);
  37         74  
70 37         39 splice(@{$self->[2]}, $i, 0, $value);
  37         61  
71 37         55 $self->[0]{$key} = $i;
72             $self->[0]{ $self->[1][$_] }++
73 37         65 for ($i+1..$#{$self->[1]});
  37         162  
74             } else {
75 101         123 push(@{$self->[1]}, $key);
  101         159  
76 101         108 push(@{$self->[2]}, $value);
  101         144  
77 101         143 $self->[0]{$key} = $#{$self->[1]};
  101         333  
78             }
79             }
80              
81             sub DELETE {
82 5     5   861 my ($self, $key) = @_;
83              
84 5 100       21 if (exists $self->[0]{$key}) {
85 4         42 my($i) = $self->[0]{$key};
86             $self->[0]{ $self->[1][$_] }--
87 4         11 for ($i+1..$#{$self->[1]});
  4         38  
88 4 100       15 $self->[3]-- if ( $i == $self->[3]-1 );
89 4         7 delete $self->[0]{$key};
90 4         11 splice @{$self->[1]}, $i, 1;
  4         10  
91 4         8 return (splice(@{$self->[2]}, $i, 1))[0];
  4         17  
92             }
93 1         6 return undef;
94             }
95              
96             sub CLEAR {
97 1     1   2 my ($self) = @_;
98 1         2 push @{$self}, {}, [], [], 0;
  1         5  
99             }
100              
101 29     29   454 sub EXISTS { exists $_[0]->[0]{ $_[1] }; }
102              
103             sub FIRSTKEY {
104 28     28   2832 $_[0][3] = 0;
105 28         49 &NEXTKEY;
106             }
107              
108             sub NEXTKEY {
109 112 100   112   162 return $_[0][1][ $_[0][3]++ ] if ($_[0][3] <= $#{ $_[0][1] } );
  112         350  
110 28         71 return undef;
111             }
112              
113 1     1   2 sub SCALAR { scalar(@{$_[0]->[1]}); }
  1         4  
114              
115             sub PARSE {
116 31     31 0 58 my ($self, $spec) = @_;
117 31         44 my (%keys, %described);
118 31         86 tie(%described, 'Hash::Typed');
119 31         43 while (@{$spec}) {
  107         206  
120 77         85 my ($key, $value) = (shift @{$spec}, shift @{$spec});
  77         119  
  77         115  
121 77 100       160 if ($key eq 'keys') {
122 16 100       1945 if (ref $value eq 'ARRAY') {
123 15         62 ($value) = $self->PARSE($value);
124 15         25 my $i = 0;
125 15         19 %keys = map { $_ => $i++ } keys %{$value};
  45         108  
  15         55  
126             } else {
127 1         147 croak "keys spec must currently be an ARRAY";
128             }
129             }
130 76         265 $described{$key} = $value;
131             }
132 30 100       137 $described{ordered_keys} = \%keys if scalar keys %keys;
133 30         99 return \%described;
134             }
135              
136             sub REQUIRED {
137 4     4 0 10 my ($self, $keys) = @_;
138 4         5 for my $key (@{$keys}) {
  4         8  
139 11 100       22 if (! defined $self->[0]{$key}) {
140 1         310 croak "Required key $key not set.";
141             }
142             }
143             }
144              
145             1;
146              
147             __END__;