File Coverage

lib/Oryx/DBI/Class.pm
Criterion Covered Total %
statement 18 133 13.5
branch 0 32 0.0
condition 0 14 0.0
subroutine 6 14 42.8
pod 5 8 62.5
total 29 201 14.4


line stmt bran cond sub pod time code
1             package Oryx::DBI::Class;
2              
3 15     15   18192 use SQL::Abstract;
  15         156895  
  15         262  
4              
5 15     15   8731 use Oryx::DBI::Association;
  15         46  
  15         174  
6 15     15   7407 use Oryx::DBI::Attribute;
  15         56  
  15         188  
7 15     15   7086 use Oryx::DBI::Method;
  15         39  
  15         137  
8 15     15   6516 use Oryx::DBI::Parent;
  15         39  
  15         174  
9              
10 15     15   413 use base qw(Oryx::MetaClass);
  15         24  
  15         31847  
11              
12             # Other MetaClass constructs are true instances and save their meta
13             # data as $self->{meta}. Class meta objects are different because
14             # their state is saved as class data instead of as instances of the
15             # MetaClass class.
16              
17             # make some noise
18             our $DEBUG = 0;
19              
20 0     0 0   sub dbh { $_[0]->storage->dbh }
21              
22             sub create {
23 0     0 1   my ($class, $param) = @_;
24 0           my %query = ( table => $class->table );
25 0   0       $param->{_isa} ||= $class;
26              
27 0           $class->notify_observers('before_create', { param => $param, query => \%query });
28              
29 0           $_->create(\%query, $param) foreach $class->members;
30              
31             # grab out the attributes that this class knows about
32 0           my @keys = (keys %{$class->attributes});
  0            
33 0 0         push @keys, '_isa' if $class->is_abstract;
34 0           my $proto = { };
35 0           @$proto{@keys} = @$param{@keys};
36              
37 0           my $sql = SQL::Abstract->new;
38 0           my ($stmnt, @bind) = $sql->insert($query{table}, $proto);
39              
40 0           my $sth;
41 0           eval { $sth = $class->dbh->prepare_cached($stmnt) };
  0            
42 0 0         die "ERROR: statement $stmnt $@" if $@;
43 0           $sth->execute(@bind);
44 0           $sth->finish;
45              
46 0           $param->{id} = $class->lastId();
47 0           $proto->{id} = $class->lastId();
48              
49 0           $_->create(\%query, $param) foreach @{$class->parents};
  0            
50 0           $class->notify_observers('after_create', { param => $param, proto => $proto });
51              
52 0           return $class->construct($proto);
53             }
54              
55             sub retrieve {
56 0     0 1   my ($class, $id) = @_;
57              
58             # fetch the object from the cache if it exists
59 0           my $key = $class->_mk_cache_key($id);
60 0           my $object;
61 0 0         return $object if ($object = $Live_Objects{$key});
62              
63 0           my %query = (
64             table => $class->table,
65             fields => [ 'id' ],
66             where => { id => $id },
67             );
68              
69 0 0         if ($class->is_abstract) {
70 0 0         $DEBUG && $class->_carp("ABSTRACT CLASS retrieve $class");
71 0           push @{$query{fields}}, '_isa';
  0            
72             }
73 0 0         $DEBUG && $class->_carp("retrieve : id => $id");
74 0           $class->notify_observers('before_retrieve', { query => \%query, id => $id });
75 0           $_->retrieve(\%query, $id) foreach $class->members;
76 0           $_->retrieve(\%query, $id) foreach @{$class->parents};
  0            
77              
78 0           my $sql = SQL::Abstract->new;
79             my ($stmnt, @bind) = $sql->select(@query{
80 0           qw(table fields where order)
81             });
82 0           my $sth = $class->dbh->prepare_cached($stmnt);
83              
84 0           eval { $sth->execute(@bind) };
  0            
85 0 0         $class->_croak("execute failed [$stmnt], bind => "
86             .join(", ", @bind)." $@") if $@;
87              
88 0           my $values = $sth->fetch;
89 0           $sth->finish;
90              
91 0 0 0       if ($values and @$values) {
92 0           my $proto = $class->row2proto($query{fields}, $values);
93              
94 0 0 0       if ($class->is_abstract and $proto->{_isa} ne $class) {
95             # abstract classes are never instantiated directly, so we
96             # need to retrieve the decendant instead. The descendant's
97             # ID is the same as the abstract class' ID because we used
98             # the abstract class' sequence when the decendant instance
99             # was created... so no need for a JOIN here
100 0 0         $DEBUG>1 && $class->_carp("RETRIEVE subclass : "
101             .$proto->{_isa}." for abstract class : $class");
102 0           eval "use ".$proto->{_isa};
103 0 0         $class->_croak($@) if $@;
104 0           return $proto->{_isa}->retrieve($proto->{id});
105             }
106 0           $class->notify_observers('after_retrieve', { proto => $proto });
107 0           return $class->construct($proto);
108             } else {
109 0           return undef;
110             }
111             }
112              
113             sub update {
114 0     0 1   my ($self) = @_;
115 0 0         return if $self->is_abstract;
116 0           my %query = (
117             table => $self->table,
118             fieldvals => { },
119             where => { id => $self->id },
120             );
121 0           $self->notify_observers('before_update', { query => \%query });
122 0           $_->update(\%query, $self) foreach $self->members;
123 0           $_->update(\%query, $self) foreach @{$self->parents};
  0            
124              
125 0           my $sql = SQL::Abstract->new;
126             my ($stmnt, @bind) = $sql->update(@query{
127 0           qw(table fieldvals where)
128             });
129 0           my $sth = $self->dbh->prepare_cached($stmnt);
130              
131 0           eval { $sth->execute(@bind) };
  0            
132 0 0         $self->_croak("execute failed for $stmnt, bind => "
133             .join(", ", @bind)." $@") if $@;
134              
135 0           $sth->finish;
136              
137 0           $self->notify_observers('after_update');
138 0           return $self;
139             }
140              
141             sub delete {
142 0     0 1   my ($self) = @_;
143 0           my %query = (
144             table => $self->table,
145             where => { id => $self->id },
146             );
147 0           $self->notify_observers('before_delete', { query => \%query });
148 0           $_->delete(\%query, $self) foreach $self->members;
149 0           $_->delete(\%query, $self) foreach @{$self->parents};
  0            
150              
151 0           my $sql = SQL::Abstract->new;
152 0           my ($stmnt, @bind) = $sql->delete(@query{qw(table where)});
153 0           my $sth = $self->dbh->prepare_cached($stmnt);
154              
155 0           $sth->execute(@bind);
156 0           $sth->finish;
157              
158 0           $self->remove_from_cache;
159 0           $self->notify_observers('after_delete');
160 0           return $self;
161             }
162              
163             sub search {
164 0     0 1   my ($class, $param, $order, $limit, $offset) = @_;
165 0   0       my %query = (
166             table => $class->table,
167             fields => [ 'id' ],
168             where => $param,
169             order => $order || [ ],
170             );
171 0 0         $limit = -1 unless defined $limit;
172              
173 0 0         push @{$query{fields}}, '_isa' if $class->is_abstract;
  0            
174              
175 0           $class->notify_observers('before_search', {
176             query => \%query,
177             param => $param,
178             order => $order,
179             limit => $limit,
180             }
181             );
182              
183 0           $_->search(\%query) foreach $class->members;
184 0           $_->search(\%query) foreach @{$class->parents};
  0            
185              
186 0           my $sql = SQL::Abstract->new(cmp => 'like');
187             my ($stmnt, @bind) = $sql->select(@query{
188 0           qw(table fields where order)
189             });
190             #warn 'SEARCH STATEMENT => '.$stmnt;
191 0           my $sth = $class->dbh->prepare_cached($stmnt);
192 0           $sth->execute(@bind);
193              
194 0           my (@objs, @row);
195 0 0         if (defined $offset) {
196 0           @row = $sth->fetch while ($offset-- > 0);
197             }
198 0   0       while ($limit-- and (@row = $sth->fetch)) {
199 0           my $proto = $class->row2proto($query{fields}, \@row);
200 0           push @objs, $class->construct($proto);
201             }
202 0           $sth->finish;
203              
204 0           $class->notify_observers('after_search', {
205             query => \%query,
206             param => $param,
207             order => $order,
208             limit => $limit,
209             objects => \@objs
210             }
211             );
212              
213 0           return @objs;
214             }
215              
216             sub row2proto {
217 0     0 0   my ($class, $fields, $values) = @_;
218 0           my $proto = { };
219 0           @$proto{ @$fields } = @$values;
220 0           return $proto;
221             }
222              
223             sub lastId {
224 0     0 0   my $class = shift;
225 0           $class->storage->util->lastval($class->dbh, $class->table);
226             }
227              
228              
229             1;
230             __END__