File Coverage

blib/lib/Data/DPath/Validator/Visitor.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Data::DPath::Validator::Visitor;
2             our $VERSION = '0.093411';
3              
4             #ABSTRACT: Data::Visitor subclass for generating DPaths
5              
6              
7              
8 1     1   23028 use Moose;
  0            
  0            
9             use Moose::Util::TypeConstraints;
10             use MooseX::Types::Moose(':all');
11             use Scalar::Util;
12             use namespace::autoclean;
13              
14             extends 'Data::Visitor';
15              
16             use constant DEBUG => $ENV{DATA_DPATH_VALIDATOR_DEBUG};
17              
18              
19             has 'templates' =>
20             (
21             is => 'ro',
22             isa => ArrayRef[Str],
23             traits => ['Array'],
24             default => sub { [ ] },
25             handles =>
26             {
27             add_template => 'push',
28             },
29             );
30              
31              
32             has 'current_template' =>
33             (
34             is => 'ro',
35             isa => Str,
36             default => '',
37             traits => ['String'],
38             handles =>
39             {
40             append_text => 'append',
41             prepend_text => 'prepend',
42             reset_template => 'clear',
43             }
44             );
45              
46              
47             has 'template_stack' =>
48             (
49             is => 'ro',
50             isa => ArrayRef[Str],
51             default => sub { [ ] },
52             traits => ['Array'],
53             handles =>
54             {
55             push_template => 'push',
56             pop_template => 'pop',
57             },
58             );
59              
60              
61             has 'structure_depth' =>
62             (
63             is => 'ro',
64             isa => Int,
65             traits => ['Counter'],
66             default => 0,
67             handles =>
68             {
69             lower => 'inc',
70             higher => 'dec',
71             },
72             );
73              
74              
75              
76             has 'value_type' =>
77             (
78             is => 'rw',
79             isa => enum([qw/ArrayElem HashVal HashKey NONE/]),
80             default => 'NONE'
81             );
82              
83              
84              
85             sub dive
86             {
87             my $self = shift;
88              
89             warn 'DIVE: '. $self->current_template if DEBUG;
90             $self->push_template($self->current_template);
91             $self->lower();
92             }
93              
94              
95              
96             sub rise
97             {
98             my $self = shift;
99              
100             warn 'PRE-RISE: '. $self->current_template if DEBUG;
101             my $template = $self->pop_template();
102             $self->reset_template();
103             $self->append_text($template);
104             $self->higher();
105             warn 'POST-RISE: '. $self->current_template if DEBUG;
106             }
107              
108              
109             sub visit_value
110             {
111             my ($self, $val) = @_;
112             warn 'VISIT: '. $self->current_template if DEBUG;
113            
114             if($self->value_type eq 'ArrayElem')
115             {
116             if($val eq '*')
117             {
118             $self->append_text("/$val");
119             $self->add_template($self->current_template);
120             return;
121             }
122             $self->append_text('/.[ value ');
123             }
124             elsif($self->value_type eq 'HashVal')
125             {
126             if($val eq '*')
127             {
128             $self->append_text("/$val");
129             $self->add_template($self->current_template);
130             return;
131             }
132             $self->append_text('/*[ value ');
133             }
134             elsif($self->value_type eq 'HashKey')
135             {
136             $self->append_text($val);
137             $self->add_template($self->current_template);
138             return;
139             }
140             else
141             {
142             if($self->structure_depth == 0)
143             {
144             $self->dive();
145             $self->append_text('.[ value ');
146             if(Scalar::Util::looks_like_number($val))
147             {
148             $self->append_text("== $val");
149             }
150             else
151             {
152             $self->append_text("eq '$val'");
153             }
154             $self->append_text(']');
155             $self->add_template($self->current_template);
156             $self->rise();
157             return;
158             }
159             $self->append_text('.[ value ');
160             }
161              
162             if(Scalar::Util::looks_like_number($val))
163             {
164             $self->append_text("== $val");
165             }
166             else
167             {
168             $self->append_text("eq '$val'");
169             }
170              
171             $self->append_text(']');
172             $self->add_template($self->current_template);
173             }
174              
175              
176             around visit_hash => sub
177             {
178             my ($orig, $self, $hash) = @_;
179             $self->dive();
180             $self->append_text('/') if $self->structure_depth > 1;
181             warn 'HASH: '. $self->current_template if DEBUG;
182             $self->$orig($hash);
183             $self->rise();
184             };
185              
186              
187             around visit_hash_key => sub
188             {
189             my ($orig, $self, $key) = @_;
190             $self->value_type('HashKey');
191             warn 'HASHKEY: '. $self->current_template if DEBUG;
192             $self->$orig($key);
193             $self->value_type('NONE');
194             };
195              
196              
197             around visit_hash_value => sub
198             {
199             my ($orig, $self, $val) = @_;
200             $self->value_type('HashVal');
201             warn 'HASHVAL: '. $self->current_template if DEBUG;
202             $self->$orig($val);
203             $self->value_type('NONE');
204             };
205              
206              
207             around visit_hash_entry => sub
208             {
209             my ($orig, $self, $key, $value, $hash) = @_;
210             $self->dive();
211             warn 'HASHENTRY: '. $self->current_template if DEBUG;
212             $self->$orig($key, $value, $hash);
213             $self->rise();
214             };
215              
216              
217             around visit_array => sub
218             {
219             my ($orig, $self, $array) = @_;
220             $self->dive();
221             $self->append_text('/') if $self->structure_depth > 1;
222             warn 'ARRAY: '. $self->current_template if DEBUG;
223             $self->$orig($array);
224             $self->rise();
225             };
226              
227              
228             around visit_array_entry => sub
229             {
230             my ($orig, $self, $elem, $index, $array) = @_;
231             $self->dive();
232             $self->value_type('ArrayElem');
233             $self->append_text("*[ idx == $index ]");
234             warn 'ARRAYENTRY: '. $self->current_template if DEBUG;
235             $self->$orig($elem, $index, $array);
236             $self->rise();
237             $self->value_type('NONE');
238             };
239              
240              
241             around visit => sub
242             {
243             my ($orig, $self) = (shift, shift);
244            
245             if($self->structure_depth == 0)
246             {
247             $self->append_text('/');
248             }
249             my @ret = $self->$orig(@_);
250            
251             $self->reset_template() if $self->structure_depth == 0;
252              
253             defined wantarray ? @ret : $ret[0];
254              
255             };
256              
257             __PACKAGE__->meta->make_immutable();
258             1;
259              
260              
261              
262             =pod
263              
264             =head1 NAME
265              
266             Data::DPath::Validator::Visitor - Data::Visitor subclass for generating DPaths
267              
268             =head1 VERSION
269              
270             version 0.093411
271              
272             =head1 SYNOPSIS
273              
274             use Data::DPath::Validator::Visitor;
275             my $v = Data::DPath::Validator::Visitor->new();
276             $v->visit({foo => '*'});
277              
278             $v->templates; # [ '/foo/*' ]
279              
280             =head1 DESCRIPTION
281              
282             Data::DPath::Validator::Visitor subclasses Data::Visitor to parse arbitrary
283             Perl data structures into Data::DPath paths. It stores these paths in its
284             templates attribute.
285              
286             =cut
287              
288             =pod
289              
290             =head1 ATTRIBUTES
291              
292             =head2 templates is: ro, isa: ArrayRef[Str], traits: Array
293              
294             templates contains the parsed paths from calling visit on template data
295              
296             handles =>
297             {
298             add_template => 'push'
299             }
300              
301             =cut
302              
303             =pod
304              
305             =head2 current_template is: ro, isa: Str, default: '', traits: String
306              
307             current_template holds the template as it is being build prior to being added
308             to the templates attribute
309              
310             handles =>
311             {
312             append_text => 'append',
313             prepend_text => 'prepend',
314             reset_template => 'clear',
315             }
316              
317             =cut
318              
319             =pod
320              
321             =head2 template_stack is: ro, isa: ArrayRef[Str], default: [], traits: Array
322              
323             template_stack maintains the templates as we branch down the data structure. At
324             each level down, the current template is pushed onto the stack and popped off
325             when the that branch bottom is reached.
326              
327             handles =>
328             {
329             push_template => 'push',
330             pop_template => 'pop',
331             },
332              
333             =cut
334              
335             =pod
336              
337             =head2 structure_depth is: ro, isa: Int, default: 0, traits: Counter
338              
339             structure_depth keeps track of how deep we are in the data structure.
340              
341             handles =>
342             {
343             lower => 'inc',
344             higher => 'dec',
345             },
346              
347             =cut
348              
349             =pod
350              
351             =head2 value_type is: rw, isa: enum ArrayElem HashVal HashKey NONE, default:NONE
352              
353             value_type keeps track of what kind of element we are viewing inside
354             visit_value. This attribute is important for determining path construction.
355              
356             =cut
357              
358             =pod
359              
360             =head1 METHODS
361              
362             =head2 dive
363              
364             dive() increases our depth into the data structure, pushing the current
365             template onto the template stack.
366              
367             =cut
368              
369             =pod
370              
371             =head2 rise
372              
373             rise() decreases our depth from the data structure, popping a template from the
374             template stack and replacing the current_template with it.
375              
376             =cut
377              
378             =pod
379              
380             =head2 visit_value
381              
382             visit_value is overriden to provide the meat of the DPath generation algorithm.
383             It reads $self->value_type to know how append to the current_template.
384              
385             =cut
386              
387             =pod
388              
389             =head2 around visit_hash
390              
391             visit_hash is advised to adjust our depth and prep our current template.
392              
393             After calling the original method, depth is adjusted back.
394              
395             =cut
396              
397             =pod
398              
399             =head2 around visit_hash_key
400              
401             visit_hash_key is advised to set value_type to HashKey prior to calling the
402             original method
403              
404             =cut
405              
406             =pod
407              
408             =head2 around visit_hash_value
409              
410             visit_hash_value is advised to set value_type to HashVal prior to calling the
411             original method
412              
413             =cut
414              
415             =pod
416              
417             =head2 around visit_hash_entry
418              
419             visit_hash_entry is advised to adjust out depth prior to evaluating the key and
420             value.
421              
422             After calling the original method, depth is adjusted back.
423              
424             =cut
425              
426             =pod
427              
428             =head2 around visit_array
429              
430             visit_array is advised to adjust our depth and prep our current template.
431              
432             After calling the original method, depth is adjusted back.
433              
434             =cut
435              
436             =pod
437              
438             =head2 around visit_array_entry
439              
440             visit_array_entry is advised to set the value_type to ArrayElem and to also
441             prep the current template with the array index before calling the original.
442              
443             After calling the original method, depth is adjusted back.
444              
445             =cut
446              
447             =pod
448              
449             =head2 around visit
450              
451             visit is advised to prep the initial template if the structure depth is zero
452             before calling the original. Afterward, if the depth has resolved back to zero,
453             the current template is reset.
454              
455             =head1 AUTHOR
456              
457             Nicholas Perez <nperez@cpan.org>
458              
459             =head1 COPYRIGHT AND LICENSE
460              
461             This software is copyright (c) 2009 by Infinity Interactive.
462              
463             This is free software; you can redistribute it and/or modify it under
464             the same terms as the Perl 5 programming language system itself.
465              
466             =cut
467              
468              
469             __END__
470