File Coverage

blib/lib/Class/PObject/Template.pm
Criterion Covered Total %
statement 221 236 93.6
branch 53 80 66.2
condition 19 30 63.3
subroutine 34 34 100.0
pod 0 16 0.0
total 327 396 82.5


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