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