File Coverage

blib/lib/Data/Walk/Graft.pm
Criterion Covered Total %
statement 43 44 97.7
branch 14 18 77.7
condition 6 9 66.6
subroutine 10 10 100.0
pod 1 1 100.0
total 74 82 90.2


line stmt bran cond sub pod time code
1             package Data::Walk::Graft;
2             our $AUTHORITY = 'cpan:JANDREW';
3 2     2   1035 use version; our $VERSION = version->declare('v0.28.0');
  2         4  
  2         10  
4             ###InternalExtracteDGrafT warn "You uncovered internal logging statements for Data::Walk::Graft-$VERSION";
5             ###InternalExtracteDGrafT use Data::Dumper;
6 2     2   181 use 5.010;
  2         6  
7 2     2   6 use utf8;
  2         3  
  2         57  
8 2     2   42 use Moose::Role;
  2         1  
  2         11  
9             requires qw(
10             _get_had_secondary _process_the_data _dispatch_method
11             );
12 2     2   6619 use MooseX::Types::Moose qw( Bool ArrayRef HashRef );
  2         4  
  2         15  
13 2     2   6449 use Carp qw( cluck );
  2         2  
  2         823  
14              
15             #########1 Package Variables 3#########4#########5#########6#########7#########8#########9
16              
17             $| = 1;
18             my $graft_keys = {
19             scion_ref => 'primary_ref',
20             tree_ref => 'secondary_ref',
21             };
22              
23             #########1 Dispatch Tables 3#########4#########5#########6#########7#########8#########9
24              
25              
26              
27             #########1 Public Attributes 3#########4#########5#########6#########7#########8#########9
28              
29             has 'graft_memory' =>(
30             isa => Bool,
31             writer => 'set_graft_memory',
32             reader => 'get_graft_memory',
33             predicate => 'has_graft_memory',
34             clearer => 'clear_graft_memory',
35             );
36              
37             #########1 Public Methods 3#########4#########5#########6#########7#########8#########9
38              
39             sub graft_data{#Used to convert names
40 12     12 1 5076 my ( $self, @args ) = @_;
41             ###InternalExtracteDGrafT warn "Made it to graft_data with input:" . Dumper( @args );
42 12 50 33     49 my $passed_ref = ( @args == 1 and is_HashRef( $args[0] ) ) ? $args[0] : { @args } ;
43             ###InternalExtracteDGrafT warn "reconciled hashref:" . Dumper( $passed_ref );
44 12 50       19 if( $passed_ref->{scion_ref} ){
45 12         13 $passed_ref->{before_method} = '_graft_before_method';
46 12         406 $self->_clear_grafted_positions;
47             ###InternalExtracteDGrafT warn "Start recursive parsing with:" . Dumper( $passed_ref );
48 12         122 $passed_ref = $self->_process_the_data( $passed_ref, $graft_keys );
49             }else{
50 0         0 cluck "No scion was provided to graft";
51             }
52             ###InternalExtracteDGrafT warn "End recursive parsing with:" . Dumper( $passed_ref );
53 12         77 return $passed_ref->{tree_ref};
54             }
55              
56             #########1 Private Attributes 3#########4#########5#########6#########7#########8#########9
57              
58             has '_grafted_positions' =>(
59             traits => ['Array'],
60             isa => ArrayRef[HashRef],
61             handles => {
62             _remember_graft_item => 'push',
63             number_of_scions => 'count',
64             },
65             clearer => '_clear_grafted_positions',
66             predicate => 'has_grafted_positions',
67             reader => 'get_grafted_positions',
68             );
69              
70             #########1 Private Methods 3#########4#########5#########6#########7#########8#########9
71              
72             sub _graft_before_method{
73 43     43   31 my ( $self, $passed_ref ) = @_;
74             ###InternalExtracteDGrafT warn "reached _graft_before_method with input:" . Dumper( $passed_ref );
75 43 100 100     131 if( $passed_ref->{primary_type} eq 'SCALAR' and
    100          
76             $passed_ref->{primary_ref} eq 'IGNORE' ){
77             ###InternalExtracteDGrafT warn "nothing to see here! IGNOREing ...";
78 6         8 $passed_ref->{skip} = 'YES';
79             }elsif( $self->_check_graft_state( $passed_ref ) ){
80             ###InternalExtracteDGrafT warn "Found a difference - adding new element ...";
81             ###InternalExtracteDGrafT warn "can deep clone: " . ( $self->can( 'deep_clone' ) );
82             my $clone_value = ( $self->can( 'deep_clone' ) ) ?
83 15 50       61 $self->deep_clone( $passed_ref->{primary_ref} ) :
84             'CRAZY' ;#$passed_ref->{primary_ref} ;
85             ###InternalExtracteDGrafT warn "clone value: $clone_value";
86 15         21 $passed_ref->{secondary_ref} = $clone_value;
87 15 100       378 if( $self->has_graft_memory ){
88             ###InternalExtracteDGrafT warn "recording the most recent grafted scion ...";
89             ###InternalExtracteDGrafT warn "current branch ref is:" . Dumper( $passed_ref->{branch_ref} );
90             $self->_remember_graft_item(
91             $self->_build_branch(
92             $clone_value,
93             ( ( is_ArrayRef( $clone_value ) ) ? [] : {} ),
94 2 100       6 @{$passed_ref->{branch_ref}},
  2         231  
95             )
96             );
97             ###InternalExtracteDGrafT warn "graft memory:" . Dumper( $self->get_grafted_positions );
98             }else{
99             ###InternalExtracteDGrafT warn "forget this graft - whats done is done ...";
100             }
101 15         20 $passed_ref->{skip} = 'YES';
102             }else{
103             ###InternalExtracteDGrafT warn "no action required - continue on";
104             }
105             ###InternalExtracteDGrafT warn "the current passed ref is:" . Dumper( $passed_ref );
106 43         67 return $passed_ref;
107             }
108              
109             sub _check_graft_state{
110 37     37   26 my ( $self, $passed_ref ) = @_;
111 37         30 my $answer = 0;
112             ###InternalExtracteDGrafT warn "reached _check_graft_state with passed_ref:" . Dumper( $passed_ref );
113 37 100       53 if( $passed_ref->{match} eq 'NO' ){
114             ###InternalExtracteDGrafT warn "found possible difference ...";
115 15 50 66     45 if( ( $passed_ref->{primary_type} eq 'SCALAR' ) and
116             $passed_ref->{primary_ref} =~ /IGNORE/i ){
117             ###InternalExtracteDGrafT warn "IGNORE case found ...";
118             }else{
119             ###InternalExtracteDGrafT warn "grafting now ...";
120 15         12 $answer = 1;
121             }
122             }
123             ###InternalExtracteDGrafT warn "the current answer is: $answer";
124 37         51 return $answer;
125             }
126              
127             #########1 Phinish Strong 3#########4#########5#########6#########7#########8#########9
128              
129 2     2   11 no Moose::Role;
  2         1  
  2         8  
130              
131             1;
132             # The preceding line will help the module return a true value
133              
134             #########1 Main POD starts 3#########4#########5#########6#########7#########8#########9
135              
136             __END__
137              
138             =head1 NAME
139              
140             Data::Walk::Graft - A way to say what should be added
141              
142             =head1 SYNOPSIS
143              
144             #!perl
145             use Data::Walk::Extracted;
146             use Data::Walk::Graft;
147             use Data::Walk::Print;
148             use MooseX::ShortCut::BuildInstance qw( build_instance );
149              
150             my $gardener = build_instance(
151             package => 'Jordan::Porter',
152             superclasses =>['Data::Walk::Extracted'],
153             roles =>[qw( Data::Walk::Graft Data::Walk::Clone Data::Walk::Print )],
154             sorted_nodes =>{
155             HASH => 1,
156             },# For demonstration consistency
157             #Until Data::Walk::Extracted and ::Graft support these types
158             #(watch Data-Walk-Extracted on github)
159             skipped_nodes =>{
160             OBJECT => 1,
161             CODEREF => 1,
162             },
163             graft_memory => 1,
164             );
165             my $tree_ref = {
166             Helping =>{
167             KeyTwo => 'A New Value',
168             KeyThree => 'Another Value',
169             OtherKey => 'Something',
170             },
171             MyArray =>[
172             'ValueOne',
173             'ValueTwo',
174             'ValueThree',
175             ],
176             };
177             $gardener->graft_data(
178             scion_ref =>{
179             Helping =>{
180             OtherKey => 'Otherthing',
181             },
182             MyArray =>[
183             'IGNORE',
184             {
185             What => 'Chicken_Butt!',
186             },
187             'IGNORE',
188             'IGNORE',
189             'ValueFive',
190             ],
191             },
192             tree_ref => $tree_ref,
193             );
194             $gardener->print_data( $tree_ref );
195             print "Now a list of -" . $gardener->number_of_scions . "- grafted positions\n";
196             $gardener->print_data( $gardener->get_grafted_positions );
197              
198             #####################################################################################
199             # Output of SYNOPSIS
200             # 01 {
201             # 02 Helping => {
202             # 03 KeyThree => 'Another Value',
203             # 04 KeyTwo => 'A New Value',
204             # 05 OtherKey => 'Otherthing',
205             # 06 },
206             # 07 MyArray => [
207             # 08 'ValueOne',
208             # 09 {
209             # 10 What => 'Chicken_Butt!',
210             # 11 },
211             # 12 'ValueThree',
212             # 13 undef,
213             # 14 'ValueFive',
214             # 15 ],
215             # 16 },
216             # 17 Now a list of -3- grafted positions
217             # 18 [
218             # 19 {
219             # 20 Helping => {
220             # 21 OtherKey => 'Otherthing',
221             # 22 },
222             # 23 },
223             # 24 {
224             # 25 MyArray => [
225             # 26 undef,
226             # 27 {
227             # 28 What => 'Chicken_Butt!',
228             # 29 },
229             # 30 ],
230             # 31 },
231             # 32 {
232             # 33 MyArray => [
233             # 34 undef,
234             # 35 undef,
235             # 36 undef,
236             # 37 undef,
237             # 38 'ValueFive',
238             # 39 ],
239             # 40 },
240             # 41 ],
241             #####################################################################################
242              
243             =head1 DESCRIPTION
244              
245             This L<Moose::Role> contains methods for adding a new branch ( or three ) to an existing
246             data ref. The method used to do this is L<graft_data|/graft_data( %args|$arg_ref )> using
247             L<Data::Walk::Extracted>. Grafting is accomplished by sending a $scion_ref that has
248             additions that need to be made to a $tree_ref. Anything in the scion ref that does not
249             exist in the tree ref is grafted to the tree ref. I<Anytime the scion_ref is different
250             from the tree_ref the scion_ref branch will replace the tree_ref branch!>
251              
252             =head2 USE
253              
254             This is a L<Moose::Role|https://metacpan.org/module/Moose::Manual::Roles> specifically
255             designed to be used with L<Data::Walk::Extracted
256             |Data::Walk::Extracted/Extending Data::Walk::Extracted>. It can be combined traditionaly
257             to the ~::Extracted class using L<Moose> or at run time. see L<Moose::Util> and
258             L<MooseX::ShortCut::BuildInstance> for more information.
259              
260             =head2 Deep cloning the graft
261              
262             In general grafted data refs are subject to external modification by changing the data
263             in that ref from another location of the code. This module assumes that you don't want
264             to do that! As a consequence it checks to see if a 'deep_clone' method has been provided to
265             the class that consumes this role. If so it calls that method on the data ref to be
266             grafted. One possiblity is to add the Role L<Data::Walk::Clone> to your object so that
267             a deep_clone method is automatically available (all compatability testing complete). If
268             you choose to add your own deep_clone method it will be called like this;
269              
270             my $clone_value = ( $self->can( 'deep_clone' ) ) ?
271             $self->deep_clone( $scion_ref ) : $scion_ref ;
272              
273             Where $self is the active object instance.
274              
275             =head2 Grafting unsupported node types
276              
277             If you want to add data from another ref to a current ref and the add ref contains nodes
278             that are not supported then you need to L<skip|Data::Walk::Extracted/skipped_nodes> those
279             nodes in the cloning process.
280              
281             =head1 Attributes
282              
283             Data passed to -E<gt>new when creating an instance. For modification of these attributes
284             see L<Methods|/Methods>. The -E<gt>new function will either accept fat comma lists or a
285             complete hash ref that has the possible attributes as the top keys. Additionally
286             some attributes that have all the following methods; get_$attribute, set_$attribute,
287             has_$attribute, and clear_$attribute, can be passed to L<graft_data
288             |/graft_data( %args|$arg_ref )> and will be adjusted for just the run of that
289             method call. These are called 'one shot' attributes. The class and each role (where
290             applicable) in this package have a list of L<supported one shot attributes
291             |/Supported one shot attributes>.
292              
293             =head2 graft_memory
294              
295             =over
296              
297             B<Definition:> When running a 'graft_data' operation any branch of the $scion_ref
298             that does not terminate past the end of the tree ref or differ from the tree_ref
299             will not be used. This attribute turns on tracking of the actual grafts made and
300             stores them for review after the method is complete. This is a way to know if a graft
301             was actually implemented. The potentially awkward wording of the associated methods
302             is done to make this an eligible 'one shot' attribute.
303              
304             B<Default> undefined = don't remember the grafts
305              
306             B<Range> 1 = remember the grafts | 0 = don't remember
307              
308             =back
309              
310             =head2 (see also)
311              
312             L<Data::Walk::Extracted|https://metacpan.org/module/Data::Walk::Extracted#Attributes>
313             Attributes
314              
315             =head1 Methods
316              
317             =head2 graft_data( %args|$arg_ref )
318              
319             =over
320              
321             B<Definition:> This is a method to add defined elements to targeted parts of a data
322             reference.
323              
324             B<Accepts:> a hash ref with the keys 'scion_ref' and 'tree_ref'. The scion
325             ref can contain more than one place that will be grafted to the tree data.
326              
327             =over
328              
329             B<tree_ref> This is the primary data ref that will be manipulated and returned
330             changed. If an empty 'tree_ref' is passed then the 'scion_ref' is returned in it's
331             entirety.
332              
333             B<scion_ref> This is a data ref that will be used to graft to the 'tree_ref'.
334             For the scion ref to work it must contain the parts of the tree ref below the new
335             scions as well as the scion itself. During data walking when a difference is found
336             graft_data will attempt to clone the remaining untraveled portion of the 'scion_ref'
337             and then graft the result to the 'tree_ref' at that point. Any portion of the tree
338             ref that differs from the scion ref at that point will be replaced. If L<graft_memory
339             |/graft_memory> is on then a full recording of the graft with a map to the data root
340             will be saved in the object. The word 'IGNORE' can be used in either an array position
341             or the value for a key in a hash ref. This tells the program to ignore differences (in
342             depth) past that point. For example if you wish to change the third element of an array
343             node then placing 'IGNORE' in the first two positions will cause 'graft_data' to skip the
344             analysis of the first two branches. This saves replicating deep references in the
345             scion_ref while also avoiding a defacto 'prune' operation. If an array position in the
346             scion_ref is set to 'IGNORE' in the 'scion_ref' but a graft is made below the node with
347             IGNORE then the grafted tree will contain 'IGNORE' in that element of the array (not
348             undef). Any positions that exist in the tree_ref that do not exist in the scion_ref
349             will be ignored. If an empty 'scion_ref' is sent then the code will L<cluck
350             |https://metacpan.org/module/Carp> and then return the 'tree_ref'.
351              
352             B<[attribute name]> - attribute names are accepted with temporary attribute settings.
353             These settings are temporarily set for a single "graft_data" call and then the original
354             attribute values are restored. For this to work the the attribute must meet the
355             L<necessary criteria|/Attributes>.
356              
357             B<Example>
358              
359             $grafted_tree_ref = $self->graft_data(
360             tree_ref => $tree_data,
361             scion_ref => $addition_data,
362             graft_memory => 0,
363             );
364              
365             =back
366              
367             B<Returns:> The $tree_ref with any changes (possibly deep cloned)
368              
369             =back
370              
371             =head2 has_graft_memory
372              
373             =over
374              
375             B<Definition:> This will indicate if the attribute L<graft_memory|/graft_memory> is active
376              
377             B<Accepts:> nothing
378              
379             B<Returns:> 1 or 0
380              
381             =back
382              
383             =head2 set_graft_memory( $Bool )
384              
385             =over
386              
387             B<Definition:> This will set the L<graft_memory|/graft_memory> attribute
388              
389             B<Accepts:> 1 or 0
390              
391             B<Returns:> nothing
392              
393             =back
394              
395             =head2 get_graft_memory
396              
397             =over
398              
399             B<Definition:> This will return the current value for the L<graft_memory|/graft_memory> attribute.
400              
401             B<Accepts:> nothing
402              
403             B<Returns:> 1 or 0
404              
405             =back
406              
407             =head2 clear_graft_memory
408              
409             =over
410              
411             B<Definition:> This will clear the L<graft_memory|/graft_memory> attribute.
412              
413             B<Accepts:> nothing
414              
415             B<Returns:> nothing
416              
417             =back
418              
419             =head2 number_of_scions
420              
421             =over
422              
423             B<Definition:> This will return the number of scion points grafted in the most recent
424             graft action if the L<graft_memory|/graft_memory> attribute is on.
425              
426             B<Accepts:> nothing
427              
428             B<Returns:> a positive integer
429              
430             =back
431              
432             =head2 has_grafted_positions
433              
434             =over
435              
436             B<Definition:> This will indicate if any grafted positions were saved.
437              
438             B<Accepts:> nothing
439              
440             B<Returns:> 1 or 0
441              
442             =back
443              
444             =head2 get_grafted_positions
445              
446             =over
447              
448             B<Definition:> This will return any saved grafted positions.
449              
450             B<Accepts:> nothing
451              
452             B<Returns:> an ARRAY ref of grafted positions. This will include
453             one full data branch to the root for each position actually grafted.
454              
455             =back
456              
457             =head1 Caveat utilitor
458              
459             =head2 Supported Node types
460              
461             =over
462              
463             =item ARRAY
464              
465             =item HASH
466              
467             =item SCALAR
468              
469             =item Other node support
470              
471             Support for Objects is partially implemented and as a consequence graft_data won't
472             immediatly die when asked to graft an object. It will still die but on a dispatch table
473             call that indicates where there is missing object support not at the top of the node.
474              
475             =back
476              
477             =head2 Supported one shot attributes
478              
479             L<explanation|/Attributes>
480              
481             =over
482              
483             =item graft_memory
484              
485             =back
486              
487             =head1 GLOBAL VARIABLES
488              
489             =over
490              
491             B<$ENV{Smart_Comments}>
492              
493             The module uses L<Smart::Comments|https://metacpan.org/module/Smart::Comments> if the '-ENV'
494             option is set. The 'use' is encapsulated in an if block triggered by an environmental
495             variable to comfort non-believers. Setting the variable $ENV{Smart_Comments} in a BEGIN
496             block will load and turn on smart comment reporting. There are three levels of 'Smartness'
497             available in this module '###', '####', and '#####'.
498              
499             =back
500              
501             =head1 SUPPORT
502              
503             =over
504              
505             L<github Data-Walk-Extracted/issues|https://github.com/jandrew/Data-Walk-Extracted/issues>
506              
507             =back
508              
509             =head1 TODO
510              
511             =over
512              
513             B<1.> Add L<Log::Shiras|https://metacpan.org/module/Log::Shiras> debugging in exchange for
514             L<Smart::Comments|https://metacpan.org/module/Smart::Comments>
515              
516             B<2.> Support grafting through class instance nodes (can - should you even do this?)
517              
518             B<3.> Support grafting through CodeRef nodes (can - should you even do this?)
519              
520             B<4.> Support grafting through REF nodes
521              
522             B<5.> A possible depth check to ensure the scion is deeper than the tree_ref
523              
524             =over
525              
526             Implemented with an attribute that turns the feature on and off. The goal
527             would be to eliminate unintentional swapping of small branches for large branches.
528             This feature has some overhead downside and may not be usefull so I'm not sure
529             if it makes sence yet.
530              
531             =back
532              
533             =back
534              
535             =head1 AUTHOR
536              
537             =over
538              
539             =item Jed Lund
540              
541             =item jandrew@cpan.org
542              
543             =back
544              
545             =head1 COPYRIGHT
546              
547             This program is free software; you can redistribute
548             it and/or modify it under the same terms as Perl itself.
549              
550             The full text of the license can be found in the
551             LICENSE file included with this module.
552              
553             This software is copyrighted (c) 2012, 2016 by Jed Lund.
554              
555             =head1 Dependencies
556              
557             =over
558              
559             L<version>
560              
561             L<Moose::Role>
562              
563             =over
564              
565             B<requires>
566              
567             =over
568              
569             =item _process_the_data
570              
571             =item _dispatch_method
572              
573             =item _build_branch
574              
575             =back
576              
577             =back
578              
579             L<MooseX::Types::Moose>
580              
581             L<Data::Walk::Extracted>
582              
583             L<Data::Walk::Extracted::Dispatch>
584              
585             L<Carp> - cluck
586              
587             =back
588              
589             =head1 SEE ALSO
590              
591             =over
592              
593             L<Log::Shiras::Unhide> - Can use to unhide '###InternalExtracteDGrafT' tags
594              
595             L<Log::Shiras::TapWarn> - to manage the output of exposed '###InternalExtracteDGrafT' lines
596              
597             L<Data::Dumper> - used in the '###InternalExtracteDGrafT' lines
598              
599             =back
600              
601             =cut
602              
603             #########1 Main POD ends 3#########4#########5#########6#########7#########8#########9