File Coverage

blib/lib/TreePath.pm
Criterion Covered Total %
statement 273 288 94.7
branch 118 138 85.5
condition 31 38 81.5
subroutine 31 32 96.8
pod 11 11 100.0
total 464 507 91.5


line stmt bran cond sub pod time code
1             package TreePath;
2              
3 7     7   120503 use utf8;
  7         31  
  7         40  
4 7     7   3707 use Moose;
  7         2678929  
  7         62  
5             with 'MooseX::Object::Pluggable';
6              
7 7     7   43256 use Moose::Util::TypeConstraints;
  7         20  
  7         63  
8 7     7   16819 use Config::JFDI;
  7         1364429  
  7         361  
9 7     7   60 use Carp qw/croak/;
  7         10  
  7         540  
10 7     7   5756 use Data::Dumper;
  7         42424  
  7         23393  
11              
12             our $VERSION = '0.13';
13              
14             subtype MyConf => as 'HashRef';
15             coerce 'MyConf'
16             => from 'Str' => via {
17             my $conf = shift;
18             my ($jfdi_h, $jfdi) = Config::JFDI->open($conf)
19             or croak "Error (conf: $conf) : $!\n";
20             return $jfdi->get;
21             };
22              
23             has conf => ( is => 'rw',
24             isa => 'MyConf',
25             coerce => 1,
26             trigger => sub {
27             my $self = shift;
28             my $args = shift;
29              
30             # if conf exist
31             if ( defined $args->{$self->configword} ) {
32             croak "Error: Can not find " . $self->configword . " in your conf !"
33             if ( ! $args->{$self->configword});
34              
35             $self->config($args->{$self->configword});
36              
37             $self->debug($self->config->{'debug'})
38             if ( ! defined $self->debug && defined $self->config->{'debug'} );
39              
40             $self->_search_field($self->config->{backend}->{args}->{search_field})
41             if defined $self->config->{backend}->{args}->{search_field};
42             $self->_parent_field($self->config->{backend}->{args}->{parent_field})
43             if defined $self->config->{backend}->{args}->{parent_field};
44              
45             $self->_sync($self->config->{backend}->{args}->{sync})
46             if defined $self->config->{backend}->{args}->{sync};
47              
48             $self->_load_backend if ! $self->can('backend');
49             }
50             # it's a hash
51             else {
52             $self->tree($args);
53             $self->_build_tree;
54             }
55             }
56             );
57              
58              
59              
60             has config => (
61             isa => "HashRef",
62             is => "rw",
63             );
64              
65             has 'configword' => (
66             is => 'rw',
67             default => sub { __PACKAGE__ },
68             );
69              
70             has 'debug' => (
71             is => 'rw',
72             );
73              
74             has '_backend' => (
75             is => 'rw',
76             isa => 'Str',
77             );
78              
79             has '_sync' => (
80             is => 'rw',
81             isa => 'Str',
82             );
83              
84             has _plugin_ns => (
85             is => 'rw',
86             required => 1,
87             isa => 'Str',
88             default => sub{ 'Backend' },
89             );
90              
91             has _type_field => (
92             is => 'rw',
93             isa => 'Str',
94             default => sub{ 'type' },
95             );
96              
97             has _search_field => (
98             is => 'rw',
99             isa => 'Str',
100             default => sub{ 'name' },
101             );
102              
103             has _parent_field => (
104             is => 'rw',
105             isa => 'Str',
106             default => sub{ 'parent' },
107             );
108              
109             has _position_field => (
110             is => 'rw',
111             isa => 'Str',
112             default => sub{ 'position' },
113             );
114              
115             has _rules => (
116             isa => "HashRef",
117             is => "rw",
118             );
119              
120             has tree => (
121             isa => "HashRef",
122             is => "rw",
123             );
124              
125              
126             has root => (
127             isa => "HashRef",
128             is => "rw",
129             );
130              
131              
132              
133             sub _load_backend {
134 7     7   14 my $self = shift;
135 7         191 my $backend = $self->config->{'backend'}->{name};
136 7         184 $self->_backend($backend);
137              
138 7         45 $self->_log("Loading $backend backend ...");
139 7         38 $self->load_plugin( $backend );
140 7         43350 $self->_load_tree;
141 7 100       239 $self->_load_rules if ( defined $self->config->{rules});
142 7         55 $self->_build_tree;
143             }
144              
145             sub reload {
146 1     1 1 118 my $self = shift;
147              
148 1 50       51 $self->_populate_backend(0)
149             if $self->can('_populate_backend');
150              
151 1         6 $self->_load_tree;
152             }
153              
154             sub _log{
155 43     43   93 my ($self, $msg ) = @_;
156              
157 43 50       1243 return if ! $self->debug;
158              
159 0         0 say STDERR "[debug] $msg";
160             }
161              
162             # Load tree from backend
163             sub _load_tree {
164 8     8   20 my $self = shift;
165              
166 8         41 $self->tree($self->_load);
167             }
168              
169             # Load rules from config
170             sub _load_rules {
171 1     1   3 my $self = shift;
172 1         22 $self->_rules($self->config->{'rules'});
173             }
174              
175             # Build Tree (children, position, ...)
176             # parents and children become HashRef
177             sub _build_tree {
178 9     9   19 my $self = shift;
179              
180 9         234 my $tree = $self->tree;
181 9         119 foreach my $id ( sort { $a <=> $b } keys %$tree ) {
  161         167  
182 70         99 my $node = $tree->{$id};
183 70         124 $node->{id} = $id;
184 70         105 my $parent = $tree->{$node->{parent}};
185 70         90 $node->{parent} = $parent;
186              
187 70         132 $self->add($node, $parent);
188             }
189             }
190              
191             # return the last node sorted by id
192             sub _last_node {
193 45     45   88 my $self = shift;
194              
195 45         77 my @nodes_sorted_by_id = sort { $a <=> $b } map $_->{id}, values %{$self->tree};
  728         1899  
  45         1575  
196 45 100       205 return { id => 0 } if ! defined $nodes_sorted_by_id[0];
197 43         1267 return $self->tree->{$nodes_sorted_by_id[-1]};
198             }
199              
200             sub _bckd_create {
201 40     40   70 my $self = shift;
202 40         60 my $node = shift;
203 40         67 my $msg = shift;
204              
205 40 100 66     1185 return if ( ! $self->_backend || ! $self->_sync );
206 15         398 $self->_log("[" . $self->_backend . "] CREATE " . $node->{name} . " | $msg");
207 15         98 $self->_create($node);
208             }
209              
210             sub _bckd_delete {
211 15     15   25 my $self = shift;
212 15         21 my $nodes = shift;
213 15         30 my $msg = shift;
214              
215 15 100 66     450 return if ( ! $self->_backend || ! $self->_sync );
216 4         13 my @nodes_name = map { $_->{$self->_search_field} } @$nodes;
  11         275  
217 4         114 $self->_log("[" . $self->_backend . "] DELETE @nodes_name | $msg");
218              
219 4         26 $self->_delete($nodes);
220             }
221              
222             sub _bckd_update {
223 49     49   75 my $self = shift;
224 49         73 my $node = shift;
225 49         68 my $msg = shift;
226              
227 49 100 66     1689 return if ( ! $self->_backend || ! $self->_sync );
228 9         235 $self->_log("[" . $self->_backend . "] UPDATE " . $node->{name} . " | $msg");
229 9         53 $self->_update($node);
230             }
231              
232              
233             sub _check_rules{
234 121     121   184 my ($self, $node, $parent) = @_;
235              
236 121 100       302 $node->{id} = $self->_last_node->{id} +1
237             if ( ! defined $node->{id} );
238              
239 121 100 100     3084 return 1 if ( ! defined $self->config ||
      100        
240             ! defined $self->config->{rules} ||
241             $node->{id} == 1
242             );
243             # Is node can 'link' to parent ?
244 8         241 my $rules = $self->_rules;
245 8 50       221 if ( defined $node->{$self->_type_field} ) {
246 8 50       222 if ( defined $rules->{$node->{$self->_type_field}}) {
247 8 100       211 if ( defined $rules->{$node->{$self->_type_field}}->{$parent->{$self->_type_field}}) {
248 5         113 return $rules->{$node->{$self->_type_field}}->{$parent->{$self->_type_field}};
249             }
250 3         72 else { return 0 }
251             }
252 0         0 else { return 0 }
253             }
254 0         0 else { return 0 }
255             }
256              
257             sub _update_children_position {
258 34     34   62 my $self = shift;
259 34         83 my @children = @_;
260              
261 34         50 my $n=1;
262 34         81 foreach my $child ( @children ) {
263 78 100 100     3196 if ( ! defined $child->{$self->_position_field} ||
264             $child->{$self->_position_field} != $n )
265             {
266 41 100       1297 my $pos = defined $child->{$self->_position_field} ?
267             $child->{$self->_position_field} : '?';
268 41         1400 my $msg = 'updating position of '. $child->{$self->_search_field} .
269             "( $pos -> $n)";
270 41         1303 $child->{$self->_position_field} = $n;
271 41         160 $self->_bckd_update($child, $msg );
272             }
273 78         161556 $n++;
274             }
275             }
276              
277              
278             # removes the child's father
279             sub _remove_child_father{
280 23     23   45 my $self = shift;
281 23         40 my $node = shift;
282              
283 23         39 my $father = $node->{parent};
284 23         82 my $key_children = $self->_key_children($node, $father);
285 23         33 my $id = 0;
286 23         71 foreach my $child ( @{$father->{$key_children}}) {
  23         80  
287 43 100 66     1320 if ( $child->{$self->_search_field} eq $node->{$self->_search_field} &&
288             $child->{parent} eq $node->{parent} ){
289 23         40 return splice ( @{$father->{$key_children}},$id,1);
  23         123  
290             }
291 20         44 $id++;
292             }
293             }
294              
295              
296             sub _clone_node {
297 24     24   45 my $self = shift;
298 24         41 my $node = shift;
299              
300 24         48 my $clone = {};
301 24         109 foreach my $k (keys %$node) {
302 84 100       169 if ( $k eq 'parent'){
303 24 100       73 if ( $node->{'parent'}) {
304 22         748 $clone->{$self->_parent_field} = $node->{$k}->{id};
305             }
306             # is root
307             else {
308 2         72 $clone->{$self->_parent_field} = 0;
309             }
310             }
311 60         161 else { $clone->{$k} = $node->{$k} }
312             }
313 24         73 return $clone;
314             }
315              
316             # return key name of children parent
317             sub _key_children {
318 224     224   272 my ($self, $node, $parent) = @_;
319              
320 224         227 my $key_children;
321             #if parent have a type
322 224 100       6369 if ( defined $parent->{$self->_type_field}) {
323              
324             # if node have a type
325 22 100       506 if ( defined $node->{$self->_type_field}) {
326              
327             # if node and parent have the same type
328 19 100       412 if ( $node->{$self->_type_field} eq $parent->{$self->_type_field} ) {
329 14         28 $key_children = 'children';
330             }
331             else {
332 5         112 $key_children = 'children_' . $node->{$self->_type_field};
333             }
334             }
335             # else node have the same type as parent
336             else {
337 3         60 $node->{$self->_type_field} = $parent->{$self->_type_field};
338 3         3 $key_children = 'children';
339             }
340             }
341             # parent haven't type
342             else {
343 202 50       5270 if ( defined $node->{$self->_type_field}) {
344 0         0 die "node " . $node->{$self->_search_field} . " [id:". $node->{id} . "] have a type but not the parent !";
345             }
346             else {
347 202         281 $key_children = 'children';
348             }
349             }
350 224         463 return $key_children;
351             }
352             sub search {
353 98     98 1 5672 my ( $self, $args, $opts ) = @_;
354              
355 98         147 my $results = [];
356 98         2904 my $tree = $self->tree;
357 98         603 foreach my $id ( sort {$a <=> $b} keys %$tree ) {
  2420         2206  
358              
359 526         432 my $found = 1;
360 526         781 foreach my $key ( keys %$args ) {
361 628         469 my $current;
362 628 100       1267 if ( $key =~ m/(.*)\.(.*)/) {
363             # ex: parent.name
364 214 100 66     1143 if ( defined $tree->{$id}->{$1} && ref($tree->{$id}->{$1})) {
365 167         335 $current = $tree->{$id}->{$1}->{$2};
366             }
367 47         75 else { next }
368             }
369             else {
370 414 100       733 if ( defined $tree->{$id}->{$key} ){
371 410         556 $current = $tree->{$id}->{$key};
372             }
373             else {
374 4         41 die "'$key' is not a key hash [node id:$id]";
375             }
376             }
377 577         563 my $value = $args->{$key};
378 577 100       940 if ( $current ne $value ) {
379 444         333 $found = 0;
380 444         418 last;
381             }
382             }
383              
384 522 100       931 if ( $found ){
385 78 100       114 if ( wantarray) {
386 9         20 push(@$results, $tree->{$id});
387             }
388             # if found and scalar context
389             else {
390 69         300 return $tree->{$id};
391             }
392             }
393             }
394              
395 25 50 66     237 return 0 if ( ! wantarray && ! $$results[0] );
396              
397             # wantarray
398 5         40 return @$results;
399             }
400              
401              
402             # ex : search_path(/A/B/C')
403             sub search_path {
404 16     16 1 2187 my ( $self, $path, $opts ) = @_;
405              
406             # search by 'name' if not defined
407 16 50       635 $opts->{by} = $self->_search_field if ! defined $opts->{by};
408              
409 16 50       75 croak "path must be start by '/' !: $!\n" if ( $path !~ m|^/| );
410              
411 16         75 my $nodes = [ split m%/%, $path ];
412 16         57 $$nodes[0] = '/';
413              
414 16         22 my (@found, @not_found);
415 16         39 my $parent = '/';
416 16         31 foreach my $node ( @$nodes ) {
417 60         208 my $args = { $opts->{by} => $node, "parent\.$opts->{by}" => $parent};
418 60         134 my $result = $self->search($args, $opts);
419              
420 60 100       168 $parent = $result->{$opts->{by}} if $result;
421              
422 60 100       89 if ( $result ) {
423 44         119 push(@found, $result);
424             }
425             else {
426              
427 16         49 push(@not_found, $node);
428             }
429             }
430              
431 16 100       30 if ( wantarray ) {
432 4         39 return ( \@found, \@not_found );
433             }
434             else {
435 12 100       23 if ( $not_found[-1] ) {
436 4         48 return '';
437             }
438             else {
439 8         51 return $found[-1];
440             }
441             }
442             }
443              
444              
445             sub count {
446 22     22 1 2153 my $self = shift;
447              
448 22         38 return scalar keys %{$self->tree};
  22         805  
449             }
450              
451             sub dump {
452 0     0 1 0 my $self = shift;
453 0         0 my $var = shift;
454              
455 0 0       0 $var = $self->tree if ! defined $var;
456 0         0 $Data::Dumper::Maxdepth = 3;
457 0         0 $Data::Dumper::Sortkeys = 1;
458 0         0 $Data::Dumper::Terse = 1;
459 0         0 return Dumper($var);
460             }
461              
462             sub traverse {
463 71     71 1 106 my ($self, $node, $funcref, $args) = @_;
464              
465 71 50       151 return 0 if ( ! $node );
466 71   100     183 $args ||= {};
467 71 100       170 $args->{_count} = 1 if ! defined ($args->{_count});
468              
469 71         69 my $nofunc = 0;
470 71 100       117 if ( ! $funcref ) {
471 19         29 $nofunc = 1;
472 51     51   67 $funcref = sub { my ($node, $args) = @_;
473 51 100       134 $args->{_each_nodes} = []
474             if ( ! defined $args->{_each_nodes});
475 51 50       136 if(defined($node)) {
476 51         45 push(@{$args->{_each_nodes}}, $node);
  51         88  
477 51         131 return 1;
478             }
479             }
480 19         123 }
481             # if first node
482 71 100       139 if ( $args->{_count} == 1 ) {
483 23 50       65 return 0 if ( ! &$funcref( $node, $args ) )
484             }
485              
486 71         196 my $key_children = $self->_key_children($node, $node->{parent});
487 71 100       177 if(defined($node->{$key_children})) {
488              
489 26         27 foreach my $child ( @{$node->{$key_children}} ) {
  26         58  
490 48 50       82 return 0 if ( ! &$funcref( $child, $args ) );
491 48         151 $args->{_count}++;
492 48         92 $self->traverse( $child, $funcref, $args );
493             }
494             }
495              
496 71 100       244 return $args->{_each_nodes} if $nofunc;
497 52         106 return 1;
498             }
499              
500              
501             sub del {
502 11     11 1 48 my ($self, @nodes) = @_;
503              
504 11         22 my @deleted;
505 11         31 foreach my $node ( @nodes ) {
506              
507 15         77816 my $father = $node->{parent};
508              
509 15         65 $self->_remove_child_father($node);
510              
511 15         55 my $key_children = $self->_key_children($node, $node->{parent});
512             # if position field exist, recalc all children position
513 15 100       451 $self->_update_children_position( @{$father->{$key_children}} )
  8         42  
514             if ( defined $node->{$self->_position_field});
515              
516             # traverse child branches and delete it
517 15         69 my $nodes = $self->traverse($node);
518 15         44 push(@deleted,map { delete $self->tree->{$_->{id}} } @$nodes);
  31         836  
519 15         126 $self->_bckd_delete($nodes, "delete " . @$nodes . " node(s)");
520             }
521 11         267679 return @deleted;
522             }
523              
524             # Inserts a node beneath the parent at the given position.
525             sub add {
526 115     115 1 5015 my ($self, $node, $parent, $position) = @_;
527              
528 115 100       439 $node->{id} = $self->_last_node->{id} +1
529             if ! defined $node->{id};
530              
531 115 100 100     3333 if ( ! $position && defined $node->{$self->_position_field}) {
532 5         120 $position = $node->{$self->_position_field};
533             }
534              
535 115 100       249 if ( ! $parent ){
536 13 100       390 if ( $self->root) {
537 4         43 die "root already exist !";
538             }
539 9         220 $self->root($node);
540             }
541              
542 111         191 $node->{parent} = $parent;
543              
544             # check rules
545 111 100       311 return 0 if ! $self->_check_rules($node, $parent);
546              
547              
548 110         132 my $is_exist_in_backend;
549 110 100       2628 $is_exist_in_backend = 1
550             if $self->tree->{$node->{id}};
551              
552             # save ref node in tree
553 110         2636 $self->tree->{$node->{id}} = $node;
554              
555 110         113 my $key_children;
556 110         125 my $update_children = 0;
557             # node != root
558 110 100 100     504 if ( defined $parent && $parent ) {
559              
560 101         298 $key_children = $self->_key_children($node, $parent);
561              
562 101 50       220 if ( $parent ) {
563 101 100       173 if ( $position ) {
564 9         16 splice @{$parent->{$key_children}}, $position -1, 0, $self->tree->{$node->{id}};
  9         274  
565 9         17 $update_children = 1;
566             }
567             else {
568 92         99 push(@{$parent->{$key_children}}, $self->tree->{$node->{id}});
  92         2298  
569 92 100 66     110 if ( defined ${$parent->{$key_children}}[0] && ${$parent->{$key_children}}[0]->{$self->_position_field}) {
  92         304  
  92         3351  
570 11         26 $update_children = 1;
571             }
572             }
573             }
574             }
575             # add node in tree
576 110 100       1405 $self->_bckd_create($node, 'add node ' . $node->{$self->_search_field})
577             if ! $is_exist_in_backend;
578              
579 110 100       452615 return $self->root if ! $parent;
580              
581             # recalc position of children
582 101 100       2647 $self->_update_children_position(@{$parent->{$key_children}}) if $update_children;
  20         104  
583              
584 101         466 return $node;
585             }
586              
587             sub insert_before {
588 3     3 1 42 my ($self, $sibling, $node) = @_;
589              
590 3 50       153 if ( ! defined $sibling->{$self->_position_field}) {
591 0         0 return $self->add($node, $sibling->{parent}, 1 );
592             }
593              
594 3         109 return $self->add($node, $sibling->{parent}, $sibling->{$self->_position_field} );
595             }
596              
597              
598             sub update {
599 14     14 1 33 my ($self, $node, $datas) = @_;
600              
601 14         78 foreach my $k ( sort keys %$datas ) {
602              
603 14 50       108 if ( ! defined $node->{$k} ){
  0 50       0  
604 0         0 $self->_log("update: can not update, node->{$k} is not defined but _force_create_key is not equal 1");
605 0         0 next;
606             }
607             elsif ( $node->{$k} eq $datas->{$k} ){next}
608              
609 14         36 my $previous = $node->{$k};
610 14         29 my $parent = $node->{parent};
611 14         47 my $key_children = $self->_key_children($node, $parent);
612 14         35 my $children = $parent->{$key_children};
613              
614              
615             # if update 'position'
616 14 100       468 if ( $k eq $self->_position_field ) {
    100          
    100          
617             # delete child in parent in previous position
618 2         12 my $old = splice(@$children, $previous -1, 1);
619             # add child in parent in new positioh
620 2         11 splice(@$children,$datas->{$k} -1,0, $old);
621 2 50       69 $self->_update_children_position(@{$node->{parent}->{$key_children}})
  2         18  
622             if ( defined $node->{$self->_position_field});
623             }
624             elsif ( $k eq $self->_type_field) {
625             # check rules
626 1 50       6 return 0 if ! $self->_check_rules({ type => $datas->{$k} }, $node->{parent});
627             }
628             elsif ( $k eq 'parent') {
629              
630 9 100       44 return 0 if ! $self->_check_rules($node, $datas->{parent});
631              
632 8         40 my $old = $self->_remove_child_father($node);
633 8         22 my $new_parent = $datas->{$k};
634 8         16 push(@{$new_parent->{$key_children}}, $old);
  8         26  
635              
636 8         18 $node->{parent} = $new_parent;
637              
638 8         231 my $msg = 'updating parent of '. $node->{$self->_search_field} .
639             " ( " . $old->{name} . " -> ". $node->{parent}->{name} .")";
640              
641 8         46 $self->_bckd_update($node, $msg);
642              
643 8 100       162388 if ( defined $node->{$self->_position_field}) {
644 2         11 $self->_update_children_position(@{$parent->{$key_children}});
  2         15  
645 2         4 $self->_update_children_position(@{$new_parent->{$key_children}});
  2         9  
646             }
647             }
648             else {
649 2         11 $node->{$k} = $datas->{$k};
650             }
651             }
652 12         80 return $node;
653             }
654              
655              
656             sub move {
657 6     6 1 15 my ($self, $node, $parent) = @_;
658              
659 6         36 return $self->update($node, { parent => $parent });
660             }
661              
662             =head1 NAME
663              
664             TreePath - Simple Tree Path!
665              
666             =head1 VERSION
667              
668              
669             =head1 SYNOPSIS
670              
671             use TreePath;
672              
673             my $tp = TreePath->new( conf => $conf );
674             my $tree = $tp->tree;
675              
676             # All nodes are hash
677             # The first is called 'root'
678             my $root = $tp->root;
679              
680             # a node can have children
681             my $children = $root->{children};
682              
683             =head1 SUBROUTINES/METHODS
684              
685             =head2 new($method => $value)
686              
687             # for now there are two backends : DBIX and File
688             $tp = TreePath->new( conf => 't/conf/treefromdbix.yml')
689              
690             # see t/conf/treepath.yml for hash structure
691             $tp = TreePath->new( datas => $datas);
692              
693             also see t/01-tpath.t
694              
695             =cut
696              
697             =head2 tree
698              
699             $tree = $tp->tree;
700              
701             =cut
702              
703             =head2 reload
704              
705             # reload tree from backend
706             $tree = $tp->reload;
707              
708             =cut
709              
710             =head2 nodes
711              
712             $root = $tp->root;
713             # $root and $tree->{1} are the same node
714              
715             This is the root node ( a simple hashref )
716             it has no parent.
717             {
718             '1' => {
719             'id' => '1',
720             'name' => '/',
721             'parent' => '0'
722             }
723             }
724              
725             $A = $tp->search( { name => 'A' } );
726             See the dump :
727              
728             {
729             'children' => [
730             {
731             'children' => 'ARRAY(0x293ce00)',
732             'id' => '3',
733             'name' => 'B',
734             'parent' => $VAR1
735             },
736             {
737             'children' => 'ARRAY(0x2fd69b0)',
738             'id' => '7',
739             'name' => 'F',
740             'parent' => $VAR1
741             }
742             ],
743             'id' => '2',
744             'name' => 'A',
745             'parent' => {
746             'children' => [
747             $VAR1
748             ],
749             'id' => '1',
750             'name' => '/',
751             'parent' => '0'
752             }
753             }
754              
755             => 'parent' is a reference on root node and 'children' is an array containing 2 nodes
756              
757             => if nodes have 'type', it's possible to 'link' some 'type' of node on some others types
758              
759             For example, A:(type1), B:(type1, parent:A), C:(type2, parent:A) becomes :
760             A:(type1, children:[B], children_type2[C])
761              
762             Each children can be ordered : { name => A, position => 2};
763              
764             It's also possible to block link with 'rules'. 'link' must be follow these rules to be applied.
765              
766             =cut
767              
768             =head2 search (hashref)
769              
770             # in scalar context return the first result
771             my $E = $tp->search( { name => 'E' } );
772              
773             # return all result in array context
774             my @allE = $tp->search( { name => 'E' } );
775              
776             # It is also possible to specify a particular field of a hash
777             my $B = $tp->search( { name => 'B', 'parent.name' => 'A'} );
778              
779             =cut
780              
781             =head2 search_path (PATH)
782              
783             # Search a path in a tree
784             # in scalar context return last node
785             my $c = $tp->search_path('/A/B/C');
786              
787             # in array context return found and not_found nodes
788             my ($found, $not_found) = $tp->search_path('/A/B/X/D/E');
789              
790             =cut
791              
792             =head2 dump
793              
794             # dump whole tree
795             print $tp->dump;
796              
797             # dump a node
798             print $tp->dump($c);;
799              
800             =cut
801              
802             =head2 count
803              
804             # return the number of nodes
805             print $tp->count;
806              
807             =cut
808              
809             =head2 traverse ($node, [\&function], [$args])
810              
811             # return an arrayref of nodes
812             my $nodes = $tp->traverse($node);
813              
814             # or use a function on each nodes
815             $tp->traverse($node, \&function, $args);
816              
817             =cut
818              
819             =head2 del ($node)
820              
821             # delete recursively all children and node
822             $deleted = $tp->del($node);
823              
824             # delete several nodes at once
825             @del = $tp->del($n1, $n2, ...);
826              
827             =cut
828              
829             =head2 add ($node, $parent)
830              
831             # add the root
832             $root = $tp->add({ name => '/'}, 0);
833              
834             # add a node beneath the parent at the last position.
835             $Z = $tp->add({ name => 'Z' }, $parent);
836              
837             # or at given position
838             $Z = $tp->add({ name => 'Z', position => 2 }, $parent);
839              
840             =cut
841              
842             =head2 update ($node, $datas)
843              
844             # update node with somes datas
845             $Z = $tp->update($node, { name => 'new_name' });
846              
847             # it's possible to update position
848             $X = $tp->update($node, { position => 2 });
849              
850             =cut
851              
852              
853             =head2 move ($node, $parent)
854              
855             # move a node as child of given parent
856             $Z = $tp->move($Z, $X);
857              
858             =cut
859              
860             =head2 insert_before ($sibling, $node)
861              
862             # Inserts a node beneath the parent before the given sibling.
863             $Y = $tp->insert_before($Z, { name => 'Y' });
864              
865             =cut
866              
867              
868              
869             =head1 AUTHOR
870              
871             Daniel Brosseau, C<< <dab at catapulse.org> >>
872              
873             =head1 BUGS
874              
875             Please report any bugs or feature requests to C<bug-tpath at rt.cpan.org>, or through
876             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=TreePath>. I will be notified, and then you'll
877             automatically be notified of progress on your bug as I make changes.
878              
879              
880              
881              
882             =head1 SUPPORT
883              
884             You can find documentation for this module with the perldoc command.
885              
886             perldoc TreePath
887              
888              
889             You can also look for information at:
890              
891             =over 4
892              
893             =item * RT: CPAN's request tracker (report bugs here)
894              
895             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=TreePath>
896              
897             =item * AnnoCPAN: Annotated CPAN documentation
898              
899             L<http://annocpan.org/dist/TreePath>
900              
901             =item * CPAN Ratings
902              
903             L<http://cpanratings.perl.org/d/TreePath>
904              
905             =item * Search CPAN
906              
907             L<http://search.cpan.org/dist/TreePath/>
908              
909             =back
910              
911              
912             =head1 ACKNOWLEDGEMENTS
913              
914              
915             =head1 LICENSE AND COPYRIGHT
916              
917             Copyright 2014 Daniel Brosseau.
918              
919             This program is free software; you can redistribute it and/or modify it
920             under the terms of the the Artistic License (2.0). You may obtain a
921             copy of the full license at:
922              
923             L<http://www.perlfoundation.org/artistic_license_2_0>
924              
925             Any use, modification, and distribution of the Standard or Modified
926             Versions is governed by this Artistic License. By using, modifying or
927             distributing the Package, you accept this license. Do not use, modify,
928             or distribute the Package, if you do not accept this license.
929              
930             If your Modified Version has been derived from a Modified Version made
931             by someone other than you, you are nevertheless required to ensure that
932             your Modified Version complies with the requirements of this license.
933              
934             This license does not grant you the right to use any trademark, service
935             mark, tradename, or logo of the Copyright Holder.
936              
937             This license includes the non-exclusive, worldwide, free-of-charge
938             patent license to make, have made, use, offer to sell, sell, import and
939             otherwise transfer the Package with respect to any patent claims
940             licensable by the Copyright Holder that are necessarily infringed by the
941             Package. If you institute patent litigation (including a cross-claim or
942             counterclaim) against any party alleging that the Package constitutes
943             direct or contributory patent infringement, then this Artistic License
944             to you shall terminate on the date that such litigation is filed.
945              
946             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
947             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
948             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
949             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
950             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
951             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
952             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
953             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
954              
955              
956             =cut
957              
958             1; # End of TreePath