File Coverage

blib/lib/Table/ParentChild.pm
Criterion Covered Total %
statement 54 56 96.4
branch 10 16 62.5
condition n/a
subroutine 10 10 100.0
pod 0 4 0.0
total 74 86 86.0


line stmt bran cond sub pod time code
1             # ============================================================
2             # Table::
3             # ____ _ ____ _ _ _ _
4             # | _ \ __ _ _ __ ___ _ __ | |_ / ___| |__ (_) | __| |
5             # | |_) / _` | '__/ _ \ '_ \| __| | | '_ \| | |/ _` |
6             # | __/ (_| | | | __/ | | | |_| |___| | | | | | (_| |
7             # |_| \__,_|_| \___|_| |_|\__|\____|_| |_|_|_|\__,_|
8             #
9             # ============================================================
10              
11             =head1 NAME
12              
13             Table::ParentChild - Fast lookup for Parent-Child relationships
14              
15             =head1 SYNOPSIS
16              
17             use Table::ParentChild;
18             my $table = new Table::ParentChild( \@relationships );
19              
20             my @parents = $table->parent_lookup( $child_id );
21             my @children = $table->child_lookup( $parent_id );
22             my $quantity = $table->quantity_lookup( $parent_id, $child_id );
23              
24             # Alternatively, given a $child_id...
25              
26             my $parent = $table->parent_lookup( $child_id );
27             my @parents = keys %$parent;
28              
29             foreach my $parent_id ( @parents ) {
30             my $quantity = $parent->{ $parent_id };
31             print "There are $quantity $child_id in $parent_id\n";
32             }
33              
34             # Or, given a $parent_id...
35              
36             my $child = $table->child_lookup( $parent_id );
37             my @children = keys %$child;
38              
39             foreach my $child_id ( @children ) {
40             my $quantity = $child->{ $child_id };
41             print "There are $quantity $child_id in $parent_id\n";
42             }
43              
44             =head1 DESCRIPTION
45              
46             Table::ParentChild implements a cross-linked list in two
47             dimensions. It is ideal for describing the parent-child
48             relationships of large numbers of entities. For maximum
49             speed, Table::ParentChild uses hashes to get access to
50             the table row/column headers, and then traverses a linked-
51             list written in XS. The emphasis of development was on
52             speed first, small memory footprint second, ease-of-use
53             third, and flexibility be damned :^)>.
54              
55             To populate a table, simply build an array of arrays.
56             The first element in the sub-array is the id of the parent.
57             The second element of the sub-array is the id of the child.
58             The third (and optional) element of the sub-array is the
59             quantity. Table::ParentChild will automatically build
60             appropriate headers for the table and populate the table,
61             returning a table object for your lookup pleasure.
62              
63             Be forewarned that ids are implemented as unsigned long
64             integers and quantities are implemented as floating point
65             values. The values you feed the table will be coerced into
66             the appropriate data type, which may cause a failure in
67             translation of the data.
68              
69             =cut
70              
71             package Table::ParentChild;
72              
73             require 5.005_62;
74 1     1   4202 use strict;
  1         3  
  1         47  
75 1     1   6 use warnings;
  1         2  
  1         35  
76 1     1   7 use Carp;
  1         14  
  1         2940  
77              
78             require Exporter;
79             require DynaLoader;
80 1     1   2796 use AutoLoader;
  1         33249  
  1         8  
81 1     1   1868 use Table::ParentChild::Head;
  1         4  
  1         1282  
82              
83             our @ISA = qw(Exporter DynaLoader);
84              
85             our %EXPORT_TAGS = ( 'all' => [ qw( ) ] );
86             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
87             our @EXPORT = qw( );
88             our $VERSION = '0.05';
89              
90             # ============================================================
91             sub new {
92             # ============================================================
93 2 50   2 0 70 my ($class) = map { ref || $_ } shift;
  2         12  
94 2         5 my $relationships = shift;
95              
96 2         10 my $self = bless {
97             parent => {},
98             child => {}
99             }, $class;
100              
101 2         7 foreach my $relationship ( @$relationships ) {
102 6         92 $self->add_relationship( @$relationship );
103             }
104              
105 2         8 return $self;
106             }
107              
108             # ============================================================
109             sub add_relationship {
110             # ============================================================
111 6     6 0 9 my $self = shift;
112 6         5 my $parent_id = shift;
113 6         31 my $child_id = shift;
114 6         6 my $quantity = shift;
115              
116 1     1   15 no strict;
  1         2  
  1         1467  
117 6 50       14 $quantity = 1 if( not defined $quantity );
118              
119 6         7 my $parent;
120             my $child;
121              
122 6 100       24 if( exists $self->{ parent }{ $parent_id }) {
123 1         3 $parent = $self->{ parent }{ $parent_id };
124              
125             } else {
126 5         34 $parent = new Table::ParentChild::Head( $parent_id );
127 5         15 $self->{ parent }{ $parent_id } = $parent;
128             }
129              
130 6 100       32 if( exists $self->{ child }{ $child_id }) {
131 1         1 $child = $self->{ child }{ $child_id };
132              
133             } else {
134 5         16 $child = new Table::ParentChild::Head( $child_id );
135 5         12 $self->{ child }{ $child_id } = $child;
136             }
137              
138 6         21 $parent->add_node( $child, $quantity );
139              
140             }
141              
142             # ============================================================
143             sub parent_lookup {
144             # ============================================================
145 1     1 0 23 my $self = shift;
146 1         2 my $child_id = shift;
147 1         2 my $child;
148             my $results;
149              
150 1 50       5 if( exists $self->{ child }{ $child_id } ) {
151 1         3 $child = $self->{ child }{ $child_id };
152              
153             } else {
154 0         0 return;
155             }
156              
157 1         19 $results = $child->search_for_parents;
158 1 50       5 return wantarray ? sort keys %$results : $results;
159             }
160              
161             # ============================================================
162             sub child_lookup {
163             # ============================================================
164 1     1 0 18 my $self = shift;
165 1         1 my $parent_id = shift;
166 1         2 my $parent;
167             my $results;
168              
169 1 50       4 if( exists $self->{ parent }{ $parent_id } ) {
170 1         3 $parent = $self->{ parent }{ $parent_id };
171              
172             } else {
173 0         0 return;
174             }
175              
176 1         6 $results = $parent->search_for_children;
177 1 50       4 return wantarray ? sort keys %$results : $results;
178             }
179              
180             1;
181             __END__