File Coverage

lib/Ormlette.pm
Criterion Covered Total %
statement 733 940 77.9
branch 121 210 57.6
condition 5 6 83.3
subroutine 187 225 83.1
pod 2 2 100.0
total 1048 1383 75.7


line stmt bran cond sub pod time code
1             package Ormlette;
2              
3 8     8   1150242 use strict;
  9         1702  
  8         1983  
4 8     9   144 use warnings;
  8         16  
  8         738  
5              
6             our $VERSION = 0.005;
7              
8 8     8   48 use Carp;
  8         21  
  8         10260  
9              
10             sub init {
11 43     43 1 157621 my ($class, $dbh, %params) = @_;
12              
13 43 100       382 croak 'First param to Ormlette->init must be a connected database handle'
14             unless $dbh->isa('DBI::db');
15              
16 41   66     234 my $namespace = $params{namespace} || caller;
17              
18 41         394 my $tbl_names = _scan_tables($dbh, $namespace, %params);
19              
20 41 50       533 my $self = bless {
    100          
21             dbh => $dbh,
22             debug => $params{debug} ? 1 : 0,
23             ignore_root => $params{ignore_root},
24             isa => $params{isa},
25             namespace => $namespace,
26             readonly => $params{readonly} ? 1 : 0,
27             tbl_names => $tbl_names,
28             }, $class;
29              
30 41 100       343 $self->_build_root_pkg unless $self->{ignore_root};
31 41         317 $self->_build_table_pkg($_) for keys %$tbl_names;
32              
33 41         344 return $self;
34             }
35              
36 231     231 1 3551 sub dbh { $_[0]->{dbh} }
37              
38             sub _scan_tables {
39 41     41   124 my ($dbh, $namespace, %params) = @_;
40              
41 41         348 my @tables = $dbh->tables(undef, undef, undef, 'TABLE');
42 41 50       22147 if (my $quote_char = $dbh->get_info(29)) {
43 41         441 for (@tables) {
44 53         352 s/$quote_char$//;
45 53         491 s/^.*$quote_char//;
46             }
47             }
48              
49 41 100       251 if ($params{tables}) {
    100          
50 3         5 my %include = map { $_ => 1 } @{$params{tables}};
  3         43  
  3         12  
51 3         10 @tables = grep { $include{$_} } @tables;
  5         180  
52             } elsif ($params{ignore_tables}) {
53 2         10 my %exclude = map { $_ => 1 } @{$params{ignore_tables}};
  2         6  
  2         48  
54 2         9 @tables = grep { !$exclude{$_} } @tables;
  3         10  
55             }
56              
57 41         103 my %tbl_names;
58 41         111 for (@tables) {
59 49         131 my $tbl = lc $_;
60 49         317 $tbl =~ s/__(.)/::\U$1/g;
61 49         253 my @words = split '_', $tbl;
62 49         142 $tbl_names{$_} = $namespace . '::' . (join '', map { ucfirst } @words);
  69         1293  
63             }
64              
65 41         186 return \%tbl_names;
66             }
67              
68             sub _scan_fields {
69 49     49   93 my ($self, $tbl_name) = @_;
70              
71 49         141 my $sth = $self->dbh->prepare("SELECT * FROM $tbl_name LIMIT 0");
72 49         3990 $sth->execute;
73 49         592 return $sth->{NAME};
74             }
75              
76             # Code generation methods below
77              
78             sub _build_root_pkg {
79 40     40   104 my $self = shift;
80 40         97 my $pkg_name = $self->{namespace};
81              
82 40         157 my $pkg_src = $self->_pkg_core($pkg_name);
83 40         319 $pkg_src .= $self->_root_methods;
84              
85 40 100       1069 $self->_compile_pkg($pkg_src) unless $pkg_name->can('_ormlette_init');
86 40         1321 $pkg_name->_ormlette_init_root($self);
87             }
88              
89             sub _build_table_pkg {
90 49     49   136 my ($self, $tbl_name) = @_;
91 49         305 my $pkg_name = $self->{tbl_names}{$tbl_name};
92              
93 49         169 my $field_list = $self->_scan_fields($tbl_name);
94              
95 49         614 my $pkg_src = $self->_pkg_core($pkg_name);
96 49         209 $pkg_src .= $self->_table_methods($tbl_name, $field_list);
97              
98 49 50       693 $self->_compile_pkg($pkg_src) unless $pkg_name->can('_ormlette_init');
99 49         1810 $pkg_name->_ormlette_init_table($self, $tbl_name);
100             }
101              
102             sub _compile_pkg {
103 81     81   206 my ($self, $pkg_src) = @_;
104 81         127 local $@;
105 81 50       1127 print STDERR $pkg_src if $self->{debug};
106 7 100   7   62 eval $pkg_src;
  7 100   7   15  
  7 100   7   262  
  7 50   6   40  
  7 50   6   84  
  7 0   6   363  
  7 50   6   40  
  7 50   6   18  
  7 0   6   13053  
  6 0   6   48  
  6 100   6   9  
  6 100   6   208  
  6 50   6   33  
  6 100   6   13  
  6 100   6   194  
  6 100   6   37  
  6 100   6   11  
  6 100   6   828  
  6 100   6   31  
  6 50   6   11  
  6 100   6   4423  
  6 100   6   41  
  6 100   6   11  
  6 100   6   189  
  6 100   6   26  
  6 100   6   10  
  6 0   6   256  
  6 0   6   29  
  6 0   5   8  
  6 0   5   1770  
  6 0   5   42  
  6 0   5   15  
  6 100   5   1983  
  6 100   4   42  
  6 0   4   14  
  6 0   4   210  
  6 0   4   38  
  6 0   4   12  
  6 0   4   13778  
  6 0   4   41  
  6 100   4   11  
  6 100   4   3555  
  6 100   4   46  
  6 50   4   14  
  6 0   4   997  
  6 0   4   35  
  6 100   4   89  
  6 50   4   298  
  6 50   4   38  
  6 0   17   11  
  6 50   14   1456  
  6 0   19   38  
  6 100   8   11  
  6 100   10   1827  
  6 50   5   38  
  6 50   5   13  
  6 100   5   194  
  6 50   5   32  
  6 50   11   11  
  6 50   4   553  
  6 0   3   35  
  6 0   3   12  
  6 0   3   3534  
  6 0   7   47  
  6 50   6   19  
  6 0   13   982  
  6 100   9   37  
  6 50   8   12  
  6 100   8   249  
  6 50   8   31  
  6 50   7   13  
  6 50   7   717  
  6 50   6   38  
  6 0   6   11  
  6 100   5   2992  
  6 50   5   51  
  6 50   6   10  
  6 50   11   270  
  6 50   4   34  
  6 50   6   13  
  6 100   6   409  
  6 0   4   35  
  6 0   10   10  
  6 0   5   3870  
  5     20   35  
  5     1   11  
  5     11   1740  
  5     13   34  
  5     1   7  
  5     5   173  
  5     5   27  
  5     10   9  
  5     4   477  
  5     1   28  
  5     4   9  
  5     10   3181  
  5     8   37  
  5     2   13  
  5     3   2153  
  4     2   30  
  4     5   8  
  4     3   148  
  4     2   25  
  4     0   8  
  4     0   513  
  4     0   30  
  4     4   10  
  4     7   270  
  4     11   41  
  4     4   7  
  4     0   976  
  4     18   28  
  4     4   10  
  4     12   481  
  4     0   143  
  4     0   8  
  4     9   2964  
  4     2   29  
  4     14   9  
  4     7   265  
  4     7   21  
  4     1   8  
  4     0   1165  
  4     7   30  
  4     0   9  
  4     3   500  
  4     0   27  
  4     2   10  
  4     0   827  
  4     13   23  
  4     1   6  
  4     3   255  
  4     3   32  
  4     0   10  
  4     0   1502  
  4     7   29  
  4     6   7  
  4     3   1720  
  4     5   26  
  4     4   9  
  4     3   128  
  4     1   21  
  4     4   9  
  4     2   261  
  4     0   25  
  4     2   6  
  4     4   1281  
  80     1   7302  
  16     0   728  
  12     1   1474  
  18     0   1657  
  9     4   865  
  5     1   517  
  0     6   0  
  2     3   787  
  2     0   19  
  0     3   0  
  0     3   0  
  1     2   5  
  0     4   0  
  0     6   0  
  0     9   0  
  0     4   0  
  0     9   0  
  1     1   14  
  5     1   16  
  1     5   901  
  1     2   11  
  0     1   0  
  0     1   0  
  0     4   0  
  0     12   0  
  1     0   35  
  6     2   49  
  0     1   0  
  0     1   0  
  0     1   0  
  0     1   0  
  5     3   12  
  5     0   41  
  4     0   9  
  4     3   14  
  4     0   9  
  4     5   17  
  4     0   9  
  4     3   20  
  8     0   21  
  4     0   17  
  4     0   9  
  4     1   29  
  4     0   14  
  4     0   16  
  8     0   21  
  10     1   74  
  16     0   60  
  11     0   37  
  3     0   7  
  3     0   14  
  3     0   11  
  3     0   15  
  3     0   6  
  3     1   13  
  3     0   7  
  3     1   18  
  7     3   17  
  7     0   33  
  6     0   14  
  6     0   68  
  13         33  
  13         47  
  9         28  
  9         37  
  8         25  
  8         33  
  8         24  
  8         29  
  8         32  
  8         34  
  7         23  
  7         30  
  7         18  
  7         30  
  6         16  
  6         24  
  6         16  
  6         23  
  5         16  
  5         24  
  5         16  
  5         31  
  6         13  
  6         28  
  6         103  
  5         29  
  10         20  
  10         42  
  10         295  
  7         38  
  4         12  
  4         19  
  3         53  
  3         13  
  5         13  
  5         22  
  5         161  
  5         24  
  4         9  
  4         21  
  3         166  
  2         13  
  2         4  
  2         14  
  2         85  
  1         6  
  8         12  
  8         31  
  8         392  
  4         30  
  3         5  
  3         21  
  3         140  
  2         11  
  19         414  
  19         79  
  19         937  
  16         109  
  1         3  
  1         6  
  1         54  
  1         6  
  11         20  
  11         46  
  11         416  
  8         55  
  12         22  
  12         65  
  16         584  
  12         90  
  0         0  
  0         0  
  3         13  
  3         25  
  4         13  
  4         27  
  1         3  
  1         5  
  9         18  
  9         47  
  15         271  
  14         80  
  15         134  
  15         64  
  9         361  
  7         42  
  3         39  
  3         16  
  2         6  
  2         16  
  5         95  
  5         16  
  3         38  
  3         17  
  2         9  
  2         16  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  4         51  
  3         26  
  15         1049  
  6         24  
  0         0  
  8         48  
  6         66  
  14         76  
  0         0  
  0         0  
  19         347  
  12         48  
  14         66  
  7         54  
  4         65  
  1         10  
  0         0  
  7         72  
  0         0  
  3         27  
  3         4  
  5         23  
  1         3  
  4         20  
  2         6  
  4         25  
  2         5  
  2         230  
  0         0  
  0         0  
  4         35  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  3         35  
  3         40  
  2         12  
  1         5  
  1         5  
  1         142  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  10         16  
  10         16  
  10         24  
  10         698  
  10         48  
  1         2  
  1         2  
  1         3  
  1         170  
  1         5  
  1         4  
  0         0  
  0         0  
  0         0  
  0         0  
  3         5  
  3         4  
  3         9  
  3         303  
  3         12  
  3         7  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  4         5  
  4         7  
  4         11  
  4         274  
  4         10  
  2         4  
  2         3  
  2         7  
  2         268  
  5         40  
  4         5  
  4         12  
  2         7  
  2         146  
  3         15  
  7         19  
  7         15  
  7         265  
  5         432  
  7         58  
  5         18  
  4         6  
  4         6  
  4         13  
  4         349  
  4         15  
  3         5  
  3         6  
  3         10  
  3         322  
  3         24  
  3         23  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         2  
  2         5  
  2         5  
  2         286  
  2         14  
  4         12  
  3         118  
  3         8  
  3         265  
  2         17  
  3         4  
  3         4  
  3         4  
  3         8  
  3         7  
  3         173  
  3         6  
  3         8  
  5         10  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  2         4  
  2         7  
  2         5  
  2         18  
  2         11  
  2         802  
  2         5  
  2         11  
  2         7  
  4         9  
  4         5  
  4         6  
  4         20  
  4         11  
  4         576  
  4         6  
  4         12  
  4         7  
  1         1  
  1         3  
  1         2  
  1         7  
  1         4  
  1         164  
  1         3  
  1         5  
  1         3  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         2  
  1         4  
  1         2  
  1         2  
  1         3  
  1         2  
  1         3  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         3  
  1         113  
  1         4  
  1         5  
  1         7  
  0         0  
  2         989  
  2         6  
  2         3  
  2         11  
  2         9  
  2         326  
  2         4  
  2         8  
  15         45  
  0         0  
  0         0  
  0         0  
  2         5  
  2         5  
  2         2  
  2         4  
  2         4  
  2         5  
  6         13  
  4         5  
  4         7  
  4         13  
  4         12  
  4         321  
  6         12  
  6         200  
  10         22  
  6         253  
  6         21  
  4         7  
  4         11  
  4         12  
  4         211  
  4         10  
  4         18  
  6         14  
  1         3  
  1         27  
  1         116  
  1         4  
  1         4  
  1         4  
  2         1055  
  2         8  
  2         3  
  2         4  
  2         7  
  2         8  
  2         6  
  3         9  
  2         5  
  2         5  
  2         15  
  2         254  
  2         9  
  2         6  
  2         9  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  3         7  
  3         10  
  3         3  
  3         5  
  3         8  
  3         7  
  3         6  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  3         9  
  3         231  
  3         10  
  6         2123  
  6         55  
  3         5  
  3         5  
  3         29  
  1         2  
  1         5  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         6  
  1         166  
  1         6  
  3         1826  
  3         16  
  2         3  
  2         3  
  2         7  
  0         0  
  0         0  
  2         9  
  2         7  
  2         5  
  2         9  
  2         8  
  2         7  
  2         195  
  2         8  
  6         1050  
  6         23  
  5         699  
  5         21  
  7         45  
  6         19  
  3         59  
  3         11  
  5         38  
  5         15  
  1         30  
  1         4  
  1         28  
  1         6  
  4         4  
  6         1906  
  4         32  
  4         11  
  3         30  
  3         14  
  3         6  
  3         6  
  3         9  
  3         12  
  10         14  
  10         26  
  0         0  
  2         7  
  5         347  
  5         13  
  7         2167  
  7         40  
  3         4  
  3         7  
  3         27  
  1         2  
  1         2  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         4  
  1         149  
  1         4  
  1         7  
  1         14  
  1         58  
  2         35  
  2         4  
  2         10  
  2         14  
  2         34  
  2         15  
  2         13  
  2         15  
  4         33  
  4         7  
  4         187  
  4         32  
  1         7  
  1         5  
  4         2524  
  4         11  
  4         7  
  4         19  
  4         12  
  4         18  
  7         1206  
  8         21  
  8         61  
  7         49  
  4         15  
  4         518  
  5         21  
  4         18  
  9         560  
  5         8  
  5         31  
  5         48  
  0         0  
  0         0  
  0         0  
  0         0  
  3         26  
  3         6  
  3         17  
  3         28  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         3  
  1         4  
  1         2  
  1         3  
  1         11  
  1         2  
  1         4  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         3  
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
107 80 50       407 die $@ if $@;
108             }
109              
110             sub _pkg_core {
111 88     87   294 my ($self, $pkg_name) = @_;
112              
113 88         239 my $core = <<"END_CODE";
114             package $pkg_name;
115              
116             use strict;
117             use warnings;
118              
119             use Carp;
120              
121             END_CODE
122              
123 8     8   66 no strict 'refs';
  8         17  
  8         13892  
124 88 100 100     322 if ($self->{isa} && !@{ $pkg_name . '::ISA' }) {
  5         31  
125 3         6 my $isa = $self->{isa};
126 3         9 $core .= <<"END_CODE";
127             our \@ISA = '$isa';
128              
129             END_CODE
130             }
131 8     8   65 use strict 'refs';
  8         17  
  8         17446  
132              
133 87         276 $core .= <<"END_CODE";
134             my \$_ormlette_dbh;
135              
136             sub dbh { \$_ormlette_dbh }
137              
138             sub _ormlette_init {
139             my (\$class, \$ormlette) = \@_;
140             \$_ormlette_dbh = \$ormlette->dbh;
141             }
142              
143             END_CODE
144              
145 87         311 return $core;
146             }
147              
148             sub _root_methods {
149 39     39   71 my $self = shift;
150              
151 39         116 return <<"END_CODE";
152             sub _ormlette_init_root {
153             my (\$class, \$ormlette) = \@_;
154             \$class->_ormlette_init(\$ormlette);
155             }
156              
157             END_CODE
158             }
159              
160             sub _table_methods {
161 50     50   219 my ($self, $tbl_name, $field_list) = @_;
162 50         168 my $pkg_name = $self->{tbl_names}{$tbl_name};
163              
164 49         128 my $select_fields = join ', ', map { "$tbl_name.$_" } @$field_list;
  76         278  
165 49         270 my $field_vars = '$' . join ', $', @$field_list;
166 48         83 my $inflate_fields; $inflate_fields .= "$_ => \$$_, " for @$field_list;
  48         300  
167              
168 48         122 my @accessor_fields = grep { !$pkg_name->can($_) } @$field_list;
  75         728  
169 48         80 my $code;
170 48 50       263 $code = $self->_add_accessors($pkg_name, @accessor_fields)
171             if @accessor_fields;
172              
173 49         318 $code .= <<"END_CODE";
174             sub table { '$tbl_name' }
175              
176             sub _ormlette_init_table {
177             my (\$class, \$ormlette, \$table_name) = \@_;
178             \$class->_ormlette_init(\$ormlette);
179             }
180              
181             sub iterate {
182             my \$class = shift;
183             my \$callback = shift;
184              
185             my \$sql = 'SELECT $select_fields FROM $tbl_name';
186             \$sql .= ' ' . shift if \@_;
187             my \$sth = \$class->dbh->prepare_cached(\$sql);
188             \$sth->execute(\@_);
189              
190             local \$_;
191             while (\$_ = \$class->_ormlette_load_from_sth(\$sth)) {
192             \$callback->();
193             }
194             }
195              
196             sub select {
197             my \$class = shift;
198             my \@results;
199             \$class->iterate(sub { push \@results, \$_ }, \@_);
200             return \\\@results;
201             }
202              
203             sub _ormlette_load_from_sth {
204             my (\$class, \$sth) = \@_;
205              
206             \$sth->bind_columns(\\(my ($field_vars)));
207             return unless \$sth->fetch;
208              
209             return bless { $inflate_fields }, \$class;
210             }
211              
212             END_CODE
213              
214 48         146 $code .= <<"END_CODE";
215             sub load {
216             my \$class = shift;
217              
218             croak '->load requires at least one argument' unless \@_;
219              
220             my \$sql = 'SELECT $select_fields FROM $tbl_name WHERE ';
221             my \@criteria;
222              
223             END_CODE
224              
225 48         131 my @key = $self->dbh->primary_key(undef, undef, $tbl_name);
226 49 100       66491 if (@key == 1) {
227 13         482 my $key_criteria = $key[0] . ' = ?';
228              
229 13         97 $code .= <<"END_CODE";
230              
231             if (\@_ == 1) {
232             \$sql .= '$key_criteria';
233             \@criteria = \@_;
234             } else {
235             croak 'if not using a single-field key, ->load requires a hash of criteria'
236             unless \@_ % 2 == 0;
237              
238             my \%params = \@_;
239             \$sql .= join ' AND ', map { "\$_ = ?" } keys \%params;
240             \@criteria = values \%params;
241             }
242             END_CODE
243             } else { # primary key absent or multi-field
244 36         156 $code .= <<"END_CODE";
245             croak 'if not using a single-field key, ->load requires a hash of criteria'
246             unless \@_ % 2 == 0;
247              
248             my \%params = \@_;
249             \$sql .= join ' AND ', map { "\$_ = ?" } keys \%params;
250             \$sql .= ' LIMIT 1';
251             \@criteria = values \%params;
252             END_CODE
253             }
254              
255 48         138 $code .= <<"END_CODE";
256             my \$sth = \$class->dbh->prepare_cached(\$sql);
257             \$sth->execute(\@criteria);
258              
259             my \$obj = \$class->_ormlette_load_from_sth(\$sth);
260             \$sth->finish;
261              
262             return \$obj;
263             }
264             END_CODE
265              
266 48 100       311 $code .= $self->_table_mutators($tbl_name, $field_list)
267             unless $self->{readonly};
268              
269 48         299 return $code;
270             }
271              
272             sub _add_accessors {
273 48     48   126 my ($self, $pkg_name, @accessor_fields) = @_;
274              
275 48         77 my $accessor_sub;
276 48 100       141 if ($self->{readonly}) {
277 2         6 $accessor_sub = '$_[0]->{$attr}';
278             } else {
279 47         107 $accessor_sub = '
280             $_[0]->{$attr} = $_[1] if defined $_[1];
281             $_[0]->{$attr};'
282             }
283              
284 49         115 my $field_list = join ' ', @accessor_fields;
285 49         215 return <<"END_CODE";
286             {
287             no strict 'refs';
288             for my \$attr (qw( $field_list )) {
289             *\$attr = sub {
290             $accessor_sub
291             };
292             }
293             }
294             END_CODE
295             }
296              
297             sub _table_mutators {
298 47     46   242 my ($self, $tbl_name, $field_list) = @_;
299 47         130 my $pkg_name = $self->{tbl_names}{$tbl_name};
300              
301 47         130 my $insert_fields = join ', ', @$field_list;
302 46         122 my $insert_params = join ', ', ('?') x @$field_list;
303 46         102 my $insert_values = join ', ', map { "\$self->{$_}" } @$field_list;
  72         227  
304 46         125 my $handle_autoincrement = '';
305 72         220 my $init_all_attribs = join ",\n ",
306 46         91 map { "'$_' => \$params{'$_'}" } @$field_list;
307              
308 47         179 my @key = $self->dbh->primary_key(undef, undef, $tbl_name);
309 47 100       45038 if (@key == 1) {
310 13         34 my $key_field = $key[0];
311 13         208 $handle_autoincrement = qq(
312             \$self->{$key_field} =
313             \$self->dbh->last_insert_id(undef, undef, qw( $tbl_name $key_field ))
314             unless defined \$self->{$key_field};);
315             }
316              
317 47         310 my $code = <<"END_CODE";
318              
319             sub _ormlette_new {
320             my (\$class, \%params) = \@_;
321             bless {
322             $init_all_attribs
323             }, \$class;
324             }
325              
326             sub create {
327             my \$class = shift;
328             \$class->new(\@_)->insert;
329             }
330              
331             sub insert {
332             my \$self = shift;
333             my \$sql =
334             'INSERT INTO $tbl_name ( $insert_fields ) VALUES ( $insert_params )';
335             my \$sth = \$self->dbh->prepare_cached(\$sql);
336             \$sth->execute($insert_values);
337             $handle_autoincrement
338             return \$self;
339             }
340              
341             sub truncate {
342             my \$class = shift;
343             croak '->truncate must be called as a class method' if ref \$class;
344             my \$sql = 'DELETE FROM $tbl_name';
345             my \$sth = dbh->prepare_cached(\$sql);
346             \$sth->execute;
347             }
348              
349             END_CODE
350              
351 47 100       139 if (@key) {
352 18         1049 my $key_criteria = join ' AND ', map { "$_ = ?" } @key;
  21         73  
353 18         45 my $key_values = join ', ', map { "\$self->{$_}" } @key;
  21         341  
354 18         52 my $update_fields = join ', ', map { "$_ = ?" } @$field_list;
  31         82  
355              
356 15         109 $code .= <<"END_CODE";
357             sub update {
358             my \$self = shift;
359             my \$sql = 'UPDATE $tbl_name SET $update_fields WHERE $key_criteria';
360             my \$sth = \$self->dbh->prepare_cached(\$sql);
361             my \$changed = \$sth->execute($insert_values, $key_values);
362             \$self->insert unless \$changed > 0;
363              
364             return \$self;
365             }
366              
367             sub delete {
368             my \$self = shift;
369             my \$sql = 'DELETE FROM $tbl_name';
370             if (ref \$self) {
371             \$sql .= ' WHERE $key_criteria';
372             \@_ = ( $key_values );
373             } else {
374             return unless \@_;
375             \$sql .= ' ' . shift;
376             }
377             my \$sth = \$self->dbh->prepare_cached(\$sql);
378             \$sth->execute(\@_);
379             }
380             END_CODE
381             } else { # no primary key
382 31         102 $code .= <<"END_CODE";
383             sub delete {
384             my \$class = shift;
385             croak '->delete may not be called as an instance method for an unkeyed table'
386             if ref \$class;
387             return unless \@_;
388             my \$sql = 'DELETE FROM $tbl_name ' . shift;
389             my \$sth = \$class->dbh->prepare_cached(\$sql);
390             \$sth->execute(\@_);
391             }
392             END_CODE
393             }
394              
395 46 100       427 unless ($pkg_name->can('new')) {
396 45         93 $code .= '
397             sub new { my $class = shift; $class->_ormlette_new(@_); }
398             ';
399             }
400              
401 46 100       131 if (@key) {
402 15 50       125 my $destroy_name =
403             $pkg_name->can('DESTROY') ? '_ormlette_DESTROY' : 'DESTROY';
404 15         43 $code .= "
405             sub mark_dirty { \$_[0]->{_dirty} = 1 }
406             sub mark_clean { \$_[0]->{_dirty} = 0 }
407             sub dirty { return \$_[0]->{_dirty} }
408              
409             sub $destroy_name { \$_[0]->update if \$_[0]->{_dirty} }"
410             }
411              
412 46         405 return $code;
413             }
414              
415             1;
416              
417             =pod
418              
419             =head1 NAME
420              
421             Ormlette - Light and fluffy object persistence
422              
423             =head1 VERSION
424              
425             version 0.005
426              
427             =head1 SYNOPSIS
428              
429             my $dbh = DBI->connect(...);
430             Ormlette->init($dbh, tables => [ 'my_table' ], namespace => Test);
431              
432             my $obj = Test::MyTable->create(foo => 1, bar => 3);
433             print Test::MyTable->load(foo => 1)->bar; # 3
434              
435             =head1 DESCRIPTION
436              
437             Ormlette is a simple object persistence mechanism which is specifically
438             designed to avoid imposing any requirements on how your code is organized or
439             what base classes you use.
440              
441             Rather than requiring your classes to inherit from it, Ormlette is initialized
442             by passing an open database handle to C<< Ormlette->init >>, at which point
443             Ormlette will identify the tables in that database and, for each one, derive a
444             package name by camel-casing the table name using C<_> in the table name as a
445             word break (e.g., C becomes C) and prepending the current
446             package's name. It will then inject its methods (see below) into the resulting
447             package. It will also create accessors corresponding to each of the table's
448             fields unless these accessors already exist.
449              
450             Note that, if you want to define your own methods within a package which will
451             have methods injected by Ormlette, you should load the corresponding module
452             (with C or C) before doing any Ormlette initializations affecting
453             that package so that Ormlette will know what methods you've defined yourself
454             and avoid interfering with them.
455              
456             =head1 Ormlette Core Methods
457              
458             =head2 init ($dbh, %params)
459              
460             Attaches Ormlette methods to classes corresponding to tables in the database
461             connected to $dbh. Recognized parameters:
462              
463             =over 4
464              
465             =head3 debug
466              
467             The C option will cause additional debugging information to be printed
468             to STDERR as Ormlette does its initialization. At this point, this consists
469             solely of the generated source code for each package affected by Ormlette.
470              
471             =head3 isa
472              
473             Specifies that all Ormlette-generated classes should be made subclasses of
474             the C package. This is done by directly setting C<@ISA>, not via C
475             base> or C, so you are responsible for ensuring that the
476             specified class is available to use as a parent.
477              
478             If Ormlette is adding to a class which already exists and already has a
479             parent in C<@ISA>, the existing parent will be untouched and the C option
480             will have no effect on that class.
481              
482             =head3 ignore_root
483              
484             Ormlette will normally inject a C method into the base namespace of its
485             generated code, providing access to the source database. This is not always
486             desirable. In such cases, setting C will prevent Ormlette from
487             making any modifications to that package.
488              
489             =head3 ignore_tables
490              
491             Ormlette will normally generate classes corresponding to all tables found
492             in the database. If there are tables which should be skipped over, a
493             reference to an array of table names to skip can be passed in the
494             C parameter.
495              
496             If you prefer to list the tables to include rather than the tables to
497             exclude, use C. There should never be a reason to specify both
498             C and C, but, if this is done, C will take
499             precedence and C will be silently ignored.
500              
501             =head3 namespace
502              
503             By default, Ormlette will use the name of the package which calls C as
504             the base namespace for its generated code. If you want the code to be placed
505             into a different namespace, use the C parameter to override this
506             default.
507              
508             =head3 readonly
509              
510             If C is set to a true value, no constructors or database-altering
511             methods will be created and generated accessors will be read-only.
512              
513             =head3 tables
514              
515             If you only require Ormlette code to be generated for some of the tables in
516             your database, providing a reference to an array of table names in the
517             C parameter will cause all other tables to be ignored.
518              
519             If you prefer to list the tables to exclude rather than those to include,
520             use C.
521              
522             =back
523              
524             =head2 dbh
525              
526             Returns the internal database handle used for database interaction. Can be
527             called on the core Ormlette object, the root namespace of its generated code
528             (unless the C option is set when calling C), or any of the
529             persistent classes generated in that namespace.
530              
531             =head1 Root Namespace Methods
532              
533             If the C option is set when calling C, no methods will
534             be generated in the root namespace.
535              
536             =head2 dbh
537              
538             Returns the database handle attached by Ormlette to the root namespace. If
539             multiple Ormlette objects have been instantiated with the same C,
540             this will return the handle corresponding to the first Ormlette initialized
541             there.
542              
543             =head1 Table Class Methods
544              
545             In addition to the methods listed below, accessors will be generated for each
546             field found in the table unless an accessor for that field already exists,
547             providing the convenience of not having to create all the accessors yourself
548             while also allowing for custom accessors to be used where needed. Generated
549             accessors will be read-only if C is set or writable using the
550             C<< $obj->attr('new value') >> convention otherwise.
551              
552             Note that the generated accessors are extremely simple and make no attempt at
553             performing any form of data validation, so you may wish to use another fine
554             CPAN module to generate accessors before initializing Ormlette.
555              
556             =head2 create
557              
558             Constructs an object by calling C, then uses C to immediately
559             store it to the database.
560              
561             This method will not be generated if C is set.
562              
563             =head2 dbh
564              
565             Returns the database handle used by Ormlette operations on this class.
566              
567             =head2 delete
568              
569             =head2 delete('WHERE name = ?', 'John Doe')
570              
571             As a class method, deletes all objects matching the criteria specified in the
572             parameters. In an attempt to avoid data loss from accidentally calling
573             C as a class method when intending to use it as an instance method,
574             nothing will be done if no criteria are provided.
575              
576             As an instance method, deletes the object from the database. In this case, any
577             parameters will be ignored. The in-memory object is unaffected and remains
578             available for further use, including re-saving it to the database.
579              
580             This method will not be generated if C is set. The instance method
581             variant will only be generated for tables which have a primary key.
582              
583             =head2 dirty
584              
585             Read-only flag indicating whether an object is "dirty" (i.e., has unsaved
586             changes). This flag is not maintained automatically; code using the object
587             must use its C method to set the flag and C to clear
588             it.
589              
590             If an object is destroyed while dirty, a C handler will automatically
591             call its C method to write changes to the database. If the class
592             already has a C handler prior to Ormlette initialization, this check
593             is instead placed into an C<_ormlette_DESTROY> method, which the other
594             C should call if autoupdate functionality is desired.
595              
596             The C attribute, C and C methods, and C
597             handler will not be generated if C is set or for tables which do
598             not have a primary key.
599              
600             =head2 insert
601              
602             Inserts the object into the database as a new record. This method will fail if
603             the record cannot be inserted. If the table uses an autoincrement/serial
604             primary key and no value for that key is set in the object, the in-memory
605             object will be updated with the id assigned by the database.
606              
607             This method will not be generated if C is set.
608              
609             =head2 iterate(sub { print $_->id })
610              
611             =head2 iterate(sub { print $_->name }, 'WHERE age > ?', 18)
612              
613             Takes a sub reference as the first parameter and passes each object returned by
614             the subsequent query to the referenced sub in C<$_> for processing. The
615             primary difference between this method and C
616             loads one record into memory at a time, while C
617             once, which may require unacceptable amounts of memory when dealing with larger
618             data sets.
619              
620             =head2 mark_clean
621              
622             Clears an object's C flag.
623              
624             =head2 mark_dirty
625              
626             Sets an object's C flag.
627              
628             =head2 new
629              
630             Basic constructor which accepts a hash of values and blesses them into the
631             class. If a ->new method has already been defined, it will not be replaced.
632             If you wish to retain the default constructor functionality within your
633             custom ->new method, you can call $class->_ormlette_new to do so.
634              
635             This method will not be generated if C is set.
636              
637             =head2 load(1)
638              
639             =head2 load(foo => 1, bar => 2)
640              
641             Retrieves a single object from the database based on the specified criteria.
642              
643             If the table has a single-field primary key, passing a single argument will
644             retrieve the record with that value as its primary key.
645              
646             Lookups on non-key fields or multiple-field primary keys can be performed by
647             passing a hash of field => value pairs. If more than one record matches the
648             given criteria, only one will be returned; which one is returned may or may
649             not be consistent from one call to the next.
650              
651             Returns undef if no matching record exists.
652              
653             =head2 select
654              
655             =head2 select('WHERE id = 42');
656              
657             =head2 select('WHERE id > ? ORDER BY name LIMIT 5', 3);
658              
659             Returns a reference to an array containing all objects matching the query
660             specified in the parameters, in the order returned by that query. If no
661             parameters are provided, returns objects for all records in the database's
662             natural sort order.
663              
664             As this method simply appends its parameters to "SELECT (fields) FROM (table)",
665             arbitrarily-complex queries can be built up in the parameters, including joins
666             and subqueries.
667              
668             =head2 table
669              
670             Returns the table name in which Ormlette stores this class's data.
671              
672             =head2 update
673              
674             Updates the object's existing database record. This method will implicitly
675             call C if the object does not already exist in the database.
676              
677             This method will not be generated if C is set or for tables which
678             do not have a primary key.
679              
680             =head1 TO DO
681              
682             =over 4
683              
684             =item *
685              
686             Verify functionality with DBD back-ends other than SQLite
687              
688             =item *
689              
690             Add support for multi-level package hierarchies
691              
692             =item *
693              
694             Allow all Ormlette functions to be overridden, not just C and accessors
695              
696             =item *
697              
698             Add transaction support
699              
700             =item *
701              
702             Cache loaded objects to prevent duplication
703              
704             =back
705              
706             =head1 CREDITS
707              
708             Although it is not intended as a drop-in replacement, Ormlette's API and
709             general coding style are heavily influenced by Adam Kennedy's L.
710              
711             =head1 AUTHOR
712              
713             Dave Sherohman
714              
715             =head1 COPYRIGHT AND LICENSE
716              
717             This software is copyright (c) 2012 by Dave Sherohman.
718              
719             This is free software; you can redistribute it and/or modify it under
720             the same terms as the Perl 5 programming language system itself.
721              
722             =cut
723              
724             __END__