File Coverage

blib/lib/Hash/Typed.pm
Criterion Covered Total %
statement 103 110 93.6
branch 31 34 91.1
condition 17 18 94.4
subroutine 15 17 88.2
pod 0 3 0.0
total 166 182 91.2


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