File Coverage

blib/lib/Tie/Indexer.pm
Criterion Covered Total %
statement 57 185 30.8
branch 2 80 2.5
condition 1 50 2.0
subroutine 17 24 70.8
pod 0 10 0.0
total 77 349 22.0


line stmt bran cond sub pod time code
1             package Tie::Indexer;
2 1     1   6 use strict;
  1         2  
  1         36  
3 1     1   5 use B::Deparse;
  1         2  
  1         57  
4             my $dp = new B::Deparse("-sCi0");
5             our $VERSION="0.1";
6              
7 1     1   622 BEGIN {
8 1     1   5 no strict;
  1         2  
  1         26  
9 1     1   4 no warnings;
  1         2  
  1         64  
10 1     1   7 use constant Equals => do { package main; sub { $_[0] eq $_[1] } };
  1         12  
  1         17  
  1         105  
  0         0  
11 1     1   5 use constant Not => do { package main; sub { !exists $_[1]->{$_[0]}; } };
  1         2  
  1         1  
  1         86  
  0         0  
12 1     1   7 use constant Exists => do { package main; sub { exists $_[0]->{$_[1]}; } };
  1         1  
  1         2  
  1         107  
  0         0  
13 1     1   6 use constant IndexSimple => do { package main; sub { Tie::Indexer::get_value(@_); } };
  1         3  
  1         2  
  1         95  
  0         0  
14 1     1   6 use constant IndexExists => do{ package main; sub { keys %{Tie::Indexer::get_value(@_)}; } };
  1         18  
  1         2  
  1         40  
  0         0  
  0         0  
15             }
16              
17             my %operators = (
18             '=' => Equals,
19             '!' => Not,
20             'E' => Exists,
21             );
22              
23             my %codeindex;
24             sub code2text {
25 2     2 0 4 my ($operator) = @_;
26 2 50       8 return undef if !defined $operator;
27 2 50       8 if (!exists $codeindex{$operator}) {
28 2         3948 $codeindex{$operator} = $dp->coderef2text($operator);
29 2         13 $codeindex{$operator} =~ s/\n/ /g;
30             }
31 2         12 return $codeindex{$operator};
32             }
33              
34             my %indexers = (
35             code2text(Equals) => IndexSimple,
36             code2text(Exists) => IndexExists,
37             );
38              
39             sub get_value {
40 0     0 0 0 my ($tie, $expr, $node) = @_;
41 0 0 0     0 return $node if !defined $expr || $expr eq '';
42 0         0 my ($prefix) = ($expr =~ m/^([a-z]+);/);
43 0         0 $expr =~ s/^[a-z]+;//;
44 0         0 my @path = split("/",$expr);
45 0         0 my $value = $node;
46 0         0 while (@path) {
47 0 0       0 if (UNIVERSAL::isa($value,'HASH')) {
    0          
48 0         0 $value = $value->{shift @path};
49             } elsif (UNIVERSAL::isa($value,'ARRAY')) {
50 0         0 $value = $value->[shift @path];
51             } else {
52 0         0 undef $value;
53 0         0 undef @path;
54             }
55             }
56 0         0 return $value;
57             }
58              
59             sub deindex_node {
60 3     3 0 5 my ($tie, $node, $nodeid) = @_;
61 3         7 foreach my $index (get_indices($tie)) {
62 0 0       0 if ($$index[3]) {
63 0         0 delete $$index[0]{$_}{$nodeid} foreach ($$index[3]->($tie,$$index[2],$node,$$index[0]));
64             } else {
65 0         0 delete $$index[0]{$_}{$nodeid} foreach ($$index[4]->($tie,$$index[2],$node,$$index[0]));
66             }
67             }
68             }
69              
70             sub index_node_single {
71 0     0 0 0 my ($tie, $node, $nodeid, $index) = @_;
72 0 0       0 if ($$index[3]) {
73 1     1   7 no warnings 'uninitialized';
  1         1  
  1         380  
74 0         0 $$index[0]{$_}{$nodeid} = undef foreach ($$index[3]->($tie,$$index[2],$node,$$index[0]));
75             } else {
76 0         0 foreach my $value ($$index[4]->($tie,$$index[2],$node,$$index[0])) {
77 0 0       0 $$index[0]{$value}{$nodeid} = undef if ($$index[1]->($value,get_value($tie,$$index[2],$node)));
78             }
79             }
80             }
81              
82             sub index_node {
83 3     3 0 5 my ($tie, $node, $nodeid) = @_;
84 3         5 foreach my $index (get_indices($tie)) {
85 0         0 index_node_single(@_,$index);
86             }
87             }
88              
89             sub get_indices {
90 6     6 0 7 my ($tie) = @_;
91 6         6 my @res;
92 6   50     16 my $index = $tie->_get_index() || {};
93 6         20 while (my ($expr, $eindex) = each %$index) {
94 0 0       0 next if $expr =~ m/^\x{200b}[^\x{200b}]/;
95 0         0 $expr =~ s/^\x{200b}\x{200b}/\x{200b}/;
96 0 0       0 my $values = eval "no strict; package main; return sub ".$$eindex{"\x{200b}values"} if exists $$eindex{"\x{200b}values"};
97 1   0 1   6 $values ||= do { no strict; package main; sub { keys %{$_[3]} } };
  1     0   2  
  1         14849  
  0         0  
  0         0  
  0         0  
  0         0  
98 0         0 while (my ($operator, $oindex) = each %$eindex) {
99 0 0       0 my $indexer = eval "no strict; package main; return sub ".$$index{"\x{200b}indexers"}{$operator} if exists $$index{"\x{200b}indexers"}{$operator};
100 0         0 push @res, [$oindex, eval "no strict; package main; return sub ".$operator, $expr, $indexer, $values];
101             }
102             }
103 6         29 return @res;
104             }
105              
106             sub get_index {
107 0     0 0   my ($tie, $expr, $operator, $force) = @_;
108 0           $operator = code2text($operator);
109 0   0       my $index = $tie->_get_index() || return undef;
110 0 0         $expr = '' if (!defined $expr);
111 0           $expr =~ s/^\x{200b}/\x{200b}\x{200b}/;
112 0 0 0       return undef if (!$force && (!exists $$index{$expr} || !exists $$index{$expr}{$operator}));
      0        
113 0           return $$index{$expr}{$operator};
114             }
115              
116             sub add_index {
117 0     0 0   my ($tie, $operator, $expr, $indexer, $values) = @_;
118 0 0         $operator = $operators{$operator} if (exists $operators{$operator});
119 0           $operator = code2text($operator);
120 0 0 0       $indexer ||= $indexers{$operator} if (exists $indexers{$operator});
121 0           $indexer = code2text($indexer);
122 0           $values = code2text($values);
123              
124 0           my $index = $tie->_get_index(1);
125 0 0 0       $$index{"\x{200b}indexers"}{$operator} ||= $indexer if (defined $indexer);
126 0           $expr =~ s/^\x{200b}/\x{200b}\x{200b}/;
127 0   0       $$index{$expr} ||= {};
128 0           $index = $$index{$expr};
129 0 0 0       $$index{"\x{200b}values"} ||= $values if (defined $values);
130 0   0       $$index{$operator} ||= {};
131             }
132              
133             sub search {
134 0     0 0   my $tie = shift;
135 0           my $base = shift;
136 0           my ($expr, $operator, $value) = ($base, Equals, undef);
137 0 0         if (ref($base) eq 'HASH') {
138 0 0         return (wantarray?values(%$base):(values %$base)[0]) if !@_;
    0          
139 0           $expr = shift;
140             } else {
141 0           undef $base;
142             }
143              
144 0 0         $expr = $operators{$expr} if (exists $operators{$expr});
145              
146 0 0 0       if (!ref($expr) || ref($expr) ne 'CODE') {
    0          
147 0           $value = shift;
148 0 0         if (ref($value) eq 'CODE') {
    0          
149 0           $operator = $value;
150 0           $value = shift;
151             } elsif (exists $operators{$value}) {
152 0           $operator = $operators{$value};
153 0           $value = shift;
154             }
155             } elsif (ref($expr) eq 'CODE') {
156 0           $operator = $expr;
157 0           $expr = undef;
158 0           $value = undef;
159             }
160 0           my $index;
161 0 0         if ($operator ne Not) {
162 0 0 0       if (defined ($index = get_index($tie, $expr, $operator))) {
    0          
163             # do nothing
164             } elsif (!$base && defined ($index = get_index($tie, $expr, Equals))) {
165             # TODO: use an Equals index for base matches as well?
166             # perhaps some heuristic: if (keys %$base > keys %$index)
167 0           my %res;
168 0           foreach my $exp (keys %$index) {
169 0           local $_ = $exp;
170 0 0         if ($operator->($exp,$value)) {
171 0   0       $res{$_} ||= $tie->FETCH($_) foreach (keys %{$$index{$exp}});
  0            
172 0 0         last if !wantarray;
173             }
174             }
175 0           return search($tie,\%res,@_);
176             }
177             }
178 0 0 0       if (!$index && !$base && (wantarray || $operator eq Not)) {
      0        
      0        
179 0           $base = {};
180 0           my $key = $tie->FIRSTKEY;
181 0           while (defined $key) {
182 0           $$base{$key} = $tie->FETCH($key);
183 0           $key = $tie->NEXTKEY;
184             }
185             }
186              
187 0 0         if ($operator eq Not) {
188 0 0         if (!$expr) {
189 0           return search($tie,$base,@_,{ %$base },Not);
190             } else {
191 0           delete $$expr{$_} foreach (keys %$base);
192 0           return search($tie,$expr);
193             }
194             }
195 0 0         if ($index) {
196 0 0         if (defined ($index = $$index{$value})) {
197 0 0         if (!$base) {
198 0           $base = { map { ($_ => $tie->FETCH($_)) } keys %$index };
  0            
199             } else {
200 0           foreach my $key (keys %$base) {
201 0 0         delete $$base{$key} if !exists $$index{$key};
202             }
203             }
204 0           return search($tie,$base,@_);
205             }
206 0           return ();
207             }
208              
209 0 0         if (!$base) {
210 0           my $key = $tie->FIRSTKEY;
211 0           while (defined $key) {
212 0           my $node = $tie->FETCH($key);
213 0           local $_ = get_value($tie,$expr,$node);
214 0 0 0       return $node if $operator->($_,$value) && search($tie,{$key => $node},@_);
215 0           $key = $tie->NEXTKEY;
216             }
217 0           return undef;
218             }
219              
220 0           while (my ($key, $node) = each %$base) {
221 0           local $_ = get_value($tie,$expr,$node);
222 0 0         delete $$base{$key} if !$operator->($_,$value);
223             }
224 0           return search($tie,$base,@_);
225             }
226              
227             sub build_index {
228 0     0 0   my ($tie) = @_;
229 0           foreach my $index (get_indices($tie)) {
230 0           foreach my $key (keys %{$$index[0]}) {
  0            
231 0           undef $$index[0]{$key};
232             }
233 0           my $key = $tie->FIRSTKEY;
234 0           while (defined $key) {
235 0           index_node_single($tie,$tie->FETCH($key),$key,$index);
236 0           $key = $tie->NEXTKEY;
237             }
238             }
239             }
240              
241 1     1   16 no warnings;
  1         3  
  1         66  
242             "Dahut!";
243             __END__