File Coverage

blib/lib/Data/Diver.pm
Criterion Covered Total %
statement 62 68 91.1
branch 36 42 85.7
condition 23 33 69.7
subroutine 10 10 100.0
pod 6 6 100.0
total 137 159 86.1


line stmt bran cond sub pod time code
1             package Data::Diver;
2 1     1   13459 use strict;
  1         3  
  1         59  
3            
4             require Exporter;
5 1     1   6 use vars qw( $VERSION @EXPORT_OK );
  1         3  
  1         123  
6             BEGIN {
7 1     1   96 $VERSION= 1.01_01;
8 1         9 @EXPORT_OK= qw( Dive DiveRef DiveVal DiveError DiveDie DiveClear );
9 1         3 *import= \&Exporter::import;
10 1         883 *isa= \&UNIVERSAL::isa;
11             }
12            
13            
14             # To figure out if an item supports being treated as a particular
15             # type of reference (hash ref, array ref, or scalar ref) we use:
16             # eval { my $x= DEREF_EXPR; 1 }
17             # Note that we are careful to not put 'DEREF_EXPR' into an "lvalue
18             # context" (to prevent autovivification) and to also avoid trying to
19             # convert the value into a number or boolean or such. The "; 1" is
20             # so that the eval always returns a true value unless something die()s.
21            
22             # Using 'ARRAY' eq ref($ref) is just a horrid alternative, as it would
23             # prevent these routines from being used on blessed data structures.
24            
25             # Using UNIVERSAL::isa($ref,'ARRAY') is a better alternative, but it
26             # still fails for more advanced cases of overloading or pathological
27             # cases of blessing into very-poorly-named packages. We use this for
28             # testing for CODE references, since eval { $ref->() } would actually
29             # run the code.
30            
31            
32             my @lastError;
33            
34            
35             sub _Error
36             {
37 9     9   41 @lastError= @_[2,0,1];
38 9         43 return;
39             }
40            
41            
42             sub DiveError
43             {
44 27     27 1 2220 return @lastError;
45             }
46            
47            
48             sub DiveClear
49             {
50 1     1 1 242 @lastError= ();
51             }
52            
53            
54             sub DiveDie
55             {
56 10 100   10 1 454 @_= Dive( @_ ) if 1 < @_;
57 10 100 100     92 return wantarray ? @_ : pop @_
    100          
58             if @_ || ! @lastError;
59 2         6 my( $errDesc, $ref, $svKey )= @lastError;
60 2         20 die "$errDesc using $$svKey on $ref (from Data::Diver).\n";
61             }
62            
63            
64             sub Dive
65             {
66 24 50   24 1 451 return if ! @_;
67 24         43 my $ref= shift @_;
68 24 50       52 return $ref if ! $ref;
69 24         63 while( @_ ) {
70 79         110 my $key= shift @_;
71 79 100 66     161 if( ! defined $key ) {
    100 100        
    100          
    100          
    100          
72             return _Error( $ref, \$key, "undef() on non-scalar-ref" )
73 14 100       20 if ! eval { my $x= $$ref; 1 };
  14         42  
  12         36  
74 12         42 $ref= $$ref;
75 65         738 } elsif( eval { my $x= $key->[0]; 1 }
  4         42  
76             && isa( $ref, 'CODE' )
77             ) {
78 4 100 66     25 if( @_ && ! defined $_[0] ) {
79 2         8 $ref= \ $ref->( @$key );
80             } else {
81 2         8 $ref= [ $ref->( @$key ) ];
82             }
83             } elsif( $key =~ /^-?\d+$/
84 23         44 && eval { my $x= $ref->[0]; 1 }
  22         83  
85             ) {
86 22 100 100     129 return _Error( $ref, \$key, "Index out of range" )
87             if $key < -@$ref
88             || $#$ref < $key;
89 20         74 $ref= $ref->[$key];
90 39         128 } elsif( eval { exists $ref->{$key} } ) {
91 34 50       39 if( eval { my $x= $$key; 1 } ) {
  34         230  
  0         0  
92 0         0 $ref= $ref->{$$key};
93             } else {
94 34         143 $ref= $ref->{$key};
95             }
96 5         20 } elsif( eval { my $x= $ref->{$key}; 1 } ) {
  2         16  
97 2         9 return _Error( $ref, \$key, "Key not present in hash" );
98             } else {
99 3         14 return _Error( $ref, \$key, "Not a valid type of reference" );
100             }
101             }
102 15         70 return $ref;
103             }
104            
105            
106             sub DiveVal :lvalue
107             {
108 2     2 1 3 ${ DiveRef( @_ ) };
  2         6  
109             }
110            
111            
112             sub DiveRef
113             {
114 17 100   17 1 2507 return if ! @_;
115 16         32 my $sv= \shift @_;
116 16 100       50 return $$sv if ! $$sv;
117 14         34 while( @_ ) {
118 39         59 my $key= shift @_;
119 39 100 33     78 if( ! defined $key ) {
    50 100        
    100 66        
    100 66        
      66        
120 7         31 $sv= \$$$sv;
121 32         269 } elsif( eval { my $x= $key->[0]; 1 }
  0         0  
122             && isa( $$sv, 'CODE' )
123             ) {
124 0 0 0     0 if( @_ && ! defined $_[0] ) {
125 0         0 $sv= \ $$sv->( @$key );
126             } else {
127 0         0 $sv= \[ $$sv->( @$key ) ];
128             }
129 32         309 } elsif( eval { my $x= $$key; 1 }
  11         56  
130             and ! defined($$sv)
131             || eval { my $x= $$sv->{0}; 1 }
132             ) {
133 10         36 $sv= \$$sv->{$$key};
134             } elsif( $key =~ /^-?\d+$/
135             and ! defined($$sv)
136             || eval { my $x= $$sv->[0]; 1 }
137             ) {
138 5         26 $sv= \$$sv->[$key];
139             } else {
140 17         113 $sv= \$$sv->{$key};
141             }
142             }
143 11         57 return $sv;
144             }
145            
146            
147             'Data::Diver';
148            
149             __END__