File Coverage

blib/lib/DBIx/Objects.pm
Criterion Covered Total %
statement 22 223 9.8
branch 0 78 0.0
condition 0 81 0.0
subroutine 8 36 22.2
pod n/a
total 30 418 7.1


line stmt bran cond sub pod time code
1             package DBIx::Objects;
2              
3 1     1   74159 use strict;
  1         3  
  1         52  
4 1     1   6 use warnings qw(all);
  1         3  
  1         502  
5 1     1   6 use vars qw($VERSION);
  1         6  
  1         75  
6              
7             BEGIN {
8 1     1   193 $VERSION=0.04;
9             }
10              
11             ###
12              
13             package DBIx::Object;
14              
15 1     1   6 use strict;
  1         1  
  1         50  
16 1     1   148 use warnings qw(all);
  1         10  
  1         4027  
17              
18             # Back-end methods
19             sub _blank { # Default back-end for constructor
20             # ARGS: $self, [$namespace,] @arglist
21             # $namespace - Scalar (string) - Namespace of managing package for variables
22             # @arglist - Array (string) - List of variables to be registered as methods
23 0     0     my $self=shift;
24 0   0       my $package=
25             (UNIVERSAL::isa($_[0],__PACKAGE__) && # Looks like a descendant
26             shift) || caller; # Shift or autodetect namespace to register
27 0 0         warn "Package $package not listed in registry"
28             unless defined($self->{_REGISTRY}{$package});
29 0           while (@_) {
30 0           local $_=uc(shift);
31 0           $self->{$_}=undef;
32 0           $self->{_REGISTRY}{_DATA}{$_}{source}=$package;
33 0           $self->{_REGISTRY}{_DATA}{$_}{access}=1; # default to rw
34 0           $self->{_REGISTRY}{_DATA}{$_}{type}="basic";
35             }
36             }
37              
38             sub _register { # Default back-end for package registration
39             # Call immediately after being bless()ed
40 0     0     my $self=shift;
41 0           my $package=caller;
42 0 0         $self->{_REGISTRY}{$package}{prep}=0 unless (defined($self->{_REGISTRY}{$package}));
43 0           return defined($self->{_REGISTRY}{$package});
44             }
45              
46             sub _unregister{# Default back-end for package de-registration
47             # If you wish to partially destruct an object, make sure to call this
48             # from each namespace being removed from the object
49 0     0     my $self=shift;
50 0           my $package=caller;
51 0           $self->_taint($package);
52 0           undef $self->{_REGISTRY}{$package};
53 0           return (!(defined($self->{_REGISTRY}{$package})));
54             }
55              
56             sub _primary { # Sets/detects whether a namespace contains the primary key
57             # Used internally to assure that the primary key's namespace is always
58             # in sync with the rest of the object
59 0     0     my $self=shift;
60 0   0       my $package=(UNIVERSAL::isa($_[0],__PACKAGE__) && shift) || caller;
61 0 0         if ($_[0]) {$self->{_REGISTRY}{_PRIMARY}=$package;$self->_taint;}
  0            
  0            
62 0   0       return ($self->{_REGISTRY}{_PRIMARY} || 0);
63             }
64              
65             sub _readonly { # Sets/detects whether a data mehod is tagged read-only
66             # Used by AUTOLOAD to detect read-only method calls
67 0     0     my $self=shift;
68 0   0       my $package=(UNIVERSAL::isa($_[0],__PACKAGE__) && shift) || caller;
69 0           my $var=uc(shift);
70 0 0         if (@_) {local $_=shift;$self->{_REGISTRY}{_DATA}{$var}{access}=(!($_)?1:0) if (/[01]/);} #Set to "0" to catch in this check next time
  0 0          
  0 0          
71 0   0       return (!($self->{_REGISTRY}{_DATA}{$var}{access}) ||
72             $self->{_REGISTRY}{_DATA}{$var}{source} eq $self->_primary);
73             }
74              
75             sub _validate { # Marks a namespace as tied to the back-end database
76             # Intended to be called on first refresh - Paired with _taint
77 0     0     my $self=shift;
78 0   0       my $package=shift || caller;
79 0 0         (my @vars=$self->_vars($package)) || return $self;
80 0           foreach my $var (@vars) {
81 0 0         if ($self->_isobject($var)) { # Reset embedded object information (only if needed)
82 0 0 0       unless ($self->{var} && ($self->{_REGISTRY}{_DATA}{$var}{data} eq $self->{$var})) {
83 0           $self->{_REGISTRY}{_DATA}{$var}{data}=$self->{$var};
84 0           $self->{_REGISTRY}{_DATA}{$var}{prep}=0;
85 0           $self->{$var}=undef;
86             }
87             }
88             }
89 0           $self->{_REGISTRY}{$package}{prep}=1;
90 0           $self->{_REGISTRY}{ref($self)}{prep}=1;
91 0           $self->_clean($package);
92             }
93              
94             sub _taint { # Marks a namespace as untied from the back-end database
95             # Intended to be called on destruction only
96 0     0     my $self=shift;
97 0   0       my $package=shift || caller;
98 0           $self->_dirty($package);
99 0 0         (my @vars=$self->_vars($package)) || return $self;
100 0           foreach my $var (@vars) {
101 0 0         if ($self->_isobject($var)) { # Reset embedded object information (only if needed)
102 0           $self->{_REGISTRY}{_DATA}{$var}{prep}=0;
103 0           $self->{$var}=undef;
104             }
105             }
106 0           $self->{_REGISTRY}{$package}{prep}=0;
107             }
108              
109             sub _clean { # Marks a namespace as in-sync with the back-end database
110             # Intended to be called on all calls to add(), refresh() and update()
111 0     0     my $self=shift;
112 0   0       my $package=shift || caller;
113 0           $self->{_REGISTRY}{$package}{dirty}=0;
114             }
115              
116             sub _dirty { # Marks a namespace as out-of-sync with the back-end databse
117             # Intended to be called upon a write-access call to a class-method
118 0     0     my $self=shift;
119 0   0       my $package=shift || caller;
120 0           $self->{_REGISTRY}{$package}{dirty}=1;
121             }
122              
123             sub _vars { # Returns a list of variables registered to a specific namespace
124             # Used internally by default _refresh() and update() methods
125 0     0     my $self=shift;
126 0   0       my $package=shift || caller;
127 0           my @vars = ();
128 0           my @keys = keys(%{$self->{_REGISTRY}{_DATA}});
  0            
129 0           foreach my $var(@keys) {
130 0 0         push @vars,$var if ($self->{_REGISTRY}{_DATA}{$var}{source} eq $package);
131             }
132 0           return @vars;
133             }
134              
135             sub _refresh { # Default back-end for refresh
136             # Inherited classes should implement a custom _refresh()
137             # Alternatively, the default _refresh may be used if a valid DBI connection
138             # is set using $__PACKAGE__::dbh and the table is set to $__PACKAGE__::table
139 0     0     my $self=shift;
140 0   0       my $package=(UNIVERSAL::isa($_[0],__PACKAGE__) && shift) || ref($self);
141 0   0       my @vars=$self->_vars($package) || return $self;
142 0           my $sth;
143             {
144 1     1   10 no strict 'vars';
  1         3  
  1         183  
  0            
145 0           eval "\$sth=\$dbh->prepare_cached('SELECT \@vars FROM \$table WHERE (ID=?)');";
146             }
147 0 0         $sth->execute(@_) or return $self->blank;
148 0 0         if ($sth->rows!=1) {
149 0           $self->blank;
150             } else {
151 0           my $res=$sth->fetchrow_hashref;
152 0           foreach my $var (@vars) {
153 0           $self->{$var}=$res->{$var};
154             }
155 0           $self->_validate;
156             }
157 0           $sth->finish;
158 0           return $self;
159             }
160              
161             sub AUTOLOAD { # Default method call handler
162             # Current support:
163             # * Read/Write registered methods from internal hash
164 0     0     my $param;
165             my $package;
166             {
167 1     1   7 no strict 'vars';
  1         3  
  1         5509  
  0            
168 0           $AUTOLOAD=~s/(.*):://;
169 0           $package=$1;
170 0           $param=$AUTOLOAD;
171             }
172 0 0         if (UNIVERSAL::isa($_[0],__PACKAGE__)) { # Method call of a sub-class
173 0           my $self=shift;
174 0 0         if ($self->{_REGISTRY}{_DATA}{uc($param)}) { # Acceptable function call
175 0           my $source=$self->{_REGISTRY}{_DATA}{uc($param)}{source};
176 0 0         if (!($self->valid($source))) {
177 0           $self->refresh($source);
178             }
179             # SET access
180 0 0 0       if ((@_) && !($self->_readonly($source,$param))) { # Update rewriteable request
181 0 0         if ($self->_isbasic($param)) {
    0          
182 0           $self->{uc($param)}=@_;
183 0           $self->_taint;
184             } elsif ($self->_isobject($param)) { # Object SET
185 0 0         unless ($self->_isobjarray($param)) { # No SET allowed on arrays
186 0           my ($temp,$pid)=@_;
187 0 0         if (ref($temp) eq $self->{_REGISTRY}{_DATA}{uc($param)}{class}) {
188             # TODO: see if temp->isa($self->{_REGISTRY}{_DATA}{uc($param)}{class})
189 0           $pid=$temp->id; #Retrieve ID from internal object
190             } else {
191 0           $pid=$temp; #Assume ID is specified if not compatible object
192             }
193 0           $self->{_REGISTRY}{_DATA}{uc($param)}{data}=$pid;
194 0           $self->_taint;
195             }
196             }
197             } # GET access
198 0 0         if ($self->_isobject($param)) { # Prepare object
199 0 0         return (wantarray?undef:0) unless $self->_o_prep($param);
    0          
200             }
201 0 0         if ($self->_isobjarray($param)) { # Object array returns special values
202 0 0         return (wantarray?@{$self->{uc($param)}}:$self->{_REGISTRY}{_DATA}{uc($param)}{prep});
  0            
203             } else {
204 0           return $self->{uc($param)};
205             }
206             }
207             }
208             }
209              
210             sub new { # Default constructor
211             # Do not overload this unless you're SURE you know what you're doing
212 0     0     my $self={ };
213 0           my $proto=shift;
214 0   0       my $class=ref($proto) || $proto;
215 0           bless $self,$class;
216 0           eval "foreach \$_ (\@".$class."::ISA) {eval \$_.\"::blank(\\\$self);\";}";
217 0           $self->_register;
218 0           $self->blank(@_);
219 0 0         if (@_) {
220 0 0         eval ($self->_primary."::_refresh(\$self,'".$self->_primary."',@_);") if ($self->_primary);
221 0           $self->_refresh(@_);
222             }
223 0           return $self;
224             }
225              
226             sub clean { # Returns true if namepace is in-sync with back-end database
227             # Be sure to check for valid()ity BEFORE using this
228 0     0     my $self=shift;
229 0   0       my $package=shift || caller;
230 0           return !($self->{_REGISTRY}{$package}{dirty});
231             }
232              
233             sub valid { # Returns true if namespace is tied and in-sync with back-end database
234 0     0     my $self=shift;
235 0   0       my $package=shift || ref($self);
236 0 0 0       if ($self->_primary)
  0            
237 0   0       {return ($self->{_REGISTRY}{$self->_primary}{prep} &&
238             $self->clean($self->_primary) &&
239             $self->{_REGISTRY}{$package}{prep} &&
240             $self->clean($package))}
241             else {return $self->{_REGISTRY}{$package}{prep} &&
242             $self->clean($package)};
243             }
244              
245             sub blank { # Default (abstract) blank method - used by the default constructor
246             # This should be overridden by any inherited class that's meant to be useful
247             # A typical blank() method should look like:
248             # sub blank {
249             # my $self=shift;
250             # $self->_register;
251             # $self->_blank("FOO", "BAR", ... , "LAST");
252             # }
253 0     0     $_[0]->_register;
254             }
255              
256             sub refresh { # Default front-end for refresh
257 0     0     my $self=shift;
258 0   0       my $package=shift || ref($self);
259 0           $self->_taint($package);
260 0           eval $package."::_refresh(\$self,".$self->id.");";
261 0           return $self->valid;
262             }
263              
264             sub id { # Default id method - must be explicitly so that it can be overloaded
265             # when needed for refresh, but not be dependant on the object being valid
266 0     0     my $self=shift;
267             # Set access
268 0 0 0       if ((@_) && !($self->_readonly("id"))) { # Update rewriteable request
269 0           $self->{ID}=@_;
270 0           $self->_taint;
271             }
272 0           return $self->{ID};
273             }
274              
275             sub _isbasic { # Returns true if access method marked as basic (default)
276 0     0     my $self=shift;
277 0           my $var=uc(shift);
278 0           return ($self->{_REGISTRY}{_DATA}{$var}{type} eq "basic");
279             }
280              
281             sub _isobject { # Returns true if access method marked as embedded object
282 0     0     my $self=shift;
283 0           my $var=uc(shift);
284 0           return ($self->{_REGISTRY}{_DATA}{$var}{type} eq "object");
285             }
286              
287             sub _isobjarray {
288 0     0     my $self=shift;
289 0           my $var=uc(shift);
290 0   0       return ($self->_isobject($var) && $self->{_REGISTRY}{_DATA}{$var}{array});
291             }
292              
293             sub _object { # Marks an access member as an object (call in blank)
294 0     0     my $self=shift;
295 0           my $var=uc(shift);
296 0   0       my $package=(UNIVERSAL::isa($_[0],__PACKAGE__) && shift) || ref($self);
297 0           $self->{_REGISTRY}{_DATA}{$var}{prep}=0;
298 0           $self->{_REGISTRY}{_DATA}{$var}{class}=$package;
299 0   0       $self->{_REGISTRY}{_DATA}{$var}{data}=$self->{$var} || undef;
300 0           $self->{_REGISTRY}{_DATA}{$var}{array}=0;
301 0           $self->{_REGISTRY}{_DATA}{$var}{type}="object";
302 0           $self->{$var}=undef;
303             }
304              
305             sub _objarray { # Marks an access member as an array of objects (call in blank)
306 0     0     my $self=shift;
307 0           my $var=uc(shift);
308 0   0       my $package=(UNIVERSAL::isa($_[0],__PACKAGE__) && shift) || ref($self);
309 0           $self->_object($var,$package);
310 0           $self->_readonly($var,1);
311 0           $self->{_REGISTRY}{_DATA}{$var}{array}=1;
312             }
313              
314             # This (objarray) won't be fully implemented until I can figure out how the heck
315             # to set the data source as an array - it probably has to be dealt with by
316             # end-module's refresh ($self->{$var}=@arrayofdata;) [UPDATE VALIDATE TO DEAL WITH THIS]...
317              
318             sub _o_prep {
319 0     0     my $self=shift;
320 0           my $var=uc(shift);
321 0           my $class=$self->{_REGISTRY}{_DATA}{$var}{class};
322 0           my $source=$self->{_REGISTRY}{_DATA}{$var}{source};
323 0 0         return 0 unless $self->valid($source);
324 0 0         return $self->{_REGISTRY}{_DATA}{$var}{prep} if
325             $self->{_REGISTRY}{_DATA}{$var}{prep};
326 0 0         if ($self->_isobjarray($var)) {
327 0           for (my $i=0;$i<=$#{$self->{_REGISTRY}{_DATA}{$var}{data}};$i++) {
  0            
328 0           $self->{$var}[$i]=$class->new($self->{_REGISTRY}{_DATA}{$var}{data}[$i]);
329             }
330 0           $self->{_REGISTRY}{_DATA}{$var}{prep}=$#{$self->{_REGISTRY}{_DATA}{$var}{data}}+1;
  0            
331             } else {
332 0 0         $self->{_REGISTRY}{_DATA}{$var}{prep}=(($self->{$var}=$class->new($self->{_REGISTRY}{_DATA}{$var}{data}))?1:0);
333             }
334 0           return $self->{_REGISTRY}{_DATA}{$var}{prep};
335             }
336              
337             # TODO: Update updates recursively (into embedded objects)
338             # UpdateNR updates non-0recursively (data ghets lost)
339              
340             # DOCUMENT: _validate also initializes objects by setting internal DATA value and clearing external value
341              
342              
343             package DBIx::Object::DBI; #Shortcut functions for DBI-based backend
344              
345             our @ISA=qw(DBIx::Object);
346              
347             sub blank {
348 0     0     $_[0]->_register;
349             }
350              
351             sub _dbidbh { # Sets/returns the DBI connection to use
352 0     0     my $self=shift;
353 0   0       my $package=(UNIVERSAL::isa($_[0],__PACKAGE__) && shift) || caller;
354 0 0         if($_[0]) {$self->{_REGISTRY}{$package}{DBI}{dbh}=$_[0];}
  0            
355 0           return $self->{_REGISTRY}{$package}{DBI}{dbh};
356             }
357              
358             sub _dbirefresh { # Sets/returns the SQL statement to run on refresh calls
359 0     0     my $self=shift;
360 0   0       my $package=(UNIVERSAL::isa($_[0],__PACKAGE__) && shift) || caller;
361 0 0         if($_[0]) {$self->{_REGISTRY}{$package}{DBI}{refresh}=$_[0];}
  0            
362 0           return $self->{_REGISTRY}{$package}{DBI}{refresh};
363             }
364              
365             sub _refresh { # Default back-end for DBI refresh
366             # Inherited classes may implement a custom _refresh()
367 0     0     my $self=shift;
368 0   0       my $package=(UNIVERSAL::isa($_[0],__PACKAGE__) && shift) || ref($self);
369 0           my $sth=$self->{_REGISTRY}{$package}{DBI}{dbh}->prepare_cached($self->{_REGISTRY}{$package}{DBI}{refresh});
370 0 0         $sth->execute(@_) or return $self->blank;
371 0 0         if ($sth->rows!=1) {
372 0           $self->blank;
373             } else {
374 0           my $res=$sth->fetchrow_hashref;
375 0           foreach my $key (keys %{$res}) {
  0            
376 0           $self->{uc($key)}=$res->{$key};
377             }
378 0           $self->_validate($package);
379             }
380 0           $sth->finish;
381 0           return $self;
382             }
383              
384             1;
385              
386             __END__