File Coverage

blib/lib/Data/Walk/Graft.pm
Criterion Covered Total %
statement 41 42 97.6
branch 14 18 77.7
condition 6 9 66.6
subroutine 9 9 100.0
pod 1 1 100.0
total 71 79 89.8


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