File Coverage

blib/lib/Class/PObject/Template.pm
Criterion Covered Total %
statement 230 252 91.2
branch 48 64 75.0
condition 27 50 54.0
subroutine 32 32 100.0
pod 0 13 0.0
total 337 411 82.0


line stmt bran cond sub pod time code
1             package Class::PObject::Template;
2              
3             # $Id: Template.pm,v 1.22.2.2 2004/05/20 06:53:53 sherzodr Exp $
4              
5 6     6   27 use strict;
  6         9  
  6         181  
6             #use diagnostics;
7 6     6   25 use Log::Agent;
  6         8  
  6         579  
8 6     6   1819 use Data::Dumper;
  6         17130  
  6         354  
9 6     6   38 use Carp;
  6         8  
  6         356  
10 6     6   26 use vars ('$VERSION');
  6         11  
  6         481  
11             use overload (
12 21     21   237 '""' => sub { $_[0]->id },
13 6         55 fallback=> 1
14 6     6   28 );
  6         10  
15              
16             $VERSION = '1.91';
17              
18             sub new {
19 64     64 0 93 my $class = shift;
20 64   33     248 $class = ref($class) || $class;
21              
22 64         1332 logtrc 2, "%s->new()", $class;
23              
24             # What should be done if we detect odd number of arguments?
25             # I'd say we should croak() right away. We don't want to
26             # end-up with corrupted record in the database, whether the
27             # code checks for error messages, or not!
28 64 50       3988 if ( @_ % 2 ) {
29 0         0 logcroak "Odd number of arguments passed to new(). May result in corrupted data"
30             }
31              
32 64         171 my $props = $class->__props();
33              
34             # object properties as represented internally by Class::PObject.
35             # Whoever accesses this information from within their code should be shot
36 64         254 my $self = {
37             columns => { @_ }, # <-- holds key/value pairs
38             _is_new => 1
39             };
40              
41 64         86 bless($self, $class);
42              
43             # It's possible that new() was not given all the column/values. So we
44             # detect the ones missing, and assign them 'undef'
45 64         58 for my $colname ( @{$props->{columns}} ) {
  64         136  
46 238 100       830 unless ( defined $self->{columns}->{$colname} ) {
47 61         83 $self->{columns}->{$colname} = undef
48             }
49             }
50              
51             # we may also check if the driver is indeed a valid one. However doing so
52             # does not allow creating in-memory objects without valid driver. So let's leave
53             # this test for related methods.
54              
55             # if pobject_init() exists, we should call it
56 64 100       300 if ( $self->UNIVERSAL::can('pobject_init') ) {
57 46         757 logtrc 2, "calling pobject_init()";
58 46         2442 $self->pobject_init
59             }
60 64         13402 return $self
61             }
62              
63              
64              
65             sub set {
66 41     41 0 61 my $self = shift;
67 41         72 my ($colname, $colvalue) = @_;
68              
69 41 50       98 unless ( @_ == 2 ) {
70 0         0 logcroak "%s->set() usage error", ref($self)
71             }
72              
73             # print Dumper($self->{columns});
74             #return $self->{columns}->{$colname} = ref($colvalue) ? $colvalue->id : $colvalue;
75              
76 41         92 my $props = $self->__props();
77 41         282 my ($typeclass, $args) = $props->{tmap}->{$colname} =~ m/^([a-zA-Z0-9_:]+)(?:\(([^\)]+)\))?$/;
78 41   50     1097 logtrc 3, "col: %s, type: %s, args: %s", $colname, $typeclass||"undef", $args || "none";
      100        
79 41 100       2912 if ( ref $colvalue eq $typeclass ) {
80 2         13 $self->{columns}->{$colname} = $colvalue;
81             } else {
82 39         289 $self->{columns}->{$colname} = $typeclass->new(id=>$colvalue);
83             }
84             }
85              
86              
87              
88              
89              
90             sub get {
91 169     169 0 244 my ($self, $colname) = @_;
92              
93 169 50       293 unless ( defined $colname ) {
94 0         0 logcroak "%s->get() usage error", ref($self)
95             }
96              
97            
98 169         232 my $colvalue = $self->{columns}->{$colname};
99              
100             # if the value is undef, we should return it as is, not to surprise anyone.
101             # if we keep going, the user will end up with an object,
102             # which may not appear as empty
103 169 100       257 unless ( defined $colvalue ) {
104             return undef
105 10         65 }
106            
107             # if we already have this value in our cache, let's return it
108 159 100       274 if ( ref $colvalue ) {
109 75         817 return $colvalue
110             }
111              
112              
113             # if we come this far, this value is being inquired for the first
114             # time. So we should load() it.
115             # To do this, we first need to identify its column type, to know how
116             # to inflate it.
117 84         151 my $props = $self->__props();
118 84         454 my ($typeclass, $args) = $props->{tmap}->{ $colname } =~ m/^([a-zA-Z0-9_:]+)(?:\(([^\)]+)\))?$/;
119 84 50       154 unless ( $typeclass ) {
120 0         0 logcroak "couldn't detect typeclass for this column (%s)", $colname
121             }
122              
123             # we should cache the loaded object in the column
124 84         392 $self->{columns}->{$colname} = $typeclass->load($colvalue);
125 84         584 return $self->{columns}->{$colname}
126             }
127              
128              
129              
130             sub save {
131 14     14 0 23 my $self = shift;
132 14   33     49 my $class = ref($self) || $self;
133              
134 14         357 logtrc 2, "%s->save(%s)", $class, join ", ", @_;
135              
136 14         938 my $props = $self->__props();
137 14         66 my $driver_obj = $self->__driver();
138              
139             # We should realize that column values are of Class::PObject::Type
140             # class, so their values should be stringified before being
141             # passed to drivers' save() method.
142 13         122 my %columns = ();
143 13         19 while ( my ($k, $v) = each %{ $self->{columns} } ) {
  59         182  
144 46         162 $v = $v->id while ref $v;
145 46         67 $columns{$k} = $v
146             }
147              
148             # we now call the driver's save() method, with the name of the class,
149             # all the props passed to pobject(), and column values to be stored
150 13         52 my $rv = $driver_obj->save($class, $props, \%columns);
151 12 50       33 unless ( defined $rv ) {
152 0         0 $self->errstr($driver_obj->errstr);
153 0         0 logerr $self->errstr;
154             return undef
155 0         0 }
156 12         56 $self->id($rv);
157 12         106 return $rv
158             }
159              
160              
161              
162              
163              
164              
165              
166             sub fetch {
167 2     2 0 4 my $self = shift;
168 2         5 my ($terms, $args) = @_;
169 2   33     11 my $class = ref($self) || $self;
170              
171 2         42 logtrc 2, "%s->fetch()", $class;
172              
173 2   100     112 $terms ||= {};
174 2   100     7 $args ||= {};
175              
176 2         5 my $props = $self->__props();
177 2         5 my $driver = $self->__driver();
178              
179 2         10 while ( my ($k, $v) = each %$terms ) {
180 1         4 $v = $v->id while ref $v;
181 1         4 $terms->{$k} = $v
182             }
183 2         8 my $ids = $driver->load_ids($class, $props, $terms, $args);
184              
185 2         840 require Class::PObject::Iterator;
186 2         12 return Class::PObject::Iterator->new($class, $ids);
187             }
188              
189              
190              
191              
192              
193              
194              
195              
196             sub load {
197 40     40 0 54 my $self = shift;
198 40         58 my ($terms, $args) = @_;
199 40   33     154 my $class = ref($self) || $self;
200              
201 40         948 logtrc 2, "%s->load()", $class;
202              
203 40 100       2458 $terms = {} unless defined $terms;
204 40 100       86 $args = {} unless defined $args;
205              
206             # if we're called in void context, why bother?
207 40 50       75 unless ( defined wantarray() ) {
208             return undef
209 0         0 }
210              
211             # if we are not called in context where array value is expected,
212             # we optimize our query by defining 'limit'
213 40 100       61 unless ( wantarray() ) {
214 23         67 $_[1]->{limit} = 1
215             }
216              
217 40         85 my $props = $self->__props();
218 40         86 my $driver_obj = $self->__driver();
219 40         49 my $ids = []; # we first initialize an empty ID list
220              
221             # now, if we had a single argument, and that argument was not a HASH,
222             # we assume we received an ID
223 40 100 66     169 if ( defined($terms) && (ref $terms ne 'HASH') ) {
224 19         41 $ids = [ $terms ]
225             } else {
226 21         70 while ( my ($k, $v) = each %$terms ) {
227 14 50       48 if ( $props->{tmap}->{$k} =~ m/^(MD5|ENCRYPT)$/ ) {
228 0         0 carp "cannot select by '$1' type columns (Yet!)"
229             }
230             # following trick will enable load(\%terms) syntax to work
231             # by passing objects.
232 14         51 $terms->{$k} = $terms->{$k}->id while ref $terms->{$k};
233             }
234 21 50       66 $ids = $driver_obj->load_ids($class, $props, $terms, $args) or return
235             }
236 40 100       87 return () unless scalar(@$ids);
237             # if called in array context, we return an array of objects:
238 39 100       67 if ( wantarray() ) {
239 16         22 my @data_set = ();
240 16         21 for my $id ( @$ids ) {
241 28 50       82 my $row = $driver_obj->load($class, $props, $id) or next;
242 28         130 my $o = $self->new( %$row );
243 28         69 $o->{_is_new} = 0;
244 28         76 push @data_set, $o
245             }
246             return @data_set
247 16         71 }
248             # if we come this far, we're being called in scalar context
249 23 100       90 my $row = $driver_obj->load($class, $props, $ids->[0]) or return;
250 22         112 my $o = $self->new( %$row );
251 22         46 $o->{_is_new} = 0;
252 22         121 return $o
253             }
254              
255              
256              
257             sub remove {
258 1     1 0 2 my $self = shift;
259 1         2 my $class = ref $self;
260              
261 1         20 logtrc 2, "%s->remove()", $class;
262 1 50       55 unless ( ref $self ) {
263 0         0 logcroak "remove() used as a static method";
264             }
265              
266 1         3 my $props = $self->__props();
267 1         2 my $driver_obj = $self->__driver();
268              
269             # if 'id' field is missing, most likely it's because this particular object
270             # hasn't been saved into disk yet
271 1 50       4 unless ( defined $self->id) {
272 0         0 logcroak "object is not saved into disk yet"
273             }
274              
275 1         5 my $rv = $driver_obj->remove($class, $props, $self->id);
276 1 50       4 unless ( defined $rv ) {
277 0         0 $self->errstr($driver_obj->errstr);
278             return undef
279 0         0 }
280 1         8 return $rv
281             }
282              
283              
284              
285              
286              
287              
288              
289             sub remove_all {
290 6     6 0 16 my ($self, $terms) = @_;
291 6   33     49 my $class = ref($self) || $self;
292              
293 6         162 logtrc 2, "%s->remove_all()", $class;
294              
295 6   100     453 $terms ||= {};
296 6         20 my $props = $self->__props();
297 6         20 my $driver_obj = $self->__driver();
298              
299 6         34 while ( my ($k, $v) = each %$terms ) {
300 1         3 $v = $v->id while ref $v;
301 1         4 $terms->{$k} = $v
302             }
303              
304 6         50 my $rv = $driver_obj->remove_all($class, $props, $terms);
305 6 50       21 unless ( defined $rv ) {
306 0         0 $self->errstr($driver_obj->errstr());
307             return undef
308 0         0 }
309 6         37 return 1
310             }
311              
312              
313              
314              
315             sub drop_datasource {
316 5     5 0 10 my $self = shift;
317 5   33     32 my ($class) = ref ($self) || $self;
318              
319 5         131 logtrc 2, "%s->drop_datasource", $self;
320              
321 5         371 my $props = $self->__props();
322 5         15 my $driver_obj = $self->__driver();
323              
324 5         23 my $rv = $driver_obj->drop_datasource($class, $props);
325 5 50       17 unless ( defined $rv ) {
326 0         0 $self->errstr( $driver_obj->errstr );
327             return undef
328 0         0 }
329              
330 5         23 return 1
331             }
332              
333              
334              
335              
336              
337              
338             sub count {
339 17     17 0 37 my ($self, $terms) = @_;
340 17   33     95 my $class = ref($self) || $self;
341              
342 17         427 logtrc 2, "%s->count()", $class;
343              
344 17   100     1232 $terms ||= {};
345 17         53 my $props = $self->__props();
346 17         47 my $driver_obj = $self->__driver();
347              
348 17         76 while ( my ($k, $v) = each %$terms ) {
349 6         34 $v = $v->id while ref $v;
350 6         22 $terms->{$k} = $v
351             }
352 17         84 return $driver_obj->count($class, $props, $terms)
353             }
354              
355              
356              
357             sub errstr {
358 7     7 0 9 my $self = shift;
359 7   33     24 my $class = ref($self) || $self;
360              
361 6     6   8826 no strict 'refs';
  6         8  
  6         1443  
362 7 50       27 if ( defined $_[0] ) {
363 0         0 ${ "$class\::errstr" } = $_[0]
  0         0  
364             }
365 7         9 return ${ "$class\::errstr" }
  7         45  
366             }
367              
368              
369              
370              
371              
372              
373              
374              
375              
376              
377             sub columns {
378 2     2 0 2 my $self = shift;
379 2   33     9 my $class = ref($self) || $self;
380              
381 2         43 logtrc 2, "%s->columns()", $class;
382              
383 2         111 my %columns = ();
384 2         3 while ( my ($k, $v) = each %{$self->{columns}} ) {
  9         27  
385 7         16 $v = $v->id while ref $v;
386 7         9 $columns{$k} = $v;
387             }
388              
389 2         12 return \%columns
390             }
391              
392              
393              
394              
395              
396              
397              
398             sub dump {
399 1     1 0 2 my ($self, $indent) = @_;
400              
401 1         4 require Data::Dumper;
402 1         9 my $d = Data::Dumper->new([$self], [ref $self]);
403 1   50     62 $d->Indent($indent||2);
404 1         18 $d->Deepcopy(1);
405 1         6 return $d->Dump()
406             }
407              
408              
409              
410              
411              
412             sub __props {
413 361     361   321 my $self = shift;
414              
415 361   66     1090 my @isa = (ref($self) || $self);
416 361         656 while ( my $pkg = shift @isa ) {
417 6     6   26 no strict 'refs';
  6         13  
  6         722  
418 364 100       256 if ( defined (my $props = ${ $pkg . "::props"}) ) {
  364         1048  
419 361   66     267 ${ (ref($self) || $self) . "::props" } = $props;
  361         928  
420 361         675 return $props;
421             }
422 3         6 push @isa, @{ $pkg . "::ISA" };
  3         15  
423             }
424              
425 0         0 logcroak "couldn't locate properties for this class";
426             }
427              
428              
429              
430             sub __driver {
431 86     86   96 my $self = shift;
432              
433 86         126 my $props = $self->__props();
434 86         174 my $pm = "Class::PObject::Driver::" . $props->{driver};
435              
436             # closure for getting and setting driver object
437             my $get_set_driver = sub {
438 6     6   23 no strict 'refs';
  6         9  
  6         902  
439 91 100   91   201 if ( defined $_[0] ) {
440 5         12 ${ "$pm\::__O" } = $_[0]
  5         29  
441             }
442 91         74 return ${ "$pm\::__O" }
  91         343  
443 86         349 };
444              
445 86         135 my $driver_obj = $get_set_driver->();
446 86 100       187 if ( defined $driver_obj ) {
447 80         314 return $driver_obj
448             }
449              
450             # if we got this far, it's the first time the driver is
451             # required.
452 6         462 eval "require $pm";
453 6 100       43 if ( $@ ) {
454 1         10 logcroak $@
455             }
456 5         51 $driver_obj = $pm->new();
457 5 50       25 unless ( defined $driver_obj ) {
458 0         0 $self->errstr($pm->errstr);
459             return undef
460 0         0 }
461 5         16 $get_set_driver->($driver_obj);
462 5         38 return $driver_obj
463             }
464              
465              
466              
467             package VARCHAR;
468 6     6   28 use vars ('@ISA');
  6         14  
  6         371  
469             require Class::PObject::Type::VARCHAR;
470             @ISA = ("Class::PObject::Type::VARCHAR");
471              
472              
473             package CHAR;
474 6     6   24 use vars ('@ISA');
  6         7  
  6         287  
475             require Class::PObject::Type::CHAR;
476             @ISA = ("Class::PObject::Type::CHAR");
477              
478              
479             package INTEGER;
480 6     6   38 use vars ('@ISA');
  6         8  
  6         322  
481             require Class::PObject::Type::INTEGER;
482             @ISA = ("Class::PObject::Type::INTEGER");
483              
484              
485             package TEXT;
486 6     6   21 use vars ('@ISA');
  6         11  
  6         313  
487             require Class::PObject::Type::TEXT;
488             @ISA = ("Class::PObject::Type::TEXT");
489              
490              
491             package ENCRYPT;
492 6     6   32 use vars ('@ISA');
  6         8  
  6         335  
493             require Class::PObject::Type::ENCRYPT;
494             @ISA = ("Class::PObject::Type::ENCRYPT");
495              
496              
497             package MD5;
498 6     6   20 use vars ('@ISA');
  6         8  
  6         315  
499             require Class::PObject::Type::MD5;
500             @ISA = ("Class::PObject::Type::MD5");
501              
502              
503             1;
504              
505             __END__;