File Coverage

blib/lib/Tree/Persist/DB/SelfReferential.pm
Criterion Covered Total %
statement 101 101 100.0
branch 15 16 93.7
condition n/a
subroutine 10 10 100.0
pod n/a
total 126 127 99.2


line stmt bran cond sub pod time code
1             package Tree::Persist::DB::SelfReferential;
2              
3 4     4   2279 use strict;
  4         5  
  4         107  
4 4     4   15 use warnings;
  4         4  
  4         125  
5              
6 4     4   13 use base qw( Tree::Persist::DB );
  4         4  
  4         1594  
7              
8 4     4   76 use Module::Runtime;
  4         6  
  4         22  
9              
10 4     4   104 use Scalar::Util qw( blessed refaddr );
  4         6  
  4         3707  
11              
12             our $VERSION = '1.13';
13              
14             my(%defaults) =
15             (
16             id_col => 'id',
17             parent_id_col => 'parent_id',
18             value_col => 'value',
19             # class_col => 'class',
20             );
21              
22             # ----------------------------------------------
23              
24             sub _init
25             {
26 10     10   10 my($class) = shift;
27 10         11 my($opts) = @_;
28 10         34 my($self) = $class -> SUPER::_init( $opts );
29 10         10 $self->{_id} = $opts->{id};
30              
31 10         30 while ( my ($name, $val) = each %defaults )
32             {
33 30 100       107 $self->{ "_${name}" } = $opts->{ $name } ? $opts->{ $name } : $val;
34             }
35              
36 10 100       20 if ( exists $opts->{class_col} )
37             {
38 7         10 $self->{_class_col} = $opts->{class_col};
39             }
40              
41 10         15 return $self;
42              
43             } # End of _init.
44              
45             # ----------------------------------------------
46              
47             sub _reload
48             {
49 6     6   5 my($self) = shift;
50 6         12 my(%sql) = $self->_build_sql;
51 6         42 my($sth) = $self->{_dbh} -> prepare( $sql{ fetch } );
52              
53 6         539 $sth -> execute( $self->{_id} );
54              
55 6         56 my ($id, $parent_id, $value, $class) = $sth -> fetchrow_array();
56              
57 6         22 $sth -> finish;
58              
59 6         13 my($tree) = Module::Runtime::use_module($class) -> new( $value );
60 6         9745 my($ref_addr) = refaddr $self;
61              
62 6         13 $tree->meta->{$ref_addr}{id} = $id;
63 6         39 $tree->meta->{$ref_addr}{parent_id} = $parent_id;
64              
65 6         23 my(@parents) = ( $tree );
66              
67 6         6 my($node);
68             my($sth_child);
69              
70 6         15 while ( my $parent = shift @parents )
71             {
72 9         44 $sth_child = $self->{_dbh} -> prepare( $sql{ fetch_children } );
73              
74 9         599 $sth_child -> execute( $parent -> meta->{$ref_addr}{id} );
75              
76 9         192 $sth_child -> bind_columns( \my ($id, $value, $class) );
77              
78 9         223 while ($sth_child -> fetch)
79             {
80 3         8 $node = Module::Runtime::use_module($class) -> new( $value );
81              
82 3         122 $parent -> add_child( $node );
83              
84 3         296 $node->meta->{$ref_addr}{id} = $id;
85 3         17 $node->meta->{$ref_addr}{parent_id} = $parent_id;
86              
87 3         22 push @parents, $node;
88             }
89              
90 9         32 $sth_child -> finish;
91             }
92              
93 6         25 $self -> _set_tree( $tree );
94              
95 6         114 return $self;
96              
97             } # End of _reload.
98              
99             # ----------------------------------------------
100              
101             sub _create
102             {
103 8     8   10 my($self) = shift;
104 8         7 my($tree) = shift;
105 8 100       25 $tree = $self -> tree if (! $tree);
106 8         8 my($dbh) = $self->{_dbh};
107 8         18 my(%sql) = $self->_build_sql;
108             my($next_id) = do
109 8         10 {
110 8         59 my($sth) = $dbh->prepare( $sql{next_id} );
111              
112 8         930 $sth->execute;
113 8         173 $sth->fetchrow_array;
114             };
115 8         18 my($ref_addr) = refaddr $self;
116 8         30 my($sth) = $dbh->prepare( $sql{create_node} );
117 8         392 my $traversal = $tree -> traverse( $tree -> LEVEL_ORDER );
118              
119 8         150 my($node_id);
120             my($parent_id);
121              
122 8         15 while ( my $node = $traversal->() )
123             {
124             $node_id
125             = $node -> meta->{$ref_addr}{id}
126 22         73750 = $next_id++;
127              
128             $parent_id
129             = $node -> meta->{$ref_addr}{parent_id}
130 22         203 = eval { $node -> parent -> meta->{$ref_addr}{id} };
  22         59  
131              
132 22 100       265 if ( $self->{_class_col} )
133             {
134 17         45 $sth->execute($node_id, $parent_id, $node->value, blessed( $node ) );
135             }
136             else {
137 5         7 $sth->execute($node_id, $parent_id, $node->value);
138             }
139             }
140              
141 8         35483 $sth -> finish;
142              
143 8         168 return $self;
144              
145             } # End of _create.
146              
147             # ----------------------------------------------
148              
149             sub _commit
150             {
151 7     7   8 my($self) = shift;
152 7         9 my($dbh) = $self->{_dbh};
153 7         12 my(%sql) = $self -> _build_sql;
154 7         18 my($ref_addr) = refaddr $self;
155              
156 7         5 my($sth);
157              
158 7         7 for my $change ( @{$self->{_changes}} )
  7         14  
159             {
160 7 100       24 if ( $change->{action} eq 'change_value' )
    100          
    50          
161             {
162 2         19 $sth = $dbh->prepare_cached( $sql{set_value} );
163              
164 2         118 $sth -> execute($change->{new_value}, $change->{node}->meta->{$ref_addr}{id});
165 2         115 $sth -> finish;
166             }
167             elsif ( $change->{action} eq 'add_child' )
168             {
169 4         6 for my $child ( @{$change->{children}} )
  4         9  
170             {
171 4         16 $self -> _create( $child );
172             }
173             }
174             elsif ( $change->{action} eq 'remove_child' )
175             {
176 1         2 for my $child ( @{$change->{children}} )
  1         2  
177             {
178 1         6 $sth = $dbh -> prepare_cached( $sql{set_parent} );
179              
180 1         73 $sth -> execute(undef, $child -> meta->{$ref_addr}{id});
181 1         51 $sth -> finish;
182             }
183             }
184             }
185              
186 7         27 return $self;
187              
188             } # End of _commit.
189              
190             # ----------------------------------------------
191              
192             sub _build_sql
193             {
194 21     21   22 my($self) = shift;
195 21         122 my(%sql) =
196             (
197             next_id => <<"__END_SQL__",
198             SELECT coalesce(MAX($self->{_id_col}),0) + 1
199             FROM $self->{_table}
200             __END_SQL__
201             set_value => <<"__END_SQL__",
202             UPDATE $self->{_table}
203             SET $self->{_value_col} = ?
204             WHERE $self->{_id_col} = ?
205             __END_SQL__
206             set_parent => <<"__END_SQL__",
207             UPDATE $self->{_table}
208             SET $self->{_parent_id_col} = ?
209             WHERE $self->{_id_col} = ?
210             __END_SQL__
211             );
212              
213 21 100       32 if ( $self->{_class_col} )
214             {
215 18         68 $sql{fetch} = <<"__END_SQL__";
216             SELECT $self->{_id_col} AS id
217             ,$self->{_parent_id_col} AS parent_id
218             ,$self->{_value_col} AS value
219             ,$self->{_class_col} AS class
220             FROM $self->{_table} AS tree
221             WHERE tree.$self->{_id_col} = ?
222             __END_SQL__
223              
224 18         48 $sql{fetch_children} = <<"__END_SQL__";
225             SELECT $self->{_id_col} AS id
226             ,$self->{_value_col} AS value
227             ,$self->{_class_col} AS class
228             FROM $self->{_table} AS tree
229             WHERE tree.$self->{_parent_id_col} = ?
230             __END_SQL__
231              
232 18         46 $sql{create_node} = <<"__END_SQL__";
233             INSERT INTO $self->{_table} (
234             $self->{_id_col}
235             ,$self->{_parent_id_col}
236             ,$self->{_value_col}
237             ,$self->{_class_col}
238             ) VALUES ( ?, ?, ?, ? )
239             __END_SQL__
240             }
241             else
242             {
243 3         18 $sql{fetch} = <<"__END_SQL__";
244             SELECT $self->{_id_col} AS id
245             ,$self->{_parent_id_col} AS parent_id
246             ,$self->{_value_col} AS value
247             ,'$self->{_class}' AS class
248             FROM $self->{_table} AS tree
249             WHERE tree.$self->{_id_col} = ?
250             __END_SQL__
251              
252 3         14 $sql{fetch_children} = <<"__END_SQL__";
253             SELECT $self->{_id_col} AS id
254             ,$self->{_value_col} AS value
255             ,'$self->{_class}' AS class
256             FROM $self->{_table} AS tree
257             WHERE tree.$self->{_parent_id_col} = ?
258             __END_SQL__
259              
260 3         12 $sql{create_node} = <<"__END_SQL__";
261             INSERT INTO $self->{_table} (
262             $self->{_id_col}
263             ,$self->{_parent_id_col}
264             ,$self->{_value_col}
265             ) VALUES ( ?, ?, ? )
266             __END_SQL__
267             }
268              
269 21         102 return %sql;
270              
271             } # End of _build_sq.
272              
273             # ----------------------------------------------
274              
275             1;
276              
277             __END__