line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Basset::Object::Persistent; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
#Basset::Object::Persistent Copyright and (c) 2000, 2002, 2003, 2004, 2005, 2006 James A Thomason III |
4
|
|
|
|
|
|
|
#Basset::Object::Persistent is distributed under the terms of the Perl Artistic License. |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
our $VERSION = '1.03'; |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=pod |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=head1 NAME |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
Basset::Object::Persistent - subclass of Basset::Object that allows objects to be easily stored into a relational database. |
13
|
|
|
|
|
|
|
Presently only supports MySQL, but that may change in the future. |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 AUTHOR |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
Jim Thomason, jim@jimandkoka.com |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=head1 SYNOPSIS |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
(no synopsis, this is an abstract super class that should never be instantiated directly, it should be subclassed for all |
22
|
|
|
|
|
|
|
persistent objects and used through them) |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=head1 DESCRIPTION |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
Basset::Object is the uber module in my Perl world. All objects should decend from Basset::Object. It handles defining attributes, |
27
|
|
|
|
|
|
|
error handling, construction, destruction, and generic initialization. It also talks to Basset::Object::Conf to allow conf file use. |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
But, some objects cannot simply be recreated constantly every time a script runs. Sometimes you need to store the data in an object |
30
|
|
|
|
|
|
|
between uses so that you can recreate an object in the same form the last time you left it. Storing user information, for instance. |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
Basset::Object::Persistent allows you to do that transparently and easily. Persistent objects need to define several pieces of additional |
33
|
|
|
|
|
|
|
information to allow them to commit to the database, including their table definitions. Once these items are defined, you'll have access |
34
|
|
|
|
|
|
|
to the load and commit methods to allow you to load and store the objects in a database. |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
It is assumed that an object is stored in the database in a primary table. The primary table |
37
|
|
|
|
|
|
|
contains a set of columns named the same as object attributes. The attributes are stored in those columns. |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
Some::Package->add_attr('foo'); |
40
|
|
|
|
|
|
|
my $obj = Some::Package->new(); |
41
|
|
|
|
|
|
|
$obj->foo('bar'); |
42
|
|
|
|
|
|
|
$obj->commit(); |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
in the database, the 'foo' column will be set to 'bar'. |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=cut |
47
|
|
|
|
|
|
|
|
48
|
2
|
|
|
2
|
|
40019
|
use Scalar::Util qw(weaken isweak); |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
189
|
|
49
|
|
|
|
|
|
|
|
50
|
2
|
|
|
2
|
|
1390
|
use Basset::Object; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
104
|
|
51
|
|
|
|
|
|
|
our @ISA = Basset::Object->pkg_for_type('object'); |
52
|
|
|
|
|
|
|
|
53
|
2
|
|
|
2
|
|
14
|
use strict; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
87
|
|
54
|
2
|
|
|
2
|
|
43
|
use warnings; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
3092
|
|
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=pod |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
=over |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
=item loaded |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
boolean flag 1/0. |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
This flag tells you whether or not the objects you are operating on has been loaded from a database or initially created |
67
|
|
|
|
|
|
|
at this time and not loaded. This flag is set internally, and you should only read it. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=cut |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=pod |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=begin btest(loaded) |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
76
|
|
|
|
|
|
|
$test->ok($o, "Got object"); |
77
|
|
|
|
|
|
|
$test->is(scalar(__PACKAGE__->loaded), undef, "could not call object method as class method"); |
78
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode, "BO-08", "proper error code"); |
79
|
|
|
|
|
|
|
$test->is(scalar($o->loaded), 0, 'loaded is 0'); |
80
|
|
|
|
|
|
|
$test->is($o->loaded('abc'), 'abc', 'set loaded to abc'); |
81
|
|
|
|
|
|
|
$test->is($o->loaded(), 'abc', 'read value of loaded - abc'); |
82
|
|
|
|
|
|
|
my $h = {}; |
83
|
|
|
|
|
|
|
$test->ok($h, 'got hashref'); |
84
|
|
|
|
|
|
|
$test->is($o->loaded($h), $h, 'set loaded to hashref'); |
85
|
|
|
|
|
|
|
$test->is($o->loaded(), $h, 'read value of loaded - hashref'); |
86
|
|
|
|
|
|
|
my $a = []; |
87
|
|
|
|
|
|
|
$test->ok($a, 'got arrayref'); |
88
|
|
|
|
|
|
|
$test->is($o->loaded($a), $a, 'set loaded to arrayref'); |
89
|
|
|
|
|
|
|
$test->is($o->loaded(), $a, 'read value of loaded - arrayref'); |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=end btest(loaded) |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=cut |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
__PACKAGE__->add_attr('loaded'); |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=pod |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=item loading |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
read only boolean flag 1/0. |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
This flag is usually used internally, it keeps track of whether or not the object is currently in the process of loading |
104
|
|
|
|
|
|
|
from the database. It will always be zero unless the object is loading. This flag is set internally, and you should only read it. |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=cut |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=pod |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=begin btest(loading) |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
113
|
|
|
|
|
|
|
$test->ok($o, "Got object"); |
114
|
|
|
|
|
|
|
$test->is(scalar(__PACKAGE__->loading), undef, "could not call object method as class method"); |
115
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode, "BO-08", "proper error code"); |
116
|
|
|
|
|
|
|
$test->is(scalar($o->loading), 0, 'loading is 0'); |
117
|
|
|
|
|
|
|
$test->is($o->loading('abc'), 'abc', 'set loading to abc'); |
118
|
|
|
|
|
|
|
$test->is($o->loading(), 'abc', 'read value of loading - abc'); |
119
|
|
|
|
|
|
|
my $h = {}; |
120
|
|
|
|
|
|
|
$test->ok($h, 'got hashref'); |
121
|
|
|
|
|
|
|
$test->is($o->loading($h), $h, 'set loading to hashref'); |
122
|
|
|
|
|
|
|
$test->is($o->loading(), $h, 'read value of loading - hashref'); |
123
|
|
|
|
|
|
|
my $a = []; |
124
|
|
|
|
|
|
|
$test->ok($a, 'got arrayref'); |
125
|
|
|
|
|
|
|
$test->is($o->loading($a), $a, 'set loading to arrayref'); |
126
|
|
|
|
|
|
|
$test->is($o->loading(), $a, 'read value of loading - arrayref'); |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=end btest(loading) |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
=cut |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
__PACKAGE__->add_attr('loading'); |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=item committing |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
read only boolean flag 1/0. |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
This flag is usually used internally, it keeps track of whether or not the object is currently in the process of committing |
140
|
|
|
|
|
|
|
to the database. It will always be zero unless the object is committing. This flag is set internally, and you should only read it. |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=cut |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=pod |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
=begin btest(committing) |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
149
|
|
|
|
|
|
|
$test->ok($o, "Got object"); |
150
|
|
|
|
|
|
|
$test->is(scalar(__PACKAGE__->committing), undef, "could not call object method as class method"); |
151
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode, "BO-08", "proper error code"); |
152
|
|
|
|
|
|
|
$test->is(scalar($o->committing), 0, 'committing is 0'); |
153
|
|
|
|
|
|
|
$test->is($o->committing('abc'), 'abc', 'set committing to abc'); |
154
|
|
|
|
|
|
|
$test->is($o->committing(), 'abc', 'read value of committing - abc'); |
155
|
|
|
|
|
|
|
my $h = {}; |
156
|
|
|
|
|
|
|
$test->ok($h, 'got hashref'); |
157
|
|
|
|
|
|
|
$test->is($o->committing($h), $h, 'set committing to hashref'); |
158
|
|
|
|
|
|
|
$test->is($o->committing(), $h, 'read value of committing - hashref'); |
159
|
|
|
|
|
|
|
my $a = []; |
160
|
|
|
|
|
|
|
$test->ok($a, 'got arrayref'); |
161
|
|
|
|
|
|
|
$test->is($o->committing($a), $a, 'set committing to arrayref'); |
162
|
|
|
|
|
|
|
$test->is($o->committing(), $a, 'read value of committing - arrayref'); |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=end btest(committing) |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=cut |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
__PACKAGE__->add_attr('committing'); |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=item committed |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
Flag, N/0. |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
This flag tells you whether this object has been committed during this instantiation. It will not keep track of whether an object has |
175
|
|
|
|
|
|
|
been committed before this instantiation. The value is either 0 (no commits during this instantiation) or N, where N is a positive integer |
176
|
|
|
|
|
|
|
number containing the number of times that this object has been committed during this instantiation. This flag is set internally, and |
177
|
|
|
|
|
|
|
you should only read it. |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
$object->commit(); |
180
|
|
|
|
|
|
|
if ($object->committed){ |
181
|
|
|
|
|
|
|
print "Yay, committed!"; |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
else { |
184
|
|
|
|
|
|
|
print "Could not commit : " . $object->errstring . "\n"; |
185
|
|
|
|
|
|
|
}; |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=cut |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=pod |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=begin btest(committed) |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
194
|
|
|
|
|
|
|
$test->ok($o, "Got object"); |
195
|
|
|
|
|
|
|
$test->is(scalar(__PACKAGE__->committed), undef, "could not call object method as class method"); |
196
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode, "BO-08", "proper error code"); |
197
|
|
|
|
|
|
|
$test->is(scalar($o->committed), 0, 'committed is 0'); |
198
|
|
|
|
|
|
|
$test->is($o->committed('abc'), 'abc', 'set committed to abc'); |
199
|
|
|
|
|
|
|
$test->is($o->committed(), 'abc', 'read value of committed - abc'); |
200
|
|
|
|
|
|
|
my $h = {}; |
201
|
|
|
|
|
|
|
$test->ok($h, 'got hashref'); |
202
|
|
|
|
|
|
|
$test->is($o->committed($h), $h, 'set committed to hashref'); |
203
|
|
|
|
|
|
|
$test->is($o->committed(), $h, 'read value of committed - hashref'); |
204
|
|
|
|
|
|
|
my $a = []; |
205
|
|
|
|
|
|
|
$test->ok($a, 'got arrayref'); |
206
|
|
|
|
|
|
|
$test->is($o->committed($a), $a, 'set committed to arrayref'); |
207
|
|
|
|
|
|
|
$test->is($o->committed(), $a, 'read value of committed - arrayref'); |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=end btest(committed) |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=cut |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
__PACKAGE__->add_attr('committed'); |
214
|
|
|
|
|
|
|
__PACKAGE__->add_attr('in_db'); |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
=item deleting |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
read only boolean flag 1/0. |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
This flag is usually used internally, it keeps track of whether or not the object is currently in the process of being deleted |
221
|
|
|
|
|
|
|
from the database. It will always be zero unless the object is deleting. This flag is set internally, and you should only read it. |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
=cut |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=pod |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=begin btest(deleting) |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
230
|
|
|
|
|
|
|
$test->ok($o, "Got object"); |
231
|
|
|
|
|
|
|
$test->is(scalar(__PACKAGE__->deleting), undef, "could not call object method as class method"); |
232
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode, "BO-08", "proper error code"); |
233
|
|
|
|
|
|
|
$test->is(scalar($o->deleting), 0, 'deleting is 0'); |
234
|
|
|
|
|
|
|
$test->is($o->deleting('abc'), 'abc', 'set deleting to abc'); |
235
|
|
|
|
|
|
|
$test->is($o->deleting(), 'abc', 'read value of deleting - abc'); |
236
|
|
|
|
|
|
|
my $h = {}; |
237
|
|
|
|
|
|
|
$test->ok($h, 'got hashref'); |
238
|
|
|
|
|
|
|
$test->is($o->deleting($h), $h, 'set deleting to hashref'); |
239
|
|
|
|
|
|
|
$test->is($o->deleting(), $h, 'read value of deleting - hashref'); |
240
|
|
|
|
|
|
|
my $a = []; |
241
|
|
|
|
|
|
|
$test->ok($a, 'got arrayref'); |
242
|
|
|
|
|
|
|
$test->is($o->deleting($a), $a, 'set deleting to arrayref'); |
243
|
|
|
|
|
|
|
$test->is($o->deleting(), $a, 'read value of deleting - arrayref'); |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
=end btest(deleting) |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
=cut |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
__PACKAGE__->add_attr('deleting'); |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
=pod |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
=item deleted |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
Boolean flag, 1/0. |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
When an object is deleted via the ->delete method, this flag is set to 1. Otherwise, it is 0. This is the only change that is made |
258
|
|
|
|
|
|
|
to an object when it is deleted, so this is the way to determine if your delete was successful. This flag is set internally, and |
259
|
|
|
|
|
|
|
you should only read it. |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
$object->delete(); |
262
|
|
|
|
|
|
|
if ($object->deleted){ |
263
|
|
|
|
|
|
|
print "Yay, deleted!"; |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
else { |
266
|
|
|
|
|
|
|
print "Could not delete : " . $object->errstring . "\n"; |
267
|
|
|
|
|
|
|
}; |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
=cut |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=pod |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
=begin btest(deleted) |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
276
|
|
|
|
|
|
|
$test->ok($o, "Got object"); |
277
|
|
|
|
|
|
|
$test->is(scalar(__PACKAGE__->deleted), undef, "could not call object method as class method"); |
278
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode, "BO-08", "proper error code"); |
279
|
|
|
|
|
|
|
$test->is(scalar($o->deleted), 0, 'deleted is 0'); |
280
|
|
|
|
|
|
|
$test->is($o->deleted('abc'), 'abc', 'set deleted to abc'); |
281
|
|
|
|
|
|
|
$test->is($o->deleted(), 'abc', 'read value of deleted - abc'); |
282
|
|
|
|
|
|
|
my $h = {}; |
283
|
|
|
|
|
|
|
$test->ok($h, 'got hashref'); |
284
|
|
|
|
|
|
|
$test->is($o->deleted($h), $h, 'set deleted to hashref'); |
285
|
|
|
|
|
|
|
$test->is($o->deleted(), $h, 'read value of deleted - hashref'); |
286
|
|
|
|
|
|
|
my $a = []; |
287
|
|
|
|
|
|
|
$test->ok($a, 'got arrayref'); |
288
|
|
|
|
|
|
|
$test->is($o->deleted($a), $a, 'set deleted to arrayref'); |
289
|
|
|
|
|
|
|
$test->is($o->deleted(), $a, 'read value of deleted - arrayref'); |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
=end btest(deleted) |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=cut |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
__PACKAGE__->add_attr('deleted'); |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
# tables is a class attribute that internally stores the tables associated with this object |
298
|
|
|
|
|
|
|
__PACKAGE__->add_trickle_class_attr('tables', []); |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=pod |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
=item arbitrary_selectables |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
This should be set in the conf file. This is a regular expression that specifies which queries arbitary_sql |
305
|
|
|
|
|
|
|
should expect to return data. A good value for MySQL is: (show|select|desc|set) |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
=cut |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
=pod |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
=begin btest(arbitrary_selectables) |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
=end btest(arbitrary_selectables) |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
=cut |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
__PACKAGE__->add_class_attr('arbitrary_selectables', '(show|select|desc|set)'); |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
=pod |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
=item force_insert |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
Boolean flag. 1/0. Trickles to subclasses. |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
Your objects may be transactional in nature such that you always want to keep a record of them |
326
|
|
|
|
|
|
|
no matter how often they've changed. In that case, you can specify the force_insert flag. |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
Care must be taken with this flag to ensure you never violate primary key constraints. Also, you |
329
|
|
|
|
|
|
|
may not use auto generated ids, for obvious reasons. |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=cut |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=pod |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
=begin btest(force_insert) |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=end btest(force_insert) |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
=cut |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
__PACKAGE__->add_trickle_class_attr('force_insert'); |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
#=pod |
344
|
|
|
|
|
|
|
# |
345
|
|
|
|
|
|
|
#=item iterator |
346
|
|
|
|
|
|
|
# |
347
|
|
|
|
|
|
|
#Internally manages the iterator used by load_next |
348
|
|
|
|
|
|
|
# |
349
|
|
|
|
|
|
|
#=cut |
350
|
|
|
|
|
|
|
# |
351
|
|
|
|
|
|
|
#=pod |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=begin btest(iterator) |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
356
|
|
|
|
|
|
|
$test->ok($o, "Got object"); |
357
|
|
|
|
|
|
|
$test->is(scalar($o->iterator), undef, 'iterator is undefined'); |
358
|
|
|
|
|
|
|
$test->is($o->iterator('abc'), 'abc', 'set iterator to abc'); |
359
|
|
|
|
|
|
|
$test->is($o->iterator(), 'abc', 'read value of iterator - abc'); |
360
|
|
|
|
|
|
|
my $h = {}; |
361
|
|
|
|
|
|
|
$test->ok($h, 'got hashref'); |
362
|
|
|
|
|
|
|
$test->is($o->iterator($h), $h, 'set iterator to hashref'); |
363
|
|
|
|
|
|
|
$test->is($o->iterator(), $h, 'read value of iterator - hashref'); |
364
|
|
|
|
|
|
|
my $a = []; |
365
|
|
|
|
|
|
|
$test->ok($a, 'got arrayref'); |
366
|
|
|
|
|
|
|
$test->is($o->iterator($a), $a, 'set iterator to arrayref'); |
367
|
|
|
|
|
|
|
$test->is($o->iterator(), $a, 'read value of iterator - arrayref'); |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
=end btest(iterator) |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
=cut |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
__PACKAGE__->add_trickle_class_attr('iterator'); |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
=pod |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
=back |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
=head1 METHODS |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
=over |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
=cut |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
sub add_primary_attr { |
386
|
0
|
|
|
0
|
0
|
0
|
my $pkg = shift; |
387
|
|
|
|
|
|
|
|
388
|
0
|
|
|
|
|
0
|
foreach my $record (@_) { |
389
|
0
|
0
|
|
|
|
0
|
my $attribute = ref $record eq 'ARRAY' ? $record->[0] : $record; |
390
|
|
|
|
|
|
|
|
391
|
0
|
|
|
|
|
0
|
$pkg->add_attr($record); |
392
|
|
|
|
|
|
|
|
393
|
0
|
0
|
|
|
|
0
|
$pkg->_primary_attributes->{$attribute}++ |
394
|
|
|
|
|
|
|
unless $pkg->is_attribute($attribute, 'non_primary'); |
395
|
|
|
|
|
|
|
} |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
} |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
sub add_non_primary_attr { |
400
|
0
|
|
|
0
|
0
|
0
|
my $pkg = shift; |
401
|
|
|
|
|
|
|
|
402
|
0
|
|
|
|
|
0
|
foreach my $record (@_) { |
403
|
0
|
0
|
|
|
|
0
|
my $attribute = ref $record eq 'ARRAY' ? $record->[0] : $record; |
404
|
|
|
|
|
|
|
|
405
|
0
|
|
|
|
|
0
|
$pkg->add_attr($record); |
406
|
|
|
|
|
|
|
|
407
|
0
|
0
|
|
|
|
0
|
$pkg->_non_primary_attributes->{$attribute}++ |
408
|
|
|
|
|
|
|
unless $pkg->is_attribute($attribute, 'primary'); |
409
|
|
|
|
|
|
|
} |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
} |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
sub attributes { |
414
|
0
|
|
|
0
|
1
|
0
|
my $class = shift->pkg; |
415
|
0
|
|
|
|
|
0
|
my $type = shift; |
416
|
|
|
|
|
|
|
|
417
|
0
|
|
|
|
|
0
|
my @attributes = (); |
418
|
|
|
|
|
|
|
|
419
|
0
|
0
|
0
|
|
|
0
|
if (defined $type && $type eq 'primary') { |
|
|
0
|
0
|
|
|
|
|
420
|
0
|
|
|
|
|
0
|
@attributes = keys %{$class->_primary_attributes}; |
|
0
|
|
|
|
|
0
|
|
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
elsif (defined $type && $type eq 'non_primary') { |
423
|
0
|
|
|
|
|
0
|
@attributes = keys %{$class->_non_primary_attributes}; |
|
0
|
|
|
|
|
0
|
|
424
|
|
|
|
|
|
|
} |
425
|
|
|
|
|
|
|
else { |
426
|
0
|
|
|
|
|
0
|
return $class->SUPER::attributes($type, @_); |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
|
429
|
0
|
|
|
|
|
0
|
return [sort grep {! /^_/} @attributes]; |
|
0
|
|
|
|
|
0
|
|
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
sub is_attribute { |
434
|
0
|
|
|
0
|
1
|
0
|
my $class = shift->pkg; |
435
|
0
|
|
|
|
|
0
|
my $attribute = shift; |
436
|
0
|
|
0
|
|
|
0
|
my $type = shift || 'instance'; |
437
|
|
|
|
|
|
|
|
438
|
0
|
0
|
|
|
|
0
|
if (defined $type) { |
439
|
0
|
0
|
|
|
|
0
|
if ($type eq 'primary') { |
|
|
0
|
|
|
|
|
|
440
|
0
|
|
|
|
|
0
|
return $class->_primary_attributes->{$attribute}; |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
elsif ($type eq 'non_primary') { |
443
|
0
|
|
|
|
|
0
|
return $class->_non_primary_attributes->{$attribute}; |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
|
447
|
0
|
|
|
|
|
0
|
return $class->SUPER::is_attribute($attribute, $type, @_); |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
__PACKAGE__->add_trickle_class_attr('_primary_attributes', {}); |
451
|
|
|
|
|
|
|
__PACKAGE__->add_trickle_class_attr('_non_primary_attributes', {}); |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
#=item init |
454
|
|
|
|
|
|
|
# |
455
|
|
|
|
|
|
|
#Nothing you need to worry about, Basset::Object::Persistent just intercepts init and makes sure that loaded and committed are specified first, |
456
|
|
|
|
|
|
|
#so that objects may rely upon them being set before the start of the initialization process. Then end up getting re-specified by the |
457
|
|
|
|
|
|
|
#super method, but that's of no consequence. |
458
|
|
|
|
|
|
|
# |
459
|
|
|
|
|
|
|
#=cut |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
#=pod |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
#=cut |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
sub init { |
466
|
14
|
|
|
14
|
1
|
28
|
my $self = shift; |
467
|
|
|
|
|
|
|
|
468
|
14
|
|
|
|
|
116
|
return $self->SUPER::init( |
469
|
|
|
|
|
|
|
'loading' => 0, |
470
|
|
|
|
|
|
|
'loaded' => 0, |
471
|
|
|
|
|
|
|
'committing' => 0, |
472
|
|
|
|
|
|
|
'committed' => 0, |
473
|
|
|
|
|
|
|
'deleting' => 0, |
474
|
|
|
|
|
|
|
'deleted' => 0, |
475
|
|
|
|
|
|
|
'in_db' => 0, |
476
|
|
|
|
|
|
|
'instantiated_relationships' => {}, |
477
|
|
|
|
|
|
|
'tied_to_parent' => 0, |
478
|
|
|
|
|
|
|
'should_be_committed' => 0, |
479
|
|
|
|
|
|
|
'should_be_deleted' => 0, |
480
|
|
|
|
|
|
|
'_deleted_relationships' => [], |
481
|
|
|
|
|
|
|
@_, |
482
|
|
|
|
|
|
|
); |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
}; |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
=pod |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
=begin btest(init) |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
491
|
|
|
|
|
|
|
$test->ok($o, "got object for init"); |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
$test->is($o->loading, 0, "loading is 0"); |
494
|
|
|
|
|
|
|
$test->is($o->loaded, 0, "loaded is 0"); |
495
|
|
|
|
|
|
|
$test->is($o->committing, 0, "committing is 0"); |
496
|
|
|
|
|
|
|
$test->is($o->committed, 0, "committed is 0"); |
497
|
|
|
|
|
|
|
$test->is($o->deleting, 0, "deleting is 0"); |
498
|
|
|
|
|
|
|
$test->is($o->deleted, 0, "deleted is 0"); |
499
|
|
|
|
|
|
|
$test->is(ref($o->instantiated_relationships), 'HASH', 'instantiated_relationships is hashref'); |
500
|
|
|
|
|
|
|
$test->is($o->tied_to_parent, 0, 'tied_to_parent is 0'); |
501
|
|
|
|
|
|
|
$test->is($o->should_be_committed, 0, 'should_be_committed is 0'); |
502
|
|
|
|
|
|
|
$test->is($o->should_be_deleted, 0, 'should_be_committed is 0'); |
503
|
|
|
|
|
|
|
$test->is(ref($o->_deleted_relationships), 'ARRAY', '_deleted_relationships is arrayref'); |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
=end btest(init) |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
=cut |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
=pod |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
=over |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
=item _keyed_accessor |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
This is an accessor designed to be specified with add_attr. For example, |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
Basset::User->add_attr(['user_group', '_keyed_accessor'], 'Basset::Group'); |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
That would specify that if you have a user object, you can only specify values to your user_group |
520
|
|
|
|
|
|
|
attribute that would successfully load into a Basset::Group object. |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
You can shut off the key validation if you're positive your value is valid |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
$user->user_group($group_id); #validates |
525
|
|
|
|
|
|
|
$user->user_group($group_id, 'valid'); #does not validate |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
Also note that the validation does not occur when the object is loading. It is assumed that if the key made it |
528
|
|
|
|
|
|
|
into the database, it's valid. |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=cut |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
=pod |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
=begin btest(_keyed_accessor) |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
=end btest(_keyed_accessor) |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
=cut |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
sub _isa_keyed_accessor { |
541
|
0
|
|
|
0
|
|
0
|
my $pkg = shift; |
542
|
0
|
|
|
|
|
0
|
my $attr = shift; |
543
|
0
|
|
|
|
|
0
|
my $prop = shift; |
544
|
0
|
|
|
|
|
0
|
my $class = shift; |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
return sub { |
547
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
548
|
0
|
0
|
|
|
|
0
|
if (@_) { |
549
|
0
|
|
|
|
|
0
|
my $val = shift; |
550
|
0
|
|
0
|
|
|
0
|
my $valid = shift || 0; |
551
|
0
|
0
|
0
|
|
|
0
|
if (defined $val && ! $valid && ! $self->loading) { |
|
|
|
0
|
|
|
|
|
552
|
0
|
0
|
|
|
|
0
|
$self->load_pkg($class) or return; |
553
|
0
|
0
|
|
|
|
0
|
unless ($class->exists($val) ) { |
554
|
0
|
|
|
|
|
0
|
return $self->error("Cannot store value $val - object does not exist for $class", "BOP-48"); |
555
|
|
|
|
|
|
|
} |
556
|
|
|
|
|
|
|
} |
557
|
0
|
|
|
|
|
0
|
return $self->$prop($val); |
558
|
|
|
|
|
|
|
} |
559
|
|
|
|
|
|
|
else { |
560
|
0
|
|
|
|
|
0
|
return $self->$prop(); |
561
|
|
|
|
|
|
|
} |
562
|
|
|
|
|
|
|
} |
563
|
0
|
|
|
|
|
0
|
} |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
sub _isa_committing_accessor { |
566
|
0
|
|
|
0
|
|
0
|
my $pkg = shift; |
567
|
0
|
|
|
|
|
0
|
my $attr = shift; |
568
|
0
|
|
|
|
|
0
|
my $prop = shift; |
569
|
0
|
0
|
|
|
|
0
|
my $interceptor = shift or return $pkg->error("Cannot make committing accessor w/o interceptor", "XXX"); |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
return sub { |
572
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
573
|
0
|
0
|
|
|
|
0
|
if ($self->committing) { |
574
|
0
|
|
|
|
|
0
|
return $self->$interceptor($prop, @_); |
575
|
|
|
|
|
|
|
} |
576
|
|
|
|
|
|
|
else { |
577
|
0
|
|
|
|
|
0
|
return $self->$prop(@_); |
578
|
|
|
|
|
|
|
} |
579
|
|
|
|
|
|
|
} |
580
|
0
|
|
|
|
|
0
|
} |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
=pod |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
=item add_primarytable |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
add_primarytable is a class method that takes a hash as an argument, which is used as a constructor |
587
|
|
|
|
|
|
|
call for a Basset::DB::Table object (or whatever you've specified as your table type object) |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
__PACKAGE__->add_primarytable( |
590
|
|
|
|
|
|
|
'name' => 'transaction', |
591
|
|
|
|
|
|
|
'primary_column' => 'id', |
592
|
|
|
|
|
|
|
'autogenerated' => 1, |
593
|
|
|
|
|
|
|
'definition' => { |
594
|
|
|
|
|
|
|
'id' => 'SQL_INTEGER', |
595
|
|
|
|
|
|
|
'account' => 'SQL_INTEGER', |
596
|
|
|
|
|
|
|
'paidby' => 'SQL_INTEGER', |
597
|
|
|
|
|
|
|
'category' => 'SQL_INTEGER', |
598
|
|
|
|
|
|
|
'day' => 'SQL_DATE', |
599
|
|
|
|
|
|
|
'amount' => 'SQL_DECIMAL', |
600
|
|
|
|
|
|
|
'description' => 'SQL_VARCHAR', |
601
|
|
|
|
|
|
|
} |
602
|
|
|
|
|
|
|
); |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
See Basset::DB::Table for more information. This table is the primary table where the object's data is stored. |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
This method is a wrapper around add_tables with a single table ->factory call on the 'table' type, but it also |
607
|
|
|
|
|
|
|
explicitly wipes out the tables list before setting the primary table. |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
=cut |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
=pod |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
=begin btest(add_primarytable) |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
=end btest(add_primarytable) |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
=cut |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
sub add_primarytable { |
620
|
3
|
|
|
3
|
1
|
7
|
my $class = shift; |
621
|
|
|
|
|
|
|
|
622
|
3
|
|
|
|
|
6
|
my $table; |
623
|
|
|
|
|
|
|
|
624
|
3
|
|
|
|
|
6
|
my $create_attributes = 0; |
625
|
|
|
|
|
|
|
|
626
|
3
|
50
|
|
|
|
12
|
if (@_ == 1) { |
627
|
0
|
|
|
|
|
0
|
$table = $_[0]; |
628
|
|
|
|
|
|
|
} else { |
629
|
|
|
|
|
|
|
|
630
|
3
|
|
|
|
|
13
|
my %init = @_; |
631
|
|
|
|
|
|
|
|
632
|
3
|
50
|
|
|
|
26
|
$table = $class->factory('type' => 'table', @_) or return; |
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
} |
635
|
|
|
|
|
|
|
|
636
|
3
|
|
|
|
|
29
|
$class->tables([]); |
637
|
3
|
|
|
|
|
21
|
$class->add_tables($table); |
638
|
|
|
|
|
|
|
|
639
|
3
|
|
|
|
|
7
|
return $table; |
640
|
|
|
|
|
|
|
}; |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
sub auto_create_attributes { |
643
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
644
|
|
|
|
|
|
|
|
645
|
0
|
0
|
0
|
|
|
0
|
my $tables = shift || $class->tables |
646
|
|
|
|
|
|
|
or return $class->error("Cannot auto-create attributes w/o tables", "BOP-86"); |
647
|
|
|
|
|
|
|
|
648
|
0
|
|
|
|
|
0
|
foreach my $table (@$tables) { |
649
|
0
|
|
|
|
|
0
|
my @attributes = keys %{$table->definition}; |
|
0
|
|
|
|
|
0
|
|
650
|
0
|
|
|
|
|
0
|
foreach my $column (@attributes) { |
651
|
0
|
|
|
|
|
0
|
my $attribute = $table->alias_column($column); |
652
|
0
|
0
|
|
|
|
0
|
$class->add_attr($attribute) or return; |
653
|
|
|
|
|
|
|
}; |
654
|
|
|
|
|
|
|
}; |
655
|
|
|
|
|
|
|
|
656
|
0
|
|
|
|
|
0
|
return 1; |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
}; |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
sub add_tables { |
661
|
3
|
|
|
3
|
1
|
6
|
my $class = shift; |
662
|
|
|
|
|
|
|
|
663
|
3
|
50
|
|
|
|
21
|
return $class->error("Cannot add table w/o tables", "BOP-85") unless @_; |
664
|
|
|
|
|
|
|
|
665
|
3
|
|
|
|
|
4
|
my @tables = @{$class->tables}; |
|
3
|
|
|
|
|
13
|
|
666
|
3
|
|
|
|
|
7
|
my %existing_table = map {$_->name, 1} @tables; |
|
0
|
|
|
|
|
0
|
|
667
|
|
|
|
|
|
|
|
668
|
3
|
|
|
|
|
10
|
while (my $table = shift @_) { |
669
|
3
|
50
|
|
|
|
12
|
next if $existing_table{$table->name}; |
670
|
3
|
|
|
|
|
7
|
push @tables, $table; |
671
|
|
|
|
|
|
|
|
672
|
3
|
50
|
|
|
|
8
|
if ($table->create_attributes) { |
673
|
|
|
|
|
|
|
|
674
|
2
|
|
|
2
|
|
24
|
no strict 'refs'; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
4275
|
|
675
|
|
|
|
|
|
|
|
676
|
0
|
|
|
|
|
0
|
my @attributes_to_create = $table->attributes_to_create; |
677
|
0
|
|
|
|
|
0
|
foreach my $attribute (@attributes_to_create) { |
678
|
|
|
|
|
|
|
|
679
|
0
|
|
|
|
|
0
|
$class->add_attr($attribute); |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
} |
682
|
|
|
|
|
|
|
} #end if create_attributes |
683
|
|
|
|
|
|
|
|
684
|
|
|
|
|
|
|
} #end while tables |
685
|
|
|
|
|
|
|
|
686
|
3
|
|
|
|
|
17
|
$class->tables(\@tables); |
687
|
|
|
|
|
|
|
|
688
|
3
|
|
|
|
|
8
|
return 1; |
689
|
|
|
|
|
|
|
} |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
##### |
692
|
|
|
|
|
|
|
# |
693
|
|
|
|
|
|
|
# XXX THIS IS EXTREMELY TEMPORARY AND A PROTOTYPE |
694
|
|
|
|
|
|
|
# |
695
|
|
|
|
|
|
|
# If you're looking in here, you shouldn't be. For the record, I'm debating a major overhaul of |
696
|
|
|
|
|
|
|
# Basset's concept of "persistence" and abstracting it royally out the ass into Basset::Storage. |
697
|
|
|
|
|
|
|
# But it's a huge undertaking, and I haven't figured out quite what needs to be done, how to do it, |
698
|
|
|
|
|
|
|
# or if I want to. But enjoy pondering the magical little method you're spying on here. It may |
699
|
|
|
|
|
|
|
# come to naught. |
700
|
|
|
|
|
|
|
# |
701
|
|
|
|
|
|
|
##### |
702
|
|
|
|
|
|
|
|
703
|
|
|
|
|
|
|
sub add_storage { |
704
|
0
|
|
|
0
|
0
|
0
|
my $class = shift; |
705
|
|
|
|
|
|
|
|
706
|
0
|
0
|
|
|
|
0
|
return $class->error("Cannot add storage w/o storage", "XXX") unless @_; |
707
|
|
|
|
|
|
|
|
708
|
0
|
|
|
|
|
0
|
while (my $storage = shift @_) { |
709
|
|
|
|
|
|
|
|
710
|
0
|
0
|
|
|
|
0
|
my $table = $class->factory( |
711
|
|
|
|
|
|
|
'type' => 'table', |
712
|
|
|
|
|
|
|
'primary_column' => $class->attributes('primary'), |
713
|
|
|
|
|
|
|
'non_primary_columns' => $class->attributes('non_primary'), |
714
|
|
|
|
|
|
|
%$storage |
715
|
|
|
|
|
|
|
) or return; |
716
|
|
|
|
|
|
|
|
717
|
0
|
|
|
|
|
0
|
$class->add_tables($table); |
718
|
|
|
|
|
|
|
} |
719
|
|
|
|
|
|
|
|
720
|
0
|
|
|
|
|
0
|
return 1; |
721
|
|
|
|
|
|
|
} |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
# XXX END TEMPORARY HACK |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
=pod |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
=item add_tables |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
add_tables is a class method that takes a list of tables as its arguments, which are the tables |
730
|
|
|
|
|
|
|
associated with this object when it is stored to the database. |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
__PACKAGE__->add_primarytable( |
733
|
|
|
|
|
|
|
__PACKAGE__->factory( |
734
|
|
|
|
|
|
|
'type' => 'table', |
735
|
|
|
|
|
|
|
'name' => 'transaction', |
736
|
|
|
|
|
|
|
'primary_column' => 'id', |
737
|
|
|
|
|
|
|
'autogenerated' => 1, |
738
|
|
|
|
|
|
|
'definition' => { |
739
|
|
|
|
|
|
|
'id' => 'SQL_INTEGER', |
740
|
|
|
|
|
|
|
'account' => 'SQL_INTEGER', |
741
|
|
|
|
|
|
|
'paidby' => 'SQL_INTEGER', |
742
|
|
|
|
|
|
|
'category' => 'SQL_INTEGER', |
743
|
|
|
|
|
|
|
'day' => 'SQL_DATE', |
744
|
|
|
|
|
|
|
'amount' => 'SQL_DECIMAL', |
745
|
|
|
|
|
|
|
'description' => 'SQL_VARCHAR', |
746
|
|
|
|
|
|
|
} |
747
|
|
|
|
|
|
|
) |
748
|
|
|
|
|
|
|
); |
749
|
|
|
|
|
|
|
|
750
|
|
|
|
|
|
|
See Basset::DB::Table for more information. |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
=cut |
753
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
=pod |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
=begin btest(add_tables) |
757
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
=end btest(add_tables) |
759
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
=cut |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
=pod |
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
=item primary_table |
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
Returns the first table associated with the given object. |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
=cut |
769
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
sub primary_table { |
771
|
36
|
|
|
36
|
1
|
108
|
return shift->tables->[0]; |
772
|
|
|
|
|
|
|
}; |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
=pod |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
=begin btest(primary_table) |
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
=end btest(primary_table) |
779
|
|
|
|
|
|
|
|
780
|
|
|
|
|
|
|
=cut |
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
=pod |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
=item relationships |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
This is a class attribute that internally stores the relationships used by this class. Specify new relationships with has_a |
787
|
|
|
|
|
|
|
or has_many. |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
=cut |
790
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
=begin btest(relationships) |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
=end btest(relationships) |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
=cut |
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
__PACKAGE__->add_trickle_class_attr('relationships', {}); |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
=pod |
800
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
=item should_be_deleted |
802
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
This is used to flag an object that has been auto-vivified and is tied to a parent object. You should rarely need |
804
|
|
|
|
|
|
|
to set, access, or worry about this flag directly. |
805
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
=cut |
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
=pod |
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
=begin btest(should_be_deleted) |
811
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
813
|
|
|
|
|
|
|
$test->ok($o, "Got object"); |
814
|
|
|
|
|
|
|
$test->is(scalar(__PACKAGE__->should_be_deleted), undef, "could not call object method as class method"); |
815
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode, "BO-08", "proper error code"); |
816
|
|
|
|
|
|
|
$test->is(scalar($o->should_be_deleted), 0, 'should_be_deleted is 0'); |
817
|
|
|
|
|
|
|
$test->is($o->should_be_deleted('abc'), 'abc', 'set should_be_deleted to abc'); |
818
|
|
|
|
|
|
|
$test->is($o->should_be_deleted(), 'abc', 'read value of should_be_deleted - abc'); |
819
|
|
|
|
|
|
|
my $h = {}; |
820
|
|
|
|
|
|
|
$test->ok($h, 'got hashref'); |
821
|
|
|
|
|
|
|
$test->is($o->should_be_deleted($h), $h, 'set should_be_deleted to hashref'); |
822
|
|
|
|
|
|
|
$test->is($o->should_be_deleted(), $h, 'read value of should_be_deleted - hashref'); |
823
|
|
|
|
|
|
|
my $a = []; |
824
|
|
|
|
|
|
|
$test->ok($a, 'got arrayref'); |
825
|
|
|
|
|
|
|
$test->is($o->should_be_deleted($a), $a, 'set should_be_deleted to arrayref'); |
826
|
|
|
|
|
|
|
$test->is($o->should_be_deleted(), $a, 'read value of should_be_deleted - arrayref'); |
827
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
=end btest(should_be_deleted) |
829
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
=cut |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
__PACKAGE__->add_attr('should_be_deleted'); |
833
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
=pod |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
=item should_be_committed |
837
|
|
|
|
|
|
|
|
838
|
|
|
|
|
|
|
This is used to flag an object that has been auto-vivified and is tied to a parent object. You should rarely need |
839
|
|
|
|
|
|
|
to set, access, or worry about this flag directly. |
840
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
=cut |
842
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
=pod |
844
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
=begin btest(should_be_committed) |
846
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
848
|
|
|
|
|
|
|
$test->ok($o, "Got object"); |
849
|
|
|
|
|
|
|
$test->is(scalar(__PACKAGE__->should_be_committed), undef, "could not call object method as class method"); |
850
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode, "BO-08", "proper error code"); |
851
|
|
|
|
|
|
|
$test->is(scalar($o->should_be_committed), 0, 'should_be_committed is zero'); |
852
|
|
|
|
|
|
|
$test->is($o->should_be_committed('abc'), 'abc', 'set should_be_committed to abc'); |
853
|
|
|
|
|
|
|
$test->is($o->should_be_committed(), 'abc', 'read value of should_be_committed - abc'); |
854
|
|
|
|
|
|
|
my $h = {}; |
855
|
|
|
|
|
|
|
$test->ok($h, 'got hashref'); |
856
|
|
|
|
|
|
|
$test->is($o->should_be_committed($h), $h, 'set should_be_committed to hashref'); |
857
|
|
|
|
|
|
|
$test->is($o->should_be_committed(), $h, 'read value of should_be_committed - hashref'); |
858
|
|
|
|
|
|
|
my $a = []; |
859
|
|
|
|
|
|
|
$test->ok($a, 'got arrayref'); |
860
|
|
|
|
|
|
|
$test->is($o->should_be_committed($a), $a, 'set should_be_committed to arrayref'); |
861
|
|
|
|
|
|
|
$test->is($o->should_be_committed(), $a, 'read value of should_be_committed - arrayref'); |
862
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
=end btest(should_be_committed) |
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
=cut |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
__PACKAGE__->add_attr('should_be_committed'); |
868
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
__PACKAGE__->add_attr('tied_to_parent'); |
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
=pod |
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
=item instantiated_relationships |
874
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
Internal hash that keeps track of which relationships for a given object have been instantiated. Check for instantiation via the |
876
|
|
|
|
|
|
|
is_instantiated method instead. |
877
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
=cut |
879
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
=pod |
881
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
=begin btest(instantiated_relationships) |
883
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
885
|
|
|
|
|
|
|
$test->ok($o, "Got object"); |
886
|
|
|
|
|
|
|
$test->is(scalar(__PACKAGE__->instantiated_relationships), undef, "could not call object method as class method"); |
887
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode, "BO-08", "proper error code"); |
888
|
|
|
|
|
|
|
$test->is(ref(scalar($o->instantiated_relationships)), 'HASH', 'instantiated_relationships is hashref'); |
889
|
|
|
|
|
|
|
$test->is($o->instantiated_relationships('abc'), 'abc', 'set instantiated_relationships to abc'); |
890
|
|
|
|
|
|
|
$test->is($o->instantiated_relationships(), 'abc', 'read value of instantiated_relationships - abc'); |
891
|
|
|
|
|
|
|
my $h = {}; |
892
|
|
|
|
|
|
|
$test->ok($h, 'got hashref'); |
893
|
|
|
|
|
|
|
$test->is($o->instantiated_relationships($h), $h, 'set instantiated_relationships to hashref'); |
894
|
|
|
|
|
|
|
$test->is($o->instantiated_relationships(), $h, 'read value of instantiated_relationships - hashref'); |
895
|
|
|
|
|
|
|
my $a = []; |
896
|
|
|
|
|
|
|
$test->ok($a, 'got arrayref'); |
897
|
|
|
|
|
|
|
$test->is($o->instantiated_relationships($a), $a, 'set instantiated_relationships to arrayref'); |
898
|
|
|
|
|
|
|
$test->is($o->instantiated_relationships(), $a, 'read value of instantiated_relationships - arrayref'); |
899
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
=end btest(instantiated_relationships) |
901
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
=cut |
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
__PACKAGE__->add_attr('instantiated_relationships'); |
905
|
|
|
|
|
|
|
|
906
|
|
|
|
|
|
|
=pod |
907
|
|
|
|
|
|
|
|
908
|
|
|
|
|
|
|
=item cental_load_cache |
909
|
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
if the use_central_load_cache parameter is set in the conf file, then objects will use a centralized loading cache, stored here. |
911
|
|
|
|
|
|
|
This is internal only. |
912
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
=cut |
914
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
=pod |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
=begin btest(cental_load_cache) |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
=end btest(cental_load_cache) |
920
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
=cut |
922
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
__PACKAGE__->add_class_attr('central_load_cache', {}); |
924
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
=pod |
926
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
=item _deleted_relationships |
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
Internal method. Keeps track of instantiated associated objects that were subsequently deleted. No looky, no touchy. |
930
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
=cut |
932
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
=pod |
934
|
|
|
|
|
|
|
|
935
|
|
|
|
|
|
|
=begin btest(_deleted_relationships) |
936
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
938
|
|
|
|
|
|
|
$test->ok($o, "Got object"); |
939
|
|
|
|
|
|
|
$test->is(scalar(__PACKAGE__->_deleted_relationships), undef, "could not call object method as class method"); |
940
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode, "BO-08", "proper error code"); |
941
|
|
|
|
|
|
|
$test->is(ref(scalar($o->_deleted_relationships)), 'ARRAY', '_deleted_relationships is arrayref'); |
942
|
|
|
|
|
|
|
$test->is($o->_deleted_relationships('abc'), 'abc', 'set _deleted_relationships to abc'); |
943
|
|
|
|
|
|
|
$test->is($o->_deleted_relationships(), 'abc', 'read value of _deleted_relationships - abc'); |
944
|
|
|
|
|
|
|
my $h = {}; |
945
|
|
|
|
|
|
|
$test->ok($h, 'got hashref'); |
946
|
|
|
|
|
|
|
$test->is($o->_deleted_relationships($h), $h, 'set _deleted_relationships to hashref'); |
947
|
|
|
|
|
|
|
$test->is($o->_deleted_relationships(), $h, 'read value of _deleted_relationships - hashref'); |
948
|
|
|
|
|
|
|
my $a = []; |
949
|
|
|
|
|
|
|
$test->ok($a, 'got arrayref'); |
950
|
|
|
|
|
|
|
$test->is($o->_deleted_relationships($a), $a, 'set _deleted_relationships to arrayref'); |
951
|
|
|
|
|
|
|
$test->is($o->_deleted_relationships(), $a, 'read value of _deleted_relationships - arrayref'); |
952
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
=end btest(_deleted_relationships) |
954
|
|
|
|
|
|
|
|
955
|
|
|
|
|
|
|
=cut |
956
|
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
__PACKAGE__->add_attr('_deleted_relationships'); |
958
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
=pod |
960
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
=item is_instantiated |
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
Boolean operator. Given an attribute, returns true if it is an associated attribute and has been instantiated, false if it has not been. |
964
|
|
|
|
|
|
|
|
965
|
|
|
|
|
|
|
=cut |
966
|
|
|
|
|
|
|
|
967
|
|
|
|
|
|
|
=pod |
968
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
=begin btest(is_instantiated) |
970
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
=end btest(is_instantiated) |
972
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
=cut |
974
|
|
|
|
|
|
|
|
975
|
|
|
|
|
|
|
sub is_instantiated { |
976
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
977
|
0
|
0
|
|
|
|
0
|
my $prop = shift or return $self->error("Cannot determine if is instantiated w/o prop", "BOP-71"); |
978
|
|
|
|
|
|
|
|
979
|
0
|
|
|
|
|
0
|
$prop = $self->deprivatize($prop); |
980
|
|
|
|
|
|
|
|
981
|
0
|
|
0
|
|
|
0
|
return $self->instantiated_relationships->{$prop} || 0; |
982
|
|
|
|
|
|
|
|
983
|
|
|
|
|
|
|
|
984
|
0
|
|
|
|
|
0
|
my $val = $self->$prop(); |
985
|
|
|
|
|
|
|
|
986
|
0
|
0
|
|
|
|
0
|
if (ref $val eq 'HASH') { |
|
|
0
|
|
|
|
|
|
987
|
0
|
|
|
|
|
0
|
return keys %$val; |
988
|
|
|
|
|
|
|
} elsif (ref $val eq 'ARRAY') { |
989
|
0
|
|
|
|
|
0
|
return @$val; |
990
|
|
|
|
|
|
|
} else { |
991
|
0
|
|
|
|
|
0
|
return ref $val; |
992
|
|
|
|
|
|
|
}; |
993
|
|
|
|
|
|
|
} |
994
|
|
|
|
|
|
|
|
995
|
|
|
|
|
|
|
=pod |
996
|
|
|
|
|
|
|
|
997
|
|
|
|
|
|
|
=item instantiate |
998
|
|
|
|
|
|
|
|
999
|
|
|
|
|
|
|
In the abstract, this is simple. Takes an attribute and an optional set of clauses, then instantiates that object. |
1000
|
|
|
|
|
|
|
|
1001
|
|
|
|
|
|
|
$obj->instantiate('foo'); |
1002
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
Now $obj->foo will contain whatever the instantiated list of information is, as defined when it was set up with the has_a or |
1004
|
|
|
|
|
|
|
has_many call. Alternatively, you can pass in a set of clauses to restrict the objects loaded. |
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
$obj->instantiate('foo', { |
1007
|
|
|
|
|
|
|
'where' => 'status_id = 1' |
1008
|
|
|
|
|
|
|
}); |
1009
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
Will instantiate the 'foo' attribute only with the objects that have a status_id of 1, anything else will simply not be loaded. A useful |
1011
|
|
|
|
|
|
|
clauses flag to pass is "temporary" - this will instantiate the relationship according to the clauses, but not populate the attribute. |
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
Note that you should only instantiate an attribute that is defined has having an instantiating parameter of 'manual' (as opposed |
1014
|
|
|
|
|
|
|
to 'lazy' ) and this is due to encapsulation reasons. |
1015
|
|
|
|
|
|
|
|
1016
|
|
|
|
|
|
|
Lazy objects are not instantiated until the attribute holding them is accessed, but then they are instantiated automatically. |
1017
|
|
|
|
|
|
|
|
1018
|
|
|
|
|
|
|
Manual objects are the ones that you want to worry about. In those cases, the instantiate method is basically a shortcut to insulate you |
1019
|
|
|
|
|
|
|
from needing to take extra steps and know the class involved. |
1020
|
|
|
|
|
|
|
|
1021
|
|
|
|
|
|
|
Say that a user has_many classes. You could do this: |
1022
|
|
|
|
|
|
|
|
1023
|
|
|
|
|
|
|
use Some::Class; |
1024
|
|
|
|
|
|
|
use Some::User; |
1025
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
my $user = Some::User->load(1); |
1027
|
|
|
|
|
|
|
my $classes = Some::Class->load_where('user_id' => $user->id); |
1028
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
or this |
1030
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
use Some::User; |
1032
|
|
|
|
|
|
|
|
1033
|
|
|
|
|
|
|
my $user = Some::User->load(1); |
1034
|
|
|
|
|
|
|
my $classes = $user->instantiate('classes'); |
1035
|
|
|
|
|
|
|
|
1036
|
|
|
|
|
|
|
=cut |
1037
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
sub instantiate { |
1039
|
|
|
|
|
|
|
|
1040
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1041
|
0
|
0
|
|
|
|
0
|
my $prop = shift or return $self->error("Cannot instantiate w/o attribute", "BOP-72"); |
1042
|
0
|
|
0
|
|
|
0
|
my $clauses = shift || {}; |
1043
|
0
|
|
|
|
|
0
|
my @values = @_; |
1044
|
|
|
|
|
|
|
|
1045
|
0
|
0
|
0
|
|
|
0
|
if ($self->is_instantiated($prop) && ! $clauses->{'temporary'}) { |
1046
|
0
|
|
|
|
|
0
|
$self->notify("warnings", "object already instantiated"); |
1047
|
|
|
|
|
|
|
}; |
1048
|
|
|
|
|
|
|
|
1049
|
0
|
|
|
|
|
0
|
my $relationships = $self->relationships; |
1050
|
|
|
|
|
|
|
|
1051
|
0
|
0
|
|
|
|
0
|
my $relationship_data = $relationships->{$prop} |
1052
|
|
|
|
|
|
|
or return $self->error("Cannot instantiate $prop : not relationship", "BOP-73"); |
1053
|
|
|
|
|
|
|
|
1054
|
0
|
|
|
|
|
0
|
my $c = $relationship_data->{'clauses'}; |
1055
|
|
|
|
|
|
|
|
1056
|
0
|
|
|
|
|
0
|
$clauses = {%$c, %$clauses}; |
1057
|
|
|
|
|
|
|
|
1058
|
0
|
|
|
|
|
0
|
my $table = $relationship_data->{'table'}; |
1059
|
0
|
0
|
|
|
|
0
|
$table = $table->[0] if ref $table eq 'ARRAY'; |
1060
|
0
|
|
|
|
|
0
|
my $fclass = $relationship_data->{'class'}; |
1061
|
|
|
|
|
|
|
|
1062
|
0
|
0
|
|
|
|
0
|
$self->load_pkg($fclass) or return; |
1063
|
|
|
|
|
|
|
|
1064
|
0
|
0
|
|
|
|
0
|
my ($referencing_cols, $foreign_cols) = $self->relationship_columns($prop) or return; |
1065
|
|
|
|
|
|
|
|
1066
|
0
|
0
|
0
|
|
|
0
|
return $self->error("Cannot instantiate - parent and child tables do not reference each other", "BOP-91") |
1067
|
|
|
|
|
|
|
unless @$foreign_cols && @$referencing_cols; |
1068
|
|
|
|
|
|
|
|
1069
|
0
|
|
|
|
|
0
|
push @values, map {$self->$_()} $table->alias_column(@$referencing_cols); |
|
0
|
|
|
|
|
0
|
|
1070
|
|
|
|
|
|
|
|
1071
|
0
|
|
|
|
|
0
|
my $where = join(' AND ', map {"$_ = ?"} @$foreign_cols); |
|
0
|
|
|
|
|
0
|
|
1072
|
|
|
|
|
|
|
|
1073
|
0
|
0
|
|
|
|
0
|
if ($clauses->{'where'}) { |
1074
|
0
|
|
|
|
|
0
|
$clauses->{'where'} .= " AND ($where)"; |
1075
|
|
|
|
|
|
|
} else { |
1076
|
0
|
|
|
|
|
0
|
$clauses->{'where'} = $where; |
1077
|
|
|
|
|
|
|
} |
1078
|
|
|
|
|
|
|
|
1079
|
0
|
0
|
0
|
|
|
0
|
my $instantiated = $clauses->{'value'} || $relationship_data->{'class'}->load_all( |
1080
|
|
|
|
|
|
|
{ |
1081
|
|
|
|
|
|
|
'key' => $relationship_data->{'key'}, |
1082
|
|
|
|
|
|
|
'constructor' => { |
1083
|
|
|
|
|
|
|
'tied_to_parent' => $relationship_data->{'tied_to_parent'} |
1084
|
|
|
|
|
|
|
}, |
1085
|
|
|
|
|
|
|
%$clauses, |
1086
|
|
|
|
|
|
|
}, |
1087
|
|
|
|
|
|
|
@values |
1088
|
|
|
|
|
|
|
) or return $self->error($relationship_data->{'class'}->errvals); |
1089
|
|
|
|
|
|
|
|
1090
|
0
|
0
|
|
|
|
0
|
if ($relationship_data->{'singleton'}) { |
1091
|
0
|
|
|
|
|
0
|
$instantiated = $instantiated->[0]; |
1092
|
|
|
|
|
|
|
} |
1093
|
|
|
|
|
|
|
|
1094
|
0
|
0
|
|
|
|
0
|
if ($clauses->{'temporary'}) { |
1095
|
0
|
|
|
|
|
0
|
return $instantiated; |
1096
|
|
|
|
|
|
|
}; |
1097
|
|
|
|
|
|
|
|
1098
|
0
|
|
|
|
|
0
|
$self->$prop($instantiated); |
1099
|
|
|
|
|
|
|
|
1100
|
0
|
|
|
|
|
0
|
$self->instantiated_relationships->{$prop}++; |
1101
|
|
|
|
|
|
|
|
1102
|
0
|
|
|
|
|
0
|
return $instantiated; |
1103
|
|
|
|
|
|
|
|
1104
|
|
|
|
|
|
|
}; |
1105
|
|
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
=pod |
1107
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
=begin btest(instantiate) |
1109
|
|
|
|
|
|
|
|
1110
|
|
|
|
|
|
|
=end btest(instantiate) |
1111
|
|
|
|
|
|
|
|
1112
|
|
|
|
|
|
|
=cut |
1113
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
|
1115
|
|
|
|
|
|
|
=pod |
1116
|
|
|
|
|
|
|
|
1117
|
|
|
|
|
|
|
=item uninstantiate |
1118
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
=cut |
1120
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
|
1122
|
|
|
|
|
|
|
sub uninstantiate { |
1123
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1124
|
0
|
0
|
|
|
|
0
|
my $prop = shift or return $self->error("Cannot uninstantiate w/o prop", "BOP-96"); |
1125
|
|
|
|
|
|
|
|
1126
|
0
|
|
|
|
|
0
|
$self->$prop(undef); |
1127
|
|
|
|
|
|
|
|
1128
|
0
|
|
|
|
|
0
|
delete $self->instantiated_relationships->{$prop}; |
1129
|
|
|
|
|
|
|
|
1130
|
0
|
|
|
|
|
0
|
return 1; |
1131
|
|
|
|
|
|
|
} |
1132
|
|
|
|
|
|
|
|
1133
|
|
|
|
|
|
|
=pod |
1134
|
|
|
|
|
|
|
|
1135
|
|
|
|
|
|
|
=begin btest(uninstantiate) |
1136
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
=end btest(uninstantiate) |
1138
|
|
|
|
|
|
|
|
1139
|
|
|
|
|
|
|
=cut |
1140
|
|
|
|
|
|
|
|
1141
|
|
|
|
|
|
|
|
1142
|
|
|
|
|
|
|
|
1143
|
|
|
|
|
|
|
=pod |
1144
|
|
|
|
|
|
|
|
1145
|
|
|
|
|
|
|
=item has_a |
1146
|
|
|
|
|
|
|
|
1147
|
|
|
|
|
|
|
has_a defines relationship between objects. "An object 'has_a' different object". The has_a method is simply a wrapper around |
1148
|
|
|
|
|
|
|
has_many, passing in a key of undef and setting the singleton flag to 1. |
1149
|
|
|
|
|
|
|
|
1150
|
|
|
|
|
|
|
=cut |
1151
|
|
|
|
|
|
|
|
1152
|
|
|
|
|
|
|
=pod |
1153
|
|
|
|
|
|
|
|
1154
|
|
|
|
|
|
|
=begin btest(has_a) |
1155
|
|
|
|
|
|
|
|
1156
|
|
|
|
|
|
|
=end btest(has_a) |
1157
|
|
|
|
|
|
|
|
1158
|
|
|
|
|
|
|
=cut |
1159
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
sub has_a { |
1161
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
1162
|
0
|
0
|
|
|
|
0
|
my $attribute = shift or return $class->error("Cannot have many w/o attribute", "BOP-75"); |
1163
|
0
|
0
|
|
|
|
0
|
my $fclass = shift or return $class->error("Cannot have many w/o class", "BOP-76"); |
1164
|
|
|
|
|
|
|
|
1165
|
0
|
|
0
|
|
|
0
|
my $init = shift || {}; |
1166
|
|
|
|
|
|
|
|
1167
|
0
|
|
|
|
|
0
|
return $class->has_many( |
1168
|
|
|
|
|
|
|
$attribute => $fclass, |
1169
|
|
|
|
|
|
|
{ |
1170
|
|
|
|
|
|
|
%$init, |
1171
|
|
|
|
|
|
|
'key' => undef, |
1172
|
|
|
|
|
|
|
'singleton' => 1, |
1173
|
|
|
|
|
|
|
} |
1174
|
|
|
|
|
|
|
); |
1175
|
|
|
|
|
|
|
}; |
1176
|
|
|
|
|
|
|
|
1177
|
|
|
|
|
|
|
=pod |
1178
|
|
|
|
|
|
|
|
1179
|
|
|
|
|
|
|
=item _instantiating_accessor |
1180
|
|
|
|
|
|
|
|
1181
|
|
|
|
|
|
|
If a relationship is defined in has_many with instantiating -> lazy, then the associated objects will be populated automagically, but not |
1182
|
|
|
|
|
|
|
until the attribute is accessed. _instantiating_accessor internally handles all of that. |
1183
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
=cut |
1185
|
|
|
|
|
|
|
|
1186
|
|
|
|
|
|
|
sub _isa_instantiating_accessor { |
1187
|
0
|
|
|
0
|
|
0
|
my $pkg = shift; |
1188
|
0
|
|
|
|
|
0
|
my $attr = shift; |
1189
|
0
|
|
|
|
|
0
|
my $prop = shift; |
1190
|
|
|
|
|
|
|
|
1191
|
|
|
|
|
|
|
return sub { |
1192
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1193
|
|
|
|
|
|
|
|
1194
|
|
|
|
|
|
|
#got me. Perl 5.6 seems to require I yank this out, since it's a tied hashref. |
1195
|
0
|
|
|
|
|
0
|
my $h = $self->relationships->{$attr}; |
1196
|
|
|
|
|
|
|
|
1197
|
|
|
|
|
|
|
#upon mutation, we'll consider that as good as an instantiation. |
1198
|
0
|
0
|
|
|
|
0
|
if (@_) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1199
|
0
|
|
|
|
|
0
|
$self->$prop(shift); |
1200
|
|
|
|
|
|
|
|
1201
|
0
|
|
|
|
|
0
|
$self->instantiated_relationships->{$attr}++; |
1202
|
|
|
|
|
|
|
|
1203
|
0
|
|
|
|
|
0
|
return $self->$prop(); |
1204
|
|
|
|
|
|
|
} |
1205
|
|
|
|
|
|
|
#otherwise, instantiate if we're a lazy load |
1206
|
|
|
|
|
|
|
elsif ($h->{'instantiating'} eq 'lazy') { |
1207
|
0
|
0
|
|
|
|
0
|
$self->instantiate($attr) unless $self->is_instantiated($attr); |
1208
|
0
|
|
|
|
|
0
|
return $self->$prop(); |
1209
|
|
|
|
|
|
|
} |
1210
|
|
|
|
|
|
|
#otherwise, if it's instantiated, we return it. |
1211
|
|
|
|
|
|
|
elsif ($self->is_instantiated($attr)) { |
1212
|
0
|
|
|
|
|
0
|
return $self->$prop(); |
1213
|
|
|
|
|
|
|
} |
1214
|
|
|
|
|
|
|
#finally, we can't do anything, so we bomb out |
1215
|
|
|
|
|
|
|
else { |
1216
|
0
|
|
|
|
|
0
|
return $self->error("Cannot access $attr : not instantiated", "BOP-93"); |
1217
|
|
|
|
|
|
|
} |
1218
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
|
1220
|
|
|
|
|
|
|
} |
1221
|
0
|
|
|
|
|
0
|
} |
1222
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
=pod |
1224
|
|
|
|
|
|
|
|
1225
|
|
|
|
|
|
|
=begin btest(_isa_instantiating_accessor) |
1226
|
|
|
|
|
|
|
|
1227
|
|
|
|
|
|
|
=end btest(_isa_instantiating_accessor) |
1228
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
=cut |
1230
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
|
1232
|
|
|
|
|
|
|
=pod |
1233
|
|
|
|
|
|
|
|
1234
|
|
|
|
|
|
|
=item has_many |
1235
|
|
|
|
|
|
|
|
1236
|
|
|
|
|
|
|
All right, now we finally get to define relationships. The has_many parameter needs two values, the attribute and its class. |
1237
|
|
|
|
|
|
|
|
1238
|
|
|
|
|
|
|
Some::Class->has_many( |
1239
|
|
|
|
|
|
|
'wibbles' => 'Some::Other::Class' |
1240
|
|
|
|
|
|
|
); |
1241
|
|
|
|
|
|
|
|
1242
|
|
|
|
|
|
|
That will create an accessor for 'wibbles' and associate it with "Some::Other::Class". You could then instantiate it from an object: |
1243
|
|
|
|
|
|
|
|
1244
|
|
|
|
|
|
|
$someObject->instantiate('wibbles'); |
1245
|
|
|
|
|
|
|
|
1246
|
|
|
|
|
|
|
And populate all of your wibbles data. |
1247
|
|
|
|
|
|
|
|
1248
|
|
|
|
|
|
|
has_many takes an optional (but recommended!) 3rd argument, the options hash. Several options are supported. |
1249
|
|
|
|
|
|
|
|
1250
|
|
|
|
|
|
|
=over 8 |
1251
|
|
|
|
|
|
|
|
1252
|
|
|
|
|
|
|
=item key |
1253
|
|
|
|
|
|
|
|
1254
|
|
|
|
|
|
|
If loading up multiple associated objects (a cat "has_many" paws), then they will by default appear in an arbitrarily ordered arrayref |
1255
|
|
|
|
|
|
|
containing all of the data. But, there are times when you want to load up all of the data and quickly associate objects associated with |
1256
|
|
|
|
|
|
|
particular attributes. In that case, pass in the key parameter. |
1257
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
Some::Class->has_many( |
1259
|
|
|
|
|
|
|
'wibbles' => 'Some::Other::Class', |
1260
|
|
|
|
|
|
|
{ |
1261
|
|
|
|
|
|
|
'key' => 'foo' |
1262
|
|
|
|
|
|
|
} |
1263
|
|
|
|
|
|
|
); |
1264
|
|
|
|
|
|
|
|
1265
|
|
|
|
|
|
|
Then your data will be populated into a hashref, with the associated objects' "foo" attributes serving as their keys. |
1266
|
|
|
|
|
|
|
|
1267
|
|
|
|
|
|
|
=item instantiating |
1268
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
This item should be one of 2 values - 'manual' or 'lazy' |
1270
|
|
|
|
|
|
|
|
1271
|
|
|
|
|
|
|
Some::Class->has_many( |
1272
|
|
|
|
|
|
|
'wibbles' => 'Some::Other::Class', |
1273
|
|
|
|
|
|
|
{ |
1274
|
|
|
|
|
|
|
'instantiating' => 'manual' |
1275
|
|
|
|
|
|
|
} |
1276
|
|
|
|
|
|
|
); |
1277
|
|
|
|
|
|
|
|
1278
|
|
|
|
|
|
|
=over 12 |
1279
|
|
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
=item lazy |
1281
|
|
|
|
|
|
|
|
1282
|
|
|
|
|
|
|
lazily instantiated objects will automatically come into being when the associated attribute of the owning object is accessed for |
1283
|
|
|
|
|
|
|
the first time. This is the default. |
1284
|
|
|
|
|
|
|
|
1285
|
|
|
|
|
|
|
=item manual |
1286
|
|
|
|
|
|
|
|
1287
|
|
|
|
|
|
|
manually instantiated objects will never automatically come into being. you will have to explicitly call 'instantiate' yourself. |
1288
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
=back |
1290
|
|
|
|
|
|
|
|
1291
|
|
|
|
|
|
|
=item singleton |
1292
|
|
|
|
|
|
|
|
1293
|
|
|
|
|
|
|
If the singleton flag is set, then it is known that this attribute is associated with a single other object, and consequently will |
1294
|
|
|
|
|
|
|
just hold a reference to that object itself (not in an arrayref or hashref) |
1295
|
|
|
|
|
|
|
|
1296
|
|
|
|
|
|
|
Some::Class->has_many( |
1297
|
|
|
|
|
|
|
'wibbles' => 'Some::Other::Class', |
1298
|
|
|
|
|
|
|
{ |
1299
|
|
|
|
|
|
|
'singleton' => 1 |
1300
|
|
|
|
|
|
|
} |
1301
|
|
|
|
|
|
|
); |
1302
|
|
|
|
|
|
|
|
1303
|
|
|
|
|
|
|
=item clauses |
1304
|
|
|
|
|
|
|
|
1305
|
|
|
|
|
|
|
the clauses hashref is the same sort of clauses hashref to be handed into the loader. in fact, it is handed into the loader when |
1306
|
|
|
|
|
|
|
the associated objects are instantiated. |
1307
|
|
|
|
|
|
|
|
1308
|
|
|
|
|
|
|
Some::Class->has_many( |
1309
|
|
|
|
|
|
|
'wibbles' => 'Some::Other::Class', |
1310
|
|
|
|
|
|
|
{ |
1311
|
|
|
|
|
|
|
'clauses' => { |
1312
|
|
|
|
|
|
|
'where' => 'status_id = 1' |
1313
|
|
|
|
|
|
|
} |
1314
|
|
|
|
|
|
|
} |
1315
|
|
|
|
|
|
|
); |
1316
|
|
|
|
|
|
|
|
1317
|
|
|
|
|
|
|
=item accessibility |
1318
|
|
|
|
|
|
|
|
1319
|
|
|
|
|
|
|
This governs encapsulation. Associating objects with other objects is good, but you don't always want the user of the class to know |
1320
|
|
|
|
|
|
|
that other objects are involved. You should set the accessibility flag to 'private' if the associated object will never be accessed outside |
1321
|
|
|
|
|
|
|
of the class that defines it. These classes should probably be inlined (or at least privatedly declared inside another package) |
1322
|
|
|
|
|
|
|
|
1323
|
|
|
|
|
|
|
Some::Class->has_many( |
1324
|
|
|
|
|
|
|
'wibbles' => 'Some::Other::Class', |
1325
|
|
|
|
|
|
|
{ |
1326
|
|
|
|
|
|
|
'accessibility' => 'private' |
1327
|
|
|
|
|
|
|
} |
1328
|
|
|
|
|
|
|
); |
1329
|
|
|
|
|
|
|
|
1330
|
|
|
|
|
|
|
Default value is 'public'; |
1331
|
|
|
|
|
|
|
|
1332
|
|
|
|
|
|
|
Making an associated object private shuts off its ability to commit or delete itself. its changes only go in when its parent object |
1333
|
|
|
|
|
|
|
is committed or deleted. |
1334
|
|
|
|
|
|
|
|
1335
|
|
|
|
|
|
|
=item relationship_key |
1336
|
|
|
|
|
|
|
|
1337
|
|
|
|
|
|
|
Sometimes, you may have an object that references two objects in a different table. You may know |
1338
|
|
|
|
|
|
|
that every Car has a primary_driver and a secondary_driver. So you define your relationship: |
1339
|
|
|
|
|
|
|
|
1340
|
|
|
|
|
|
|
Car->primary_table->references( |
1341
|
|
|
|
|
|
|
{ |
1342
|
|
|
|
|
|
|
'primary_driver' => 'driver.id', |
1343
|
|
|
|
|
|
|
'secondary_driver' => 'driver.id' |
1344
|
|
|
|
|
|
|
} |
1345
|
|
|
|
|
|
|
); |
1346
|
|
|
|
|
|
|
|
1347
|
|
|
|
|
|
|
But you wouldn't be able to establith relationships for those items, since instantiate would |
1348
|
|
|
|
|
|
|
try to load an object using both of those values. |
1349
|
|
|
|
|
|
|
|
1350
|
|
|
|
|
|
|
Car->has_a( |
1351
|
|
|
|
|
|
|
'primary_driver' => 'Driver' |
1352
|
|
|
|
|
|
|
); |
1353
|
|
|
|
|
|
|
|
1354
|
|
|
|
|
|
|
Would try to load where driver.id = car.primary_driver_id and driver.id = car.secondary_driver_id. |
1355
|
|
|
|
|
|
|
So it would only work in the edge case when they're the same driver, which is not your intent. |
1356
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
The solution is to explicitly define which key you'd like to join on. |
1358
|
|
|
|
|
|
|
|
1359
|
|
|
|
|
|
|
Car->has_a( |
1360
|
|
|
|
|
|
|
'primary_driver' => 'Driver', |
1361
|
|
|
|
|
|
|
{ |
1362
|
|
|
|
|
|
|
'relationship_key' => 'primary_driver' |
1363
|
|
|
|
|
|
|
} |
1364
|
|
|
|
|
|
|
); |
1365
|
|
|
|
|
|
|
|
1366
|
|
|
|
|
|
|
Car->has_a( |
1367
|
|
|
|
|
|
|
'secondary_driver' => 'Driver', |
1368
|
|
|
|
|
|
|
{ |
1369
|
|
|
|
|
|
|
'relationship_key' => 'secondary_driver' |
1370
|
|
|
|
|
|
|
} |
1371
|
|
|
|
|
|
|
); |
1372
|
|
|
|
|
|
|
|
1373
|
|
|
|
|
|
|
=item transform |
1374
|
|
|
|
|
|
|
|
1375
|
|
|
|
|
|
|
See the 'transform' flag in the load_all method for info. |
1376
|
|
|
|
|
|
|
|
1377
|
|
|
|
|
|
|
=item foreign_has_a |
1378
|
|
|
|
|
|
|
|
1379
|
|
|
|
|
|
|
If you have a has_many relationship, then presumably your foreign class has a has_a relationship |
1380
|
|
|
|
|
|
|
with you. You can declare that relationship here. This has two advantages. |
1381
|
|
|
|
|
|
|
|
1382
|
|
|
|
|
|
|
1) It allows you to autmatically populate the foreign object's has_a property with yourself |
1383
|
|
|
|
|
|
|
upon setting the has_many. |
1384
|
|
|
|
|
|
|
|
1385
|
|
|
|
|
|
|
2) If the foreign class references you with multiple columns (say, obj_id_1 and obj_id_2), then |
1386
|
|
|
|
|
|
|
the foreign has_a has defined the relationship key to use. Specifying the foreign_has_a here |
1387
|
|
|
|
|
|
|
uses those same relationship keys. |
1388
|
|
|
|
|
|
|
|
1389
|
|
|
|
|
|
|
=back |
1390
|
|
|
|
|
|
|
|
1391
|
|
|
|
|
|
|
=cut |
1392
|
|
|
|
|
|
|
|
1393
|
|
|
|
|
|
|
=pod |
1394
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
=begin btest(has_many) |
1396
|
|
|
|
|
|
|
|
1397
|
|
|
|
|
|
|
=end btest(has_many) |
1398
|
|
|
|
|
|
|
|
1399
|
|
|
|
|
|
|
=cut |
1400
|
|
|
|
|
|
|
|
1401
|
|
|
|
|
|
|
__PACKAGE__->add_class_attr('bridge_classes', {}); |
1402
|
|
|
|
|
|
|
|
1403
|
|
|
|
|
|
|
sub has_many { |
1404
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
1405
|
|
|
|
|
|
|
|
1406
|
0
|
0
|
|
|
|
0
|
my $attribute = shift or return $class->error("Cannot have many w/o attribute", "BOP-77"); |
1407
|
0
|
0
|
|
|
|
0
|
my $fclass = shift or return $class->error("Cannot have many w/o class", "BOP-78"); |
1408
|
|
|
|
|
|
|
|
1409
|
0
|
|
0
|
|
|
0
|
my $init = shift || {}; |
1410
|
|
|
|
|
|
|
|
1411
|
0
|
|
0
|
|
|
0
|
my $table = $init->{'table'} || $class->primary_table; |
1412
|
|
|
|
|
|
|
|
1413
|
0
|
0
|
|
|
|
0
|
if (ref $fclass eq 'ARRAY') { |
1414
|
0
|
|
|
|
|
0
|
my $bridgekey = join(',', @$fclass); |
1415
|
|
|
|
|
|
|
|
1416
|
0
|
0
|
|
|
|
0
|
if (defined $class->bridge_classes->{$bridgekey}) { |
1417
|
0
|
|
|
|
|
0
|
$fclass = $class->bridge_classes->{$bridgekey}; |
1418
|
|
|
|
|
|
|
} |
1419
|
|
|
|
|
|
|
else { |
1420
|
|
|
|
|
|
|
|
1421
|
0
|
|
|
|
|
0
|
my $loadclass = pop @$fclass; |
1422
|
|
|
|
|
|
|
|
1423
|
0
|
0
|
|
|
|
0
|
$class->load_pkg($loadclass) or return; |
1424
|
|
|
|
|
|
|
|
1425
|
0
|
|
|
|
|
0
|
my $inclass = $loadclass->inline_class; |
1426
|
|
|
|
|
|
|
|
1427
|
0
|
|
|
|
|
0
|
foreach my $c (@$fclass) { |
1428
|
0
|
0
|
|
|
|
0
|
$class->load_pkg($c) or return; |
1429
|
0
|
|
|
|
|
0
|
$inclass->add_tables(@{$c->tables}); |
|
0
|
|
|
|
|
0
|
|
1430
|
|
|
|
|
|
|
}; |
1431
|
|
|
|
|
|
|
|
1432
|
0
|
|
|
|
|
0
|
$table = [$table, $fclass->[0]->primary_table]; |
1433
|
|
|
|
|
|
|
|
1434
|
0
|
|
|
|
|
0
|
$class->bridge_classes->{$bridgekey} = $fclass = $inclass; |
1435
|
|
|
|
|
|
|
} |
1436
|
|
|
|
|
|
|
} |
1437
|
|
|
|
|
|
|
|
1438
|
0
|
|
|
|
|
0
|
$class->add_attr([$attribute, '_isa_instantiating_accessor']); |
1439
|
|
|
|
|
|
|
|
1440
|
0
|
|
|
|
|
0
|
$class->relationships->{$attribute} = { |
1441
|
|
|
|
|
|
|
'class' => $fclass, |
1442
|
|
|
|
|
|
|
'table' => $table, |
1443
|
|
|
|
|
|
|
'singleton' => 0, |
1444
|
|
|
|
|
|
|
'instantiating' => 'lazy', |
1445
|
|
|
|
|
|
|
'clauses' => {}, |
1446
|
|
|
|
|
|
|
'accessibility' => 'public', |
1447
|
|
|
|
|
|
|
%$init, |
1448
|
|
|
|
|
|
|
}; |
1449
|
|
|
|
|
|
|
|
1450
|
0
|
0
|
|
|
|
0
|
unless ($init->{'singleton'}) { |
1451
|
0
|
0
|
|
|
|
0
|
$class->create_isa_to_method($attribute) or return; |
1452
|
|
|
|
|
|
|
}; |
1453
|
|
|
|
|
|
|
|
1454
|
0
|
|
|
|
|
0
|
return 1; |
1455
|
|
|
|
|
|
|
} |
1456
|
|
|
|
|
|
|
|
1457
|
|
|
|
|
|
|
=pod |
1458
|
|
|
|
|
|
|
|
1459
|
|
|
|
|
|
|
=item create_isa_to_method |
1460
|
|
|
|
|
|
|
|
1461
|
|
|
|
|
|
|
Mainly used internally when setting up has_many relationships. When you create a has_many relationship, |
1462
|
|
|
|
|
|
|
you automatically get an add_to* method. |
1463
|
|
|
|
|
|
|
|
1464
|
|
|
|
|
|
|
Some::Store->has_many( |
1465
|
|
|
|
|
|
|
'bagels' => 'Some::Bagel::Class' |
1466
|
|
|
|
|
|
|
); |
1467
|
|
|
|
|
|
|
|
1468
|
|
|
|
|
|
|
my $store->add_to_bagels( |
1469
|
|
|
|
|
|
|
'type' => 'chocolate chip', |
1470
|
|
|
|
|
|
|
'id' => '17738' |
1471
|
|
|
|
|
|
|
); |
1472
|
|
|
|
|
|
|
|
1473
|
|
|
|
|
|
|
Is equivalent to: |
1474
|
|
|
|
|
|
|
|
1475
|
|
|
|
|
|
|
my $bagel = Some::Bagel::Class->new( |
1476
|
|
|
|
|
|
|
'type' => 'chocolate chip', |
1477
|
|
|
|
|
|
|
'id' => '17738', |
1478
|
|
|
|
|
|
|
'store_id' => $store->id, |
1479
|
|
|
|
|
|
|
); |
1480
|
|
|
|
|
|
|
|
1481
|
|
|
|
|
|
|
=cut |
1482
|
|
|
|
|
|
|
|
1483
|
|
|
|
|
|
|
sub create_isa_to_method { |
1484
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1485
|
0
|
0
|
|
|
|
0
|
my $attribute = shift or return $self->error("Cannot create add_to_* method w/o attribute", "BOP-87"); |
1486
|
|
|
|
|
|
|
|
1487
|
0
|
0
|
|
|
|
0
|
my $relationship_data = $self->relationships->{$attribute} |
1488
|
|
|
|
|
|
|
or return $self->error("Cannot create_isa_to_method for $attribute : not relationship", "BOP-88"); |
1489
|
|
|
|
|
|
|
|
1490
|
2
|
|
|
2
|
|
18
|
no strict 'refs'; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
17015
|
|
1491
|
|
|
|
|
|
|
|
1492
|
0
|
|
|
|
|
0
|
my $class = $self->pkg; |
1493
|
|
|
|
|
|
|
|
1494
|
0
|
|
|
|
|
0
|
*{$class . "::add_to_$attribute"} = sub { |
1495
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
1496
|
|
|
|
|
|
|
|
1497
|
0
|
|
|
|
|
0
|
my $obj; |
1498
|
|
|
|
|
|
|
|
1499
|
0
|
0
|
|
|
|
0
|
if (@_ == 1) { |
1500
|
0
|
|
|
|
|
0
|
$obj = shift; |
1501
|
|
|
|
|
|
|
} else { |
1502
|
0
|
|
|
|
|
0
|
my %init = @_; |
1503
|
|
|
|
|
|
|
|
1504
|
0
|
|
|
|
|
0
|
my $table = $relationship_data->{'table'}; |
1505
|
0
|
0
|
|
|
|
0
|
$table = $table->[0] if ref $table eq 'ARRAY'; |
1506
|
|
|
|
|
|
|
|
1507
|
0
|
0
|
|
|
|
0
|
my ($referencing_cols, $foreign_cols) = $self->relationship_columns($attribute) or return; |
1508
|
|
|
|
|
|
|
|
1509
|
0
|
|
|
|
|
0
|
foreach my $col (@$referencing_cols) { |
1510
|
0
|
|
|
|
|
0
|
my $foreign = $table->nonqualified_name(shift @$foreign_cols); |
1511
|
0
|
|
|
|
|
0
|
my $attr = $table->alias_column($col); |
1512
|
0
|
|
|
|
|
0
|
$init{$foreign} = $self->$attr(); |
1513
|
|
|
|
|
|
|
} |
1514
|
|
|
|
|
|
|
|
1515
|
0
|
0
|
|
|
|
0
|
$obj = $relationship_data->{'class'}->new(%init) or |
1516
|
|
|
|
|
|
|
return $self->error($relationship_data->{'class'}->errvals); |
1517
|
|
|
|
|
|
|
} |
1518
|
|
|
|
|
|
|
|
1519
|
0
|
0
|
|
|
|
0
|
if ($relationship_data->{'accessibility'} eq 'private') { |
1520
|
0
|
|
|
|
|
0
|
$obj->tied_to_parent(1); |
1521
|
|
|
|
|
|
|
} |
1522
|
|
|
|
|
|
|
|
1523
|
0
|
0
|
|
|
|
0
|
if (my $key = $relationship_data->{'key'}) { |
1524
|
0
|
0
|
|
|
|
0
|
return $self->error("Cannot add new object, missing value for $key", "BOP-90") |
1525
|
|
|
|
|
|
|
unless defined $obj->$key(); |
1526
|
0
|
|
|
|
|
0
|
$self->$attribute()->{$obj->$key()} = $obj; |
1527
|
|
|
|
|
|
|
} else { |
1528
|
0
|
|
|
|
|
0
|
push @{$self->$attribute()}, $obj |
|
0
|
|
|
|
|
0
|
|
1529
|
|
|
|
|
|
|
} |
1530
|
|
|
|
|
|
|
|
1531
|
0
|
0
|
|
|
|
0
|
if (my $foreign_method = $relationship_data->{'foreign_has_a'}) { |
1532
|
0
|
0
|
|
|
|
0
|
$obj->$foreign_method($self) || return $self->error($obj->errvals); |
1533
|
|
|
|
|
|
|
} |
1534
|
|
|
|
|
|
|
|
1535
|
0
|
|
|
|
|
0
|
return $obj; |
1536
|
0
|
|
|
|
|
0
|
}; |
1537
|
|
|
|
|
|
|
|
1538
|
|
|
|
|
|
|
}; |
1539
|
|
|
|
|
|
|
|
1540
|
|
|
|
|
|
|
=pod |
1541
|
|
|
|
|
|
|
|
1542
|
|
|
|
|
|
|
=begin btest(create_isa_to_method) |
1543
|
|
|
|
|
|
|
|
1544
|
|
|
|
|
|
|
=end btest(create_isa_to_method) |
1545
|
|
|
|
|
|
|
|
1546
|
|
|
|
|
|
|
=cut |
1547
|
|
|
|
|
|
|
|
1548
|
|
|
|
|
|
|
|
1549
|
|
|
|
|
|
|
=pod |
1550
|
|
|
|
|
|
|
|
1551
|
|
|
|
|
|
|
=item commit_relationships |
1552
|
|
|
|
|
|
|
|
1553
|
|
|
|
|
|
|
Used internally to commit all associated objects for a given object, only used for private objects |
1554
|
|
|
|
|
|
|
|
1555
|
|
|
|
|
|
|
$obj->commit_relationships |
1556
|
|
|
|
|
|
|
|
1557
|
|
|
|
|
|
|
=cut |
1558
|
|
|
|
|
|
|
|
1559
|
|
|
|
|
|
|
=pod |
1560
|
|
|
|
|
|
|
|
1561
|
|
|
|
|
|
|
=begin btest(commit_relationships) |
1562
|
|
|
|
|
|
|
|
1563
|
|
|
|
|
|
|
=end btest(commit_relationships) |
1564
|
|
|
|
|
|
|
|
1565
|
|
|
|
|
|
|
=cut |
1566
|
|
|
|
|
|
|
|
1567
|
|
|
|
|
|
|
sub commit_relationships { |
1568
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1569
|
0
|
0
|
|
|
|
0
|
my $singletons = $_[0] eq 'singletons' ? 1 : 0; |
1570
|
|
|
|
|
|
|
|
1571
|
|
|
|
|
|
|
#$self->begin() or return; |
1572
|
|
|
|
|
|
|
|
1573
|
0
|
|
|
|
|
0
|
my $instantiated = $self->instantiated_relationships; |
1574
|
|
|
|
|
|
|
|
1575
|
0
|
0
|
|
|
|
0
|
return 1 unless keys %$instantiated; |
1576
|
|
|
|
|
|
|
|
1577
|
0
|
|
|
|
|
0
|
my $seen = {}; |
1578
|
|
|
|
|
|
|
|
1579
|
0
|
|
|
|
|
0
|
my $deleted_relationships = $self->_deleted_relationships; |
1580
|
|
|
|
|
|
|
|
1581
|
0
|
|
|
|
|
0
|
foreach my $deleted_obj (@$deleted_relationships) { |
1582
|
0
|
|
|
|
|
0
|
$deleted_obj->should_be_deleted(2); |
1583
|
0
|
0
|
|
|
|
0
|
$deleted_obj->delete or return $self->error($deleted_obj->errvals); |
1584
|
|
|
|
|
|
|
} |
1585
|
|
|
|
|
|
|
|
1586
|
0
|
0
|
|
|
|
0
|
$self->_deleted_relationships([]) if @$deleted_relationships; |
1587
|
|
|
|
|
|
|
|
1588
|
0
|
|
|
|
|
0
|
my $relationships = $self->relationships; |
1589
|
|
|
|
|
|
|
|
1590
|
0
|
|
|
|
|
0
|
foreach my $rel (keys %$instantiated) { |
1591
|
|
|
|
|
|
|
|
1592
|
0
|
0
|
|
|
|
0
|
next if $seen->{$rel}++; |
1593
|
0
|
|
|
|
|
0
|
my $relationship_data = $relationships->{$rel}; |
1594
|
|
|
|
|
|
|
|
1595
|
0
|
0
|
0
|
|
|
0
|
next if $relationship_data->{'accessibility'} ne 'private' |
1596
|
|
|
|
|
|
|
|| $relationship_data->{'singleton'} != $singletons; |
1597
|
|
|
|
|
|
|
|
1598
|
0
|
|
|
|
|
0
|
my @relationships = (); |
1599
|
0
|
0
|
|
|
|
0
|
if ($relationship_data->{'singleton'}) { |
1600
|
0
|
|
|
|
|
0
|
@relationships = ($self->$rel()); |
1601
|
|
|
|
|
|
|
} else { |
1602
|
0
|
0
|
|
|
|
0
|
if ($relationship_data->{'key'}) { |
1603
|
0
|
|
|
|
|
0
|
@relationships = values %{$self->$rel}; |
|
0
|
|
|
|
|
0
|
|
1604
|
|
|
|
|
|
|
} else { |
1605
|
0
|
|
|
|
|
0
|
@relationships = @{$self->$rel}; |
|
0
|
|
|
|
|
0
|
|
1606
|
|
|
|
|
|
|
} |
1607
|
|
|
|
|
|
|
} |
1608
|
|
|
|
|
|
|
|
1609
|
0
|
|
|
|
|
0
|
foreach my $obj (@relationships) { |
1610
|
|
|
|
|
|
|
|
1611
|
0
|
|
|
|
|
0
|
my $table = $relationship_data->{'table'}; |
1612
|
|
|
|
|
|
|
|
1613
|
0
|
0
|
|
|
|
0
|
my ($referencing_cols, $foreign_cols) = $self->relationship_columns($rel) or return; |
1614
|
|
|
|
|
|
|
|
1615
|
0
|
|
|
|
|
0
|
foreach my $col (@$referencing_cols) { |
1616
|
0
|
|
|
|
|
0
|
my $foreign = $table->nonqualified_name(shift @$foreign_cols); |
1617
|
0
|
|
|
|
|
0
|
my $attr = $table->alias_column($col); |
1618
|
0
|
|
|
|
|
0
|
$obj->$foreign($self->$attr()); |
1619
|
|
|
|
|
|
|
} |
1620
|
|
|
|
|
|
|
|
1621
|
0
|
|
|
|
|
0
|
$obj->should_be_committed(1); |
1622
|
|
|
|
|
|
|
|
1623
|
0
|
0
|
|
|
|
0
|
if ($obj->commit) { |
1624
|
0
|
|
|
|
|
0
|
$obj->should_be_committed(0); |
1625
|
|
|
|
|
|
|
} else { |
1626
|
0
|
|
|
|
|
0
|
$obj->should_be_committed(0); |
1627
|
0
|
|
|
|
|
0
|
return $self->error($obj->errvals); |
1628
|
|
|
|
|
|
|
}; |
1629
|
|
|
|
|
|
|
|
1630
|
|
|
|
|
|
|
} #end foreach @relationships |
1631
|
|
|
|
|
|
|
} #end foreach instantiated |
1632
|
|
|
|
|
|
|
|
1633
|
|
|
|
|
|
|
#$self->end() or return; |
1634
|
|
|
|
|
|
|
|
1635
|
0
|
|
|
|
|
0
|
return 1; |
1636
|
|
|
|
|
|
|
} |
1637
|
|
|
|
|
|
|
|
1638
|
|
|
|
|
|
|
=pod |
1639
|
|
|
|
|
|
|
|
1640
|
|
|
|
|
|
|
=item delete_relationships |
1641
|
|
|
|
|
|
|
|
1642
|
|
|
|
|
|
|
Used internally to delete all associated objects for a given object. |
1643
|
|
|
|
|
|
|
|
1644
|
|
|
|
|
|
|
$obj->delete_relationships |
1645
|
|
|
|
|
|
|
|
1646
|
|
|
|
|
|
|
only used for private objects |
1647
|
|
|
|
|
|
|
|
1648
|
|
|
|
|
|
|
=cut |
1649
|
|
|
|
|
|
|
|
1650
|
|
|
|
|
|
|
=pod |
1651
|
|
|
|
|
|
|
|
1652
|
|
|
|
|
|
|
=begin btest(delete_relationships) |
1653
|
|
|
|
|
|
|
|
1654
|
|
|
|
|
|
|
=end btest(delete_relationships) |
1655
|
|
|
|
|
|
|
|
1656
|
|
|
|
|
|
|
=cut |
1657
|
|
|
|
|
|
|
|
1658
|
|
|
|
|
|
|
sub delete_relationships { |
1659
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1660
|
|
|
|
|
|
|
|
1661
|
|
|
|
|
|
|
#$self->begin() or return; |
1662
|
|
|
|
|
|
|
|
1663
|
0
|
|
|
|
|
0
|
my $instantiated = $self->instantiated_relationships; |
1664
|
|
|
|
|
|
|
|
1665
|
0
|
|
|
|
|
0
|
my $deleted_relationships = $self->_deleted_relationships; |
1666
|
|
|
|
|
|
|
|
1667
|
0
|
|
|
|
|
0
|
foreach my $deleted_obj (@$deleted_relationships) { |
1668
|
0
|
|
|
|
|
0
|
$deleted_obj->should_be_deleted(2); |
1669
|
0
|
0
|
|
|
|
0
|
$deleted_obj->delete or return $self->error($deleted_obj->errvals); |
1670
|
|
|
|
|
|
|
} |
1671
|
|
|
|
|
|
|
|
1672
|
0
|
|
|
|
|
0
|
my $seen = {}; |
1673
|
|
|
|
|
|
|
|
1674
|
0
|
|
|
|
|
0
|
my $relationships = $self->relationships; |
1675
|
|
|
|
|
|
|
|
1676
|
0
|
|
|
|
|
0
|
foreach my $relationship (keys %$relationships) { |
1677
|
|
|
|
|
|
|
|
1678
|
0
|
|
|
|
|
0
|
my $relationship_data = $relationships->{$relationship}; |
1679
|
|
|
|
|
|
|
|
1680
|
0
|
0
|
|
|
|
0
|
next unless $relationship_data->{'accessibility'} eq 'private'; |
1681
|
|
|
|
|
|
|
|
1682
|
0
|
|
|
|
|
0
|
$self->instantiate($relationship); |
1683
|
|
|
|
|
|
|
|
1684
|
0
|
|
|
|
|
0
|
my @relationships = (); |
1685
|
|
|
|
|
|
|
|
1686
|
0
|
0
|
|
|
|
0
|
if ($relationship_data->{'singleton'}) { |
|
|
0
|
|
|
|
|
|
1687
|
0
|
|
|
|
|
0
|
@relationships = ($self->$relationship()); |
1688
|
|
|
|
|
|
|
} elsif ($relationship_data->{'key'}) { |
1689
|
0
|
|
|
|
|
0
|
@relationships = values %{$self->$relationship()}; |
|
0
|
|
|
|
|
0
|
|
1690
|
|
|
|
|
|
|
} else { |
1691
|
0
|
|
|
|
|
0
|
@relationships = @{$self->$relationship()}; |
|
0
|
|
|
|
|
0
|
|
1692
|
|
|
|
|
|
|
} |
1693
|
|
|
|
|
|
|
|
1694
|
0
|
|
|
|
|
0
|
foreach my $obj (@relationships) { |
1695
|
0
|
|
|
|
|
0
|
$obj->should_be_deleted(1); |
1696
|
0
|
0
|
|
|
|
0
|
if ($obj->delete) { |
1697
|
0
|
|
|
|
|
0
|
$obj->should_be_deleted(0); |
1698
|
|
|
|
|
|
|
} else { |
1699
|
0
|
|
|
|
|
0
|
$obj->should_be_deleted(0); |
1700
|
0
|
|
|
|
|
0
|
return $self->error($obj->errvals); |
1701
|
|
|
|
|
|
|
} |
1702
|
|
|
|
|
|
|
} |
1703
|
|
|
|
|
|
|
} |
1704
|
|
|
|
|
|
|
|
1705
|
0
|
|
|
|
|
0
|
return 1; |
1706
|
|
|
|
|
|
|
|
1707
|
|
|
|
|
|
|
|
1708
|
0
|
|
|
|
|
0
|
foreach my $rel (keys %$instantiated) { |
1709
|
0
|
0
|
|
|
|
0
|
next if $seen->{$rel}++; |
1710
|
0
|
|
|
|
|
0
|
my $relationship_data = $relationships->{$rel}; |
1711
|
|
|
|
|
|
|
|
1712
|
0
|
0
|
|
|
|
0
|
next unless $relationship_data->{'accessibility'} eq 'private'; |
1713
|
|
|
|
|
|
|
|
1714
|
0
|
|
|
|
|
0
|
my @relationships = (); |
1715
|
0
|
0
|
|
|
|
0
|
if ($relationship_data->{'singleton'}) { |
1716
|
0
|
|
|
|
|
0
|
@relationships = ($self->$rel()); |
1717
|
|
|
|
|
|
|
} else { |
1718
|
0
|
0
|
|
|
|
0
|
if ($relationship_data->{'key'}) { |
1719
|
0
|
|
|
|
|
0
|
@relationships = values %{$self->$rel()}; |
|
0
|
|
|
|
|
0
|
|
1720
|
|
|
|
|
|
|
} else { |
1721
|
0
|
|
|
|
|
0
|
@relationships = @{$self->$rel()}; |
|
0
|
|
|
|
|
0
|
|
1722
|
|
|
|
|
|
|
} |
1723
|
|
|
|
|
|
|
} |
1724
|
|
|
|
|
|
|
|
1725
|
0
|
|
|
|
|
0
|
foreach my $obj (@relationships) { |
1726
|
|
|
|
|
|
|
|
1727
|
0
|
|
|
|
|
0
|
$obj->should_be_deleted(1); |
1728
|
0
|
0
|
|
|
|
0
|
if ($obj->delete) { |
1729
|
0
|
|
|
|
|
0
|
$obj->should_be_deleted(0); |
1730
|
|
|
|
|
|
|
} else { |
1731
|
0
|
|
|
|
|
0
|
$obj->should_be_deleted(0); |
1732
|
0
|
|
|
|
|
0
|
return $self->error($obj->errvals); |
1733
|
|
|
|
|
|
|
}; |
1734
|
|
|
|
|
|
|
|
1735
|
|
|
|
|
|
|
} #end foreach @relationships |
1736
|
|
|
|
|
|
|
} #end foreach instantiated |
1737
|
|
|
|
|
|
|
|
1738
|
|
|
|
|
|
|
#$self->end() or return; |
1739
|
|
|
|
|
|
|
|
1740
|
0
|
|
|
|
|
0
|
return 1; |
1741
|
|
|
|
|
|
|
} |
1742
|
|
|
|
|
|
|
|
1743
|
|
|
|
|
|
|
=pod |
1744
|
|
|
|
|
|
|
|
1745
|
|
|
|
|
|
|
=item is_relationship |
1746
|
|
|
|
|
|
|
|
1747
|
|
|
|
|
|
|
Given an attribute, returns true if it is a relationship, false if not. |
1748
|
|
|
|
|
|
|
|
1749
|
|
|
|
|
|
|
if ($obj->is_relationship("some_attribute")) { |
1750
|
|
|
|
|
|
|
#do interesting thing |
1751
|
|
|
|
|
|
|
} |
1752
|
|
|
|
|
|
|
|
1753
|
|
|
|
|
|
|
=cut |
1754
|
|
|
|
|
|
|
|
1755
|
|
|
|
|
|
|
=pod |
1756
|
|
|
|
|
|
|
|
1757
|
|
|
|
|
|
|
=begin btest(is_relationship) |
1758
|
|
|
|
|
|
|
|
1759
|
|
|
|
|
|
|
=end btest(is_relationship) |
1760
|
|
|
|
|
|
|
|
1761
|
|
|
|
|
|
|
=cut |
1762
|
|
|
|
|
|
|
|
1763
|
|
|
|
|
|
|
sub is_relationship { |
1764
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1765
|
0
|
0
|
|
|
|
0
|
my $attribute = shift or return $self->error("Cannot determine is_relationship w/o attribute", "BOP-82"); |
1766
|
|
|
|
|
|
|
|
1767
|
0
|
0
|
|
|
|
0
|
return $self->relationships->{$attribute} ? 1 : 0; |
1768
|
|
|
|
|
|
|
}; |
1769
|
|
|
|
|
|
|
|
1770
|
|
|
|
|
|
|
=pod |
1771
|
|
|
|
|
|
|
|
1772
|
|
|
|
|
|
|
=item relationship_columns |
1773
|
|
|
|
|
|
|
|
1774
|
|
|
|
|
|
|
Takes a relationship as an argument, returns a list of two arrayrefs - the referencing columns (yours) |
1775
|
|
|
|
|
|
|
and the foreign columns (columns in the foreign table) |
1776
|
|
|
|
|
|
|
|
1777
|
|
|
|
|
|
|
my ($referencing, $foreign) = $self->relationship_columns($relationship); |
1778
|
|
|
|
|
|
|
|
1779
|
|
|
|
|
|
|
I can't think of a reason you'd ever want to call this directly. |
1780
|
|
|
|
|
|
|
|
1781
|
|
|
|
|
|
|
=cut |
1782
|
|
|
|
|
|
|
|
1783
|
|
|
|
|
|
|
sub relationship_columns { |
1784
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1785
|
0
|
0
|
|
|
|
0
|
my $prop = shift or return $self->error("Cannot get relationship_columns w/o relationship", "BOP-98"); |
1786
|
|
|
|
|
|
|
|
1787
|
0
|
|
|
|
|
0
|
my $relationships = $self->relationships; |
1788
|
|
|
|
|
|
|
|
1789
|
0
|
0
|
|
|
|
0
|
my $relationship_data = $relationships->{$prop} |
1790
|
|
|
|
|
|
|
or return $self->error("Cannot get relationship_columns for $prop : not relationship", "BOP-99"); |
1791
|
|
|
|
|
|
|
|
1792
|
0
|
|
|
|
|
0
|
my $table = $relationship_data->{'table'}; |
1793
|
0
|
|
|
|
|
0
|
my $ftable = $relationship_data->{'class'}->primary_table; |
1794
|
|
|
|
|
|
|
|
1795
|
0
|
0
|
|
|
|
0
|
if (ref $table eq 'ARRAY') { |
1796
|
0
|
|
|
|
|
0
|
($table, $ftable) = @$table; |
1797
|
|
|
|
|
|
|
}; |
1798
|
|
|
|
|
|
|
|
1799
|
0
|
|
|
|
|
0
|
my ($foreign_cols, $referencing_cols); |
1800
|
|
|
|
|
|
|
|
1801
|
|
|
|
|
|
|
# if we have a foreign_has_a defined, then the fclass->us is a 1-many. So we can just grab the relationship |
1802
|
|
|
|
|
|
|
# columns on the foreign table -> us and be done with it. |
1803
|
0
|
0
|
|
|
|
0
|
if ($relationship_data->{'foreign_has_a'}) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1804
|
0
|
|
|
|
|
0
|
my $fclass = $relationship_data->{'class'}; |
1805
|
0
|
|
|
|
|
0
|
my $foreign_relationship_method = $relationship_data->{'foreign_has_a'}; |
1806
|
|
|
|
|
|
|
|
1807
|
|
|
|
|
|
|
#we flip the columns! their foreign are our referencing and vice-versa. |
1808
|
0
|
0
|
|
|
|
0
|
($foreign_cols, $referencing_cols) = $fclass->relationship_columns($foreign_relationship_method) |
1809
|
|
|
|
|
|
|
or return $self->error($fclass->errvals); |
1810
|
|
|
|
|
|
|
|
1811
|
|
|
|
|
|
|
} |
1812
|
|
|
|
|
|
|
#next, if we have a relationship_key, then we point to the foreign table a lot of times, but we only |
1813
|
|
|
|
|
|
|
#keep track of the values in the key |
1814
|
|
|
|
|
|
|
elsif ($relationship_data->{'relationship_key'}) { |
1815
|
0
|
|
|
|
|
0
|
@$referencing_cols = ref $relationship_data->{'relationship_key'} eq 'ARRAY' |
1816
|
0
|
0
|
|
|
|
0
|
? @{$relationship_data->{'relationship_key'}} |
1817
|
|
|
|
|
|
|
: ($relationship_data->{'relationship_key'}); |
1818
|
0
|
0
|
|
|
|
0
|
if (ref $referencing_cols->[0] eq 'ARRAY') { |
1819
|
0
|
|
|
|
|
0
|
@$foreign_cols = @{$referencing_cols->[1]}; |
|
0
|
|
|
|
|
0
|
|
1820
|
0
|
|
|
|
|
0
|
@$referencing_cols = @{$referencing_cols->[0]}; |
|
0
|
|
|
|
|
0
|
|
1821
|
|
|
|
|
|
|
} else { |
1822
|
0
|
|
|
|
|
0
|
@$foreign_cols = map {$table->referenced_column($_)} @$referencing_cols; |
|
0
|
|
|
|
|
0
|
|
1823
|
|
|
|
|
|
|
} |
1824
|
|
|
|
|
|
|
} |
1825
|
|
|
|
|
|
|
#next, if it's a singleton, it's easy. We have a column in our table pointing to a primary key in theirs. |
1826
|
|
|
|
|
|
|
elsif ($relationship_data->{'singleton'}) { |
1827
|
0
|
|
|
|
|
0
|
@$referencing_cols = $table->foreign_cols($ftable); |
1828
|
0
|
|
|
|
|
0
|
@$foreign_cols = map {$table->referenced_column($_)} @$referencing_cols; |
|
0
|
|
|
|
|
0
|
|
1829
|
|
|
|
|
|
|
} |
1830
|
|
|
|
|
|
|
#finally, still easy, it's a has_many, so they have a column in their table pointing to us. |
1831
|
|
|
|
|
|
|
else { |
1832
|
0
|
|
|
|
|
0
|
@$foreign_cols = $ftable->foreign_cols($table); |
1833
|
0
|
|
|
|
|
0
|
@$referencing_cols = map {$table->nonqualified_name($ftable->referenced_column($_))} @$foreign_cols; |
|
0
|
|
|
|
|
0
|
|
1834
|
0
|
|
|
|
|
0
|
@$foreign_cols = map {$ftable->qualified_name($_)} @$foreign_cols; |
|
0
|
|
|
|
|
0
|
|
1835
|
|
|
|
|
|
|
} |
1836
|
|
|
|
|
|
|
|
1837
|
0
|
|
|
|
|
0
|
return ($referencing_cols, $foreign_cols); |
1838
|
|
|
|
|
|
|
|
1839
|
|
|
|
|
|
|
} |
1840
|
|
|
|
|
|
|
|
1841
|
|
|
|
|
|
|
=pod |
1842
|
|
|
|
|
|
|
|
1843
|
|
|
|
|
|
|
=begin btest(relationship_columns) |
1844
|
|
|
|
|
|
|
|
1845
|
|
|
|
|
|
|
=end btest(relationship_columns) |
1846
|
|
|
|
|
|
|
|
1847
|
|
|
|
|
|
|
=cut |
1848
|
|
|
|
|
|
|
|
1849
|
|
|
|
|
|
|
|
1850
|
|
|
|
|
|
|
=pod |
1851
|
|
|
|
|
|
|
|
1852
|
|
|
|
|
|
|
=item primary_identifier |
1853
|
|
|
|
|
|
|
|
1854
|
|
|
|
|
|
|
Returns the single, unique primary identifier of the object. |
1855
|
|
|
|
|
|
|
|
1856
|
|
|
|
|
|
|
my $id = $obj->primary_identifier; |
1857
|
|
|
|
|
|
|
|
1858
|
|
|
|
|
|
|
If an object has composite keys, this method will return an error by default. You can pass the 'composite' flag to get back |
1859
|
|
|
|
|
|
|
an arrayref of all primary keys. |
1860
|
|
|
|
|
|
|
|
1861
|
|
|
|
|
|
|
my $idref = $obj->primary_identifier('composite'); |
1862
|
|
|
|
|
|
|
|
1863
|
|
|
|
|
|
|
If you simply want a string identifier to identify the object, pass in the "string" flag. |
1864
|
|
|
|
|
|
|
|
1865
|
|
|
|
|
|
|
my $string = $obj->primary_identifier('string'); |
1866
|
|
|
|
|
|
|
|
1867
|
|
|
|
|
|
|
=cut |
1868
|
|
|
|
|
|
|
|
1869
|
|
|
|
|
|
|
=pod |
1870
|
|
|
|
|
|
|
|
1871
|
|
|
|
|
|
|
=begin btest(primary_identifier) |
1872
|
|
|
|
|
|
|
|
1873
|
|
|
|
|
|
|
=end btest(primary_identifier) |
1874
|
|
|
|
|
|
|
|
1875
|
|
|
|
|
|
|
=cut |
1876
|
|
|
|
|
|
|
|
1877
|
|
|
|
|
|
|
sub primary_identifier { |
1878
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1879
|
0
|
|
0
|
|
|
0
|
my $flag = shift || 0; |
1880
|
|
|
|
|
|
|
|
1881
|
0
|
|
|
|
|
0
|
my $primary_table = $self->primary_table; |
1882
|
|
|
|
|
|
|
|
1883
|
0
|
|
|
|
|
0
|
my @primary_cols = map {$self->$_()} $primary_table->alias_column($primary_table->primary_cols); |
|
0
|
|
|
|
|
0
|
|
1884
|
|
|
|
|
|
|
|
1885
|
0
|
0
|
|
|
|
0
|
if ($self->deleted) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1886
|
0
|
|
|
|
|
0
|
return; |
1887
|
|
|
|
|
|
|
} |
1888
|
|
|
|
|
|
|
elsif ($flag eq 'composite') { |
1889
|
0
|
|
|
|
|
0
|
return \@primary_cols; |
1890
|
|
|
|
|
|
|
} |
1891
|
|
|
|
|
|
|
elsif ($flag eq 'string') { |
1892
|
0
|
|
|
|
|
0
|
my $tables = $self->tables; |
1893
|
0
|
|
|
|
|
0
|
my @column_sets = (); |
1894
|
0
|
|
|
|
|
0
|
foreach my $table (@$tables) { |
1895
|
0
|
|
|
|
|
0
|
push @column_sets, join(';', $table->name, map {$self->$_()} $table->alias_column($table->primary_cols)); |
|
0
|
|
|
|
|
0
|
|
1896
|
|
|
|
|
|
|
} |
1897
|
0
|
|
|
|
|
0
|
return join(',', |
1898
|
|
|
|
|
|
|
$self->pkg, |
1899
|
|
|
|
|
|
|
@column_sets, |
1900
|
|
|
|
|
|
|
); |
1901
|
|
|
|
|
|
|
} |
1902
|
|
|
|
|
|
|
elsif (@primary_cols > 1) { |
1903
|
0
|
|
|
|
|
0
|
return $self->error("Object has no unique identifier - composite key (@primary_cols)", "BOP-80"); |
1904
|
|
|
|
|
|
|
} |
1905
|
|
|
|
|
|
|
else { |
1906
|
0
|
|
|
|
|
0
|
return $primary_cols[0]; |
1907
|
|
|
|
|
|
|
} |
1908
|
|
|
|
|
|
|
|
1909
|
|
|
|
|
|
|
} |
1910
|
|
|
|
|
|
|
|
1911
|
|
|
|
|
|
|
=pod |
1912
|
|
|
|
|
|
|
|
1913
|
|
|
|
|
|
|
=item copy |
1914
|
|
|
|
|
|
|
|
1915
|
|
|
|
|
|
|
copy is overridden in Basset::Object::Persistent. When you copy a persistent object, it automatically wipes out |
1916
|
|
|
|
|
|
|
the object's primary keys, and breaks all flags listing it as being in the database, so you get a fresh insert. |
1917
|
|
|
|
|
|
|
Explicitly call Basset::Object's copy to key primary key values. |
1918
|
|
|
|
|
|
|
|
1919
|
|
|
|
|
|
|
my $o2 = $o->copy; #loses primary keys |
1920
|
|
|
|
|
|
|
my $o2 = $o->Basset::Object::Copy; #keeps primary keys |
1921
|
|
|
|
|
|
|
|
1922
|
|
|
|
|
|
|
=cut |
1923
|
|
|
|
|
|
|
|
1924
|
|
|
|
|
|
|
sub copy { |
1925
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1926
|
|
|
|
|
|
|
|
1927
|
0
|
0
|
|
|
|
0
|
my $copy = $self->SUPER::copy(@_) or return; |
1928
|
|
|
|
|
|
|
|
1929
|
0
|
|
|
|
|
0
|
require UNIVERSAL; |
1930
|
0
|
0
|
|
|
|
0
|
if (UNIVERSAL::isa($copy, __PACKAGE__)) { |
1931
|
0
|
0
|
|
|
|
0
|
if (my $table = $self->primary_table) { |
1932
|
|
|
|
|
|
|
|
1933
|
0
|
|
|
|
|
0
|
my @primary_cols = $table->alias_column($table->primary_cols); |
1934
|
|
|
|
|
|
|
|
1935
|
0
|
|
|
|
|
0
|
foreach my $p (@primary_cols) { |
1936
|
0
|
|
|
|
|
0
|
$copy->$p(undef); |
1937
|
|
|
|
|
|
|
}; |
1938
|
|
|
|
|
|
|
} |
1939
|
|
|
|
|
|
|
|
1940
|
0
|
|
|
|
|
0
|
$copy->loaded(0); |
1941
|
0
|
|
|
|
|
0
|
$copy->committed(0); |
1942
|
0
|
|
|
|
|
0
|
$copy->in_db(0); |
1943
|
0
|
|
|
|
|
0
|
$copy->deleted(0); |
1944
|
|
|
|
|
|
|
} |
1945
|
|
|
|
|
|
|
|
1946
|
0
|
|
|
|
|
0
|
return $copy; |
1947
|
|
|
|
|
|
|
|
1948
|
|
|
|
|
|
|
} |
1949
|
|
|
|
|
|
|
|
1950
|
|
|
|
|
|
|
=pod |
1951
|
|
|
|
|
|
|
|
1952
|
|
|
|
|
|
|
=begin btest(copy) |
1953
|
|
|
|
|
|
|
|
1954
|
|
|
|
|
|
|
=end btest(copy) |
1955
|
|
|
|
|
|
|
|
1956
|
|
|
|
|
|
|
=cut |
1957
|
|
|
|
|
|
|
|
1958
|
|
|
|
|
|
|
=pod |
1959
|
|
|
|
|
|
|
|
1960
|
|
|
|
|
|
|
=item commit |
1961
|
|
|
|
|
|
|
|
1962
|
|
|
|
|
|
|
There is a lot of internal magic here which I'll decline to get into at the moment. Suffice to say, that ->commit() |
1963
|
|
|
|
|
|
|
will store your object in the database, and that all of the Right Things will happen during the commit. |
1964
|
|
|
|
|
|
|
|
1965
|
|
|
|
|
|
|
$object->commit(); |
1966
|
|
|
|
|
|
|
if ($object->committed){ |
1967
|
|
|
|
|
|
|
print "Success!\n"; |
1968
|
|
|
|
|
|
|
} else { |
1969
|
|
|
|
|
|
|
print "Failure : " . $object->errstring . "\n"; |
1970
|
|
|
|
|
|
|
}; |
1971
|
|
|
|
|
|
|
|
1972
|
|
|
|
|
|
|
=cut |
1973
|
|
|
|
|
|
|
|
1974
|
|
|
|
|
|
|
=pod |
1975
|
|
|
|
|
|
|
|
1976
|
|
|
|
|
|
|
=begin btest(commit) |
1977
|
|
|
|
|
|
|
|
1978
|
|
|
|
|
|
|
=end btest(commit) |
1979
|
|
|
|
|
|
|
|
1980
|
|
|
|
|
|
|
=cut |
1981
|
|
|
|
|
|
|
|
1982
|
|
|
|
|
|
|
sub commit { |
1983
|
|
|
|
|
|
|
|
1984
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
1985
|
|
|
|
|
|
|
|
1986
|
0
|
0
|
|
|
|
0
|
if ($self->should_be_deleted()) { |
1987
|
0
|
|
|
|
|
0
|
$self->should_be_deleted(1); |
1988
|
0
|
|
|
|
|
0
|
return $self->delete(@_); |
1989
|
|
|
|
|
|
|
} |
1990
|
|
|
|
|
|
|
|
1991
|
0
|
0
|
0
|
|
|
0
|
if ($self->tied_to_parent && ! $self->should_be_committed) { |
1992
|
0
|
|
|
|
|
0
|
return $self; |
1993
|
|
|
|
|
|
|
} |
1994
|
|
|
|
|
|
|
|
1995
|
0
|
0
|
|
|
|
0
|
if ($self->deleted) { |
1996
|
0
|
|
|
|
|
0
|
$self->notify('warnings', "attempted to commit deleted object : $self"); |
1997
|
0
|
|
|
|
|
0
|
return $self; |
1998
|
|
|
|
|
|
|
}; |
1999
|
|
|
|
|
|
|
|
2000
|
0
|
|
|
|
|
0
|
$self->committed(0); |
2001
|
0
|
|
|
|
|
0
|
$self->committing(1); |
2002
|
|
|
|
|
|
|
|
2003
|
0
|
0
|
|
|
|
0
|
$self->begin() or return $self->fatalerror($self->errvals); |
2004
|
|
|
|
|
|
|
|
2005
|
0
|
0
|
|
|
|
0
|
$self->cleanup() or return $self->fatalerror($self->errvals); |
2006
|
|
|
|
|
|
|
|
2007
|
|
|
|
|
|
|
#we need to commit our singletons first, since their ids are stored in our table. |
2008
|
0
|
0
|
|
|
|
0
|
$self->commit_relationships('singletons') or return $self->fatalerror($self->errvals); |
2009
|
|
|
|
|
|
|
|
2010
|
0
|
0
|
|
|
|
0
|
my @tables = @{$self->tables} or return $self->fatalerror("Cannot commit with no table", "BOP-01"); |
|
0
|
|
|
|
|
0
|
|
2011
|
|
|
|
|
|
|
|
2012
|
0
|
|
|
|
|
0
|
foreach my $table (@tables) { |
2013
|
|
|
|
|
|
|
|
2014
|
|
|
|
|
|
|
#we're updating, if this object has previously been loaded or committed and if we don't force inserts |
2015
|
0
|
0
|
0
|
|
|
0
|
if (! $self->force_insert && $self->in_db) { |
2016
|
|
|
|
|
|
|
|
2017
|
0
|
0
|
|
|
|
0
|
my $update_query = $table->update_query or return $self->fatalerror($table->errvals); |
2018
|
|
|
|
|
|
|
|
2019
|
0
|
|
|
|
|
0
|
my $query = $table->attach_to_query( |
2020
|
|
|
|
|
|
|
$update_query, |
2021
|
|
|
|
|
|
|
{ |
2022
|
0
|
0
|
|
|
|
0
|
'where' => join(' and ', map {"$_ = ?"} $table->primary_cols) |
2023
|
|
|
|
|
|
|
} |
2024
|
|
|
|
|
|
|
) or return $self->fatalerror($table->errvals); |
2025
|
|
|
|
|
|
|
|
2026
|
0
|
0
|
|
|
|
0
|
my @values = map {$self->$_()} $table->alias_column($table->update_bindables) or return $self->fatalerror($self->errvals); |
|
0
|
|
|
|
|
0
|
|
2027
|
|
|
|
|
|
|
|
2028
|
0
|
0
|
|
|
|
0
|
$self->arbitrary_sql( |
2029
|
|
|
|
|
|
|
'query' => $query, |
2030
|
|
|
|
|
|
|
'vars' => \@values, |
2031
|
|
|
|
|
|
|
'table' => $table, |
2032
|
|
|
|
|
|
|
'cols' => [$table->update_bindables] |
2033
|
|
|
|
|
|
|
) or return $self->fatalerror($self->errvals); |
2034
|
|
|
|
|
|
|
|
2035
|
|
|
|
|
|
|
} |
2036
|
|
|
|
|
|
|
#or we're inserting |
2037
|
|
|
|
|
|
|
else { |
2038
|
|
|
|
|
|
|
|
2039
|
0
|
0
|
|
|
|
0
|
my $insert_query = $table->insert_query or return $self->fatalerror($table->errvals); |
2040
|
|
|
|
|
|
|
|
2041
|
0
|
0
|
|
|
|
0
|
my @values = map {$self->$_()} $table->alias_column($table->insert_bindables) or return $self->fatalerror($self->errvals); |
|
0
|
|
|
|
|
0
|
|
2042
|
|
|
|
|
|
|
|
2043
|
0
|
0
|
|
|
|
0
|
$self->arbitrary_sql( |
2044
|
|
|
|
|
|
|
'query' => $insert_query, |
2045
|
|
|
|
|
|
|
'vars' => \@values, |
2046
|
|
|
|
|
|
|
'table' => $table, |
2047
|
|
|
|
|
|
|
'cols' => [$table->insert_bindables] |
2048
|
|
|
|
|
|
|
) or return $self->fatalerror($self->errvals); |
2049
|
|
|
|
|
|
|
|
2050
|
0
|
0
|
|
|
|
0
|
if ($table->autogenerated){ |
2051
|
|
|
|
|
|
|
|
2052
|
0
|
0
|
|
|
|
0
|
my $driver = $self->driver or return $self->fatalerror($self->errvals); |
2053
|
|
|
|
|
|
|
|
2054
|
0
|
0
|
|
|
|
0
|
my $id_stmt = $driver->prepare_cached($table->last_insert_query()) |
2055
|
|
|
|
|
|
|
or return $self->fatalerror($driver->errstr, "BOP-05"); |
2056
|
|
|
|
|
|
|
|
2057
|
0
|
0
|
|
|
|
0
|
$id_stmt->execute() |
2058
|
|
|
|
|
|
|
or return $self->fatalerror($id_stmt->errstr, "BOP-04"); |
2059
|
|
|
|
|
|
|
|
2060
|
0
|
|
|
|
|
0
|
my ($id) = $id_stmt->fetchrow_array; |
2061
|
|
|
|
|
|
|
|
2062
|
0
|
0
|
|
|
|
0
|
$id_stmt->finish() |
2063
|
|
|
|
|
|
|
or return $self->fatalerror($id_stmt->errstr, "BOP-10"); |
2064
|
|
|
|
|
|
|
|
2065
|
0
|
|
|
|
|
0
|
my $primary = $table->alias_column($table->primary_column); |
2066
|
0
|
|
|
|
|
0
|
$self->$primary($id); |
2067
|
|
|
|
|
|
|
}; |
2068
|
|
|
|
|
|
|
|
2069
|
|
|
|
|
|
|
}; |
2070
|
|
|
|
|
|
|
} |
2071
|
|
|
|
|
|
|
|
2072
|
|
|
|
|
|
|
#commit our nonsingleton tied relationships |
2073
|
0
|
0
|
|
|
|
0
|
$self->commit_relationships('nonsingletons') or return $self->fatalerror($self->errvals); |
2074
|
|
|
|
|
|
|
|
2075
|
|
|
|
|
|
|
#we have committed this object |
2076
|
0
|
|
|
|
|
0
|
$self->committed(1); |
2077
|
|
|
|
|
|
|
#and it's in the database |
2078
|
0
|
|
|
|
|
0
|
$self->in_db(1); |
2079
|
|
|
|
|
|
|
|
2080
|
0
|
|
|
|
|
0
|
my $primary_identifier = $self->primary_identifier('string'); |
2081
|
0
|
|
|
|
|
0
|
my $load_cache = $self->central_load_cache; |
2082
|
0
|
0
|
|
|
|
0
|
unless (defined $load_cache->{$primary_identifier}) { |
2083
|
0
|
|
|
|
|
0
|
$load_cache->{$primary_identifier} = $self; |
2084
|
0
|
|
|
|
|
0
|
weaken($load_cache->{$primary_identifier}); |
2085
|
|
|
|
|
|
|
} |
2086
|
|
|
|
|
|
|
|
2087
|
0
|
0
|
|
|
|
0
|
$self->end() or return $self->fatalerror($self->errvals); |
2088
|
|
|
|
|
|
|
|
2089
|
0
|
|
|
|
|
0
|
$self->committing(0); |
2090
|
|
|
|
|
|
|
|
2091
|
0
|
|
|
|
|
0
|
return $self; |
2092
|
|
|
|
|
|
|
|
2093
|
|
|
|
|
|
|
} |
2094
|
|
|
|
|
|
|
|
2095
|
|
|
|
|
|
|
=pod |
2096
|
|
|
|
|
|
|
|
2097
|
|
|
|
|
|
|
=item writable_method |
2098
|
|
|
|
|
|
|
|
2099
|
|
|
|
|
|
|
Given a method name, returns true if the value of this method will be written out to disk on the |
2100
|
|
|
|
|
|
|
next commit, and false if it will not be written out. |
2101
|
|
|
|
|
|
|
|
2102
|
|
|
|
|
|
|
my $output = $object->writable_method('id'); |
2103
|
|
|
|
|
|
|
if ($output) { |
2104
|
|
|
|
|
|
|
print "object will store id\n"; |
2105
|
|
|
|
|
|
|
} else { |
2106
|
|
|
|
|
|
|
print "object will not store id\n"; |
2107
|
|
|
|
|
|
|
} |
2108
|
|
|
|
|
|
|
|
2109
|
|
|
|
|
|
|
=cut |
2110
|
|
|
|
|
|
|
|
2111
|
|
|
|
|
|
|
=pod |
2112
|
|
|
|
|
|
|
|
2113
|
|
|
|
|
|
|
=begin btest(writable_method) |
2114
|
|
|
|
|
|
|
|
2115
|
|
|
|
|
|
|
$test->is(scalar(__PACKAGE__->writable_method), undef, "Cannot determine if writable on a class"); |
2116
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode, "BOP-62", "proper error code"); |
2117
|
|
|
|
|
|
|
|
2118
|
|
|
|
|
|
|
my $subclass = "Basset::Test::Testing::__PACKAGE__::writable_method::Subclass1"; |
2119
|
|
|
|
|
|
|
|
2120
|
|
|
|
|
|
|
package Basset::Test::Testing::__PACKAGE__::writable_method::Subclass1; |
2121
|
|
|
|
|
|
|
our @ISA = qw(__PACKAGE__); |
2122
|
|
|
|
|
|
|
|
2123
|
|
|
|
|
|
|
$subclass->add_attr('one'); |
2124
|
|
|
|
|
|
|
$subclass->add_attr('two'); |
2125
|
|
|
|
|
|
|
$subclass->add_attr('three'); |
2126
|
|
|
|
|
|
|
|
2127
|
|
|
|
|
|
|
package __PACKAGE__; |
2128
|
|
|
|
|
|
|
|
2129
|
|
|
|
|
|
|
my $o = $subclass->new(); |
2130
|
|
|
|
|
|
|
$test->ok($o, "Got object"); |
2131
|
|
|
|
|
|
|
|
2132
|
|
|
|
|
|
|
$test->is(scalar($o->writable_method), undef, "Cannot determine if writable w/o method"); |
2133
|
|
|
|
|
|
|
$test->is($o->errcode, "BOP-63", "proper error code"); |
2134
|
|
|
|
|
|
|
|
2135
|
|
|
|
|
|
|
$test->is(scalar($o->writable_method('one')), undef, "Cannot determine if writable w/o primary table"); |
2136
|
|
|
|
|
|
|
$test->is($o->errcode, 'BOP-64', "proper error code"); |
2137
|
|
|
|
|
|
|
|
2138
|
|
|
|
|
|
|
$subclass->add_primarytable( |
2139
|
|
|
|
|
|
|
'name' => 'test_table', |
2140
|
|
|
|
|
|
|
'definition' => { |
2141
|
|
|
|
|
|
|
'one' => 'SQL_INTEGER', |
2142
|
|
|
|
|
|
|
'two' => 'SQL_INTEGER', |
2143
|
|
|
|
|
|
|
'three' => 'SQL_INTEGER', |
2144
|
|
|
|
|
|
|
}, |
2145
|
|
|
|
|
|
|
#'insert_columns' => ['two'], |
2146
|
|
|
|
|
|
|
#'update_columns' => ['three'], |
2147
|
|
|
|
|
|
|
); |
2148
|
|
|
|
|
|
|
|
2149
|
|
|
|
|
|
|
$test->is($o->writable_method('one'), 1, "method is writable w/o insert or update columns on insert"); |
2150
|
|
|
|
|
|
|
$test->is($o->loaded(1), 1, 'loaded is one'); |
2151
|
|
|
|
|
|
|
$test->is($o->writable_method('one'), 1, "method is writable w/o insert or update columns on update, loaded"); |
2152
|
|
|
|
|
|
|
$test->is($o->loaded(0), 0, 'loaded is zero'); |
2153
|
|
|
|
|
|
|
$test->is($o->committed(1), 1, 'committed is 1'); |
2154
|
|
|
|
|
|
|
$test->is($o->writable_method('one'), 1, "method is writable w/o insert or update columns on update, committed"); |
2155
|
|
|
|
|
|
|
$test->is($o->loaded(1), 1, 'loaded is 1'); |
2156
|
|
|
|
|
|
|
$test->is($o->committed(1), 1, 'committed is 1'); |
2157
|
|
|
|
|
|
|
$test->is($o->writable_method('one'), 1, "method is writable w/o insert or update columns on insert, force_insert"); |
2158
|
|
|
|
|
|
|
$test->is($o->loaded(0), 0, 'loaded is 0'); |
2159
|
|
|
|
|
|
|
$test->is($o->committed(0), 0, 'committed is 0'); |
2160
|
|
|
|
|
|
|
$test->is($o->writable_method('one'), 1, "method is writable w/o insert or update columns on insert, force_insert"); |
2161
|
|
|
|
|
|
|
|
2162
|
|
|
|
|
|
|
$subclass->add_primarytable( |
2163
|
|
|
|
|
|
|
'name' => 'test_table', |
2164
|
|
|
|
|
|
|
'definition' => { |
2165
|
|
|
|
|
|
|
'one' => 'SQL_INTEGER', |
2166
|
|
|
|
|
|
|
'two' => 'SQL_INTEGER', |
2167
|
|
|
|
|
|
|
'three' => 'SQL_INTEGER', |
2168
|
|
|
|
|
|
|
}, |
2169
|
|
|
|
|
|
|
'insert_columns' => ['two'], |
2170
|
|
|
|
|
|
|
'update_columns' => ['three'], |
2171
|
|
|
|
|
|
|
); |
2172
|
|
|
|
|
|
|
|
2173
|
|
|
|
|
|
|
$test->is($o->writable_method('one'), 0, "method one is not writable w/ insert and update columns on insert"); |
2174
|
|
|
|
|
|
|
$test->is($o->loaded(1), 1, 'loaded is 1'); |
2175
|
|
|
|
|
|
|
$test->is($o->writable_method('one'), 0, "method one is not writable w/ insert and update columns on update, loaded"); |
2176
|
|
|
|
|
|
|
$test->is($o->loaded(0), 0, 'loaded is zero'); |
2177
|
|
|
|
|
|
|
$test->is($o->committed(1), 1, 'committed is 1'); |
2178
|
|
|
|
|
|
|
$test->is($o->writable_method('one'), 0, "method one is not writable w/ insert and update columns on update, committed"); |
2179
|
|
|
|
|
|
|
$test->is($o->loaded(1), 1, 'loaded is 1'); |
2180
|
|
|
|
|
|
|
$test->is($o->committed(1), 1, 'committed is 1'); |
2181
|
|
|
|
|
|
|
$test->is($o->force_insert(1), 1, 'force_insert is 1'); |
2182
|
|
|
|
|
|
|
$test->is($o->writable_method('one'), 0, "method one is not writable w/ insert and update columns on insert, force_insert"); |
2183
|
|
|
|
|
|
|
$test->is($o->loaded(0), 0, 'loaded is 0'); |
2184
|
|
|
|
|
|
|
$test->is($o->committed(0), 0, 'committed is 0'); |
2185
|
|
|
|
|
|
|
$test->is($o->writable_method('one'), 0, "method one is not writable w/ insert and update columns on insert, force_insert"); |
2186
|
|
|
|
|
|
|
$test->is($o->force_insert(0), 0, 'force_insert is 0'); |
2187
|
|
|
|
|
|
|
|
2188
|
|
|
|
|
|
|
$test->is($o->writable_method('two'), 1, "method two is writable w/ insert and update columns on insert"); |
2189
|
|
|
|
|
|
|
$test->is($o->loaded(1), 1, 'loaded is 1'); |
2190
|
|
|
|
|
|
|
$test->is($o->writable_method('two'), 0, "method two is not writable w/ insert and update columns on update, loaded"); |
2191
|
|
|
|
|
|
|
$test->is($o->loaded(0), 0, 'loaded is zero'); |
2192
|
|
|
|
|
|
|
$test->is($o->committed(1), 1, 'committed is 1'); |
2193
|
|
|
|
|
|
|
$test->is($o->writable_method('two'), 0, "method two is not writable w/ insert and update columns on update, committed"); |
2194
|
|
|
|
|
|
|
$test->is($o->loaded(1), 1, 'loaded is 1'); |
2195
|
|
|
|
|
|
|
$test->is($o->committed(1), 1, 'committed is 1'); |
2196
|
|
|
|
|
|
|
$test->is($o->force_insert(1), 1, 'force_insert is 1'); |
2197
|
|
|
|
|
|
|
$test->is($o->writable_method('two'), 1, "method two is writable w/ insert and update columns on insert, force_insert"); |
2198
|
|
|
|
|
|
|
$test->is($o->loaded(0), 0, 'loaded is 0'); |
2199
|
|
|
|
|
|
|
$test->is($o->committed(0), 0, 'committed is 0'); |
2200
|
|
|
|
|
|
|
$test->is($o->writable_method('two'), 1, "method two is writable w/ insert and update columns on insert, force_insert"); |
2201
|
|
|
|
|
|
|
$test->is($o->force_insert(0), 0, 'force_insert is 0'); |
2202
|
|
|
|
|
|
|
|
2203
|
|
|
|
|
|
|
$test->is($o->writable_method('three'), 0, "method three is not writable w/ insert and update columns on insert"); |
2204
|
|
|
|
|
|
|
$test->is($o->loaded(1), 1, 'loaded is 1'); |
2205
|
|
|
|
|
|
|
$test->is($o->writable_method('three'), 1, "method three is writable w/ insert and update columns on update, loaded"); |
2206
|
|
|
|
|
|
|
$test->is($o->loaded(0), 0, 'loaded is zero'); |
2207
|
|
|
|
|
|
|
$test->is($o->committed(1), 1, 'committed is 1'); |
2208
|
|
|
|
|
|
|
$test->is($o->writable_method('three'), 1, "method three is writable w/ insert and update columns on update, committed"); |
2209
|
|
|
|
|
|
|
$test->is($o->loaded(1), 1, 'loaded is 1'); |
2210
|
|
|
|
|
|
|
$test->is($o->committed(1), 1, 'committed is 1'); |
2211
|
|
|
|
|
|
|
$test->is($o->force_insert(1), 1, 'force_insert is 1'); |
2212
|
|
|
|
|
|
|
$test->is($o->writable_method('three'), 0, "method three is not writable w/ insert and update columns on insert, force_insert"); |
2213
|
|
|
|
|
|
|
$test->is($o->loaded(0), 0, 'loaded is 0'); |
2214
|
|
|
|
|
|
|
$test->is($o->committed(0), 0, 'committed is 0'); |
2215
|
|
|
|
|
|
|
$test->is($o->writable_method('three'), 0, "method three is not writable w/ insert and update columns on insert, force_insert"); |
2216
|
|
|
|
|
|
|
$test->is($o->force_insert(0), 0, 'force_insert is 0'); |
2217
|
|
|
|
|
|
|
|
2218
|
|
|
|
|
|
|
$subclass->add_primarytable( |
2219
|
|
|
|
|
|
|
'name' => 'test_table', |
2220
|
|
|
|
|
|
|
'definition' => { |
2221
|
|
|
|
|
|
|
'alpha' => 'SQL_INTEGER', |
2222
|
|
|
|
|
|
|
'beta' => 'SQL_INTEGER', |
2223
|
|
|
|
|
|
|
'gamma' => 'SQL_INTEGER', |
2224
|
|
|
|
|
|
|
}, |
2225
|
|
|
|
|
|
|
'insert_columns' => ['beta'], |
2226
|
|
|
|
|
|
|
'update_columns' => ['gamma'], |
2227
|
|
|
|
|
|
|
'column_aliases' => { |
2228
|
|
|
|
|
|
|
'alpha' => 'one', |
2229
|
|
|
|
|
|
|
'beta' => 'two', |
2230
|
|
|
|
|
|
|
'gamma' => 'three', |
2231
|
|
|
|
|
|
|
}, |
2232
|
|
|
|
|
|
|
); |
2233
|
|
|
|
|
|
|
|
2234
|
|
|
|
|
|
|
$test->is($o->writable_method('one'), 0, "method one (from alpha) is not writable w/ insert and update columns on insert"); |
2235
|
|
|
|
|
|
|
$test->is($o->loaded(1), 1, 'loaded is 1'); |
2236
|
|
|
|
|
|
|
$test->is($o->writable_method('one'), 0, "method one (from alpha) is not writable w/ insert and update columns on update, loaded"); |
2237
|
|
|
|
|
|
|
$test->is($o->loaded(0), 0, 'loaded is zero'); |
2238
|
|
|
|
|
|
|
$test->is($o->committed(1), 1, 'committed is 1'); |
2239
|
|
|
|
|
|
|
$test->is($o->writable_method('one'), 0, "method one (from alpha) is not writable w/ insert and update columns on update, committed"); |
2240
|
|
|
|
|
|
|
$test->is($o->loaded(1), 1, 'loaded is 1'); |
2241
|
|
|
|
|
|
|
$test->is($o->committed(1), 1, 'committed is 1'); |
2242
|
|
|
|
|
|
|
$test->is($o->force_insert(1), 1, 'force_insert is 1'); |
2243
|
|
|
|
|
|
|
$test->is($o->writable_method('one'), 0, "method one (from alpha) is not writable w/ insert and update columns on insert, force_insert"); |
2244
|
|
|
|
|
|
|
$test->is($o->loaded(0), 0, 'loaded is 0'); |
2245
|
|
|
|
|
|
|
$test->is($o->committed(0), 0, 'committed is 0'); |
2246
|
|
|
|
|
|
|
$test->is($o->writable_method('one'), 0, "method one (from alpha) is not writable w/ insert and update columns on insert, force_insert"); |
2247
|
|
|
|
|
|
|
$test->is($o->force_insert(0), 0, 'force_insert is 0'); |
2248
|
|
|
|
|
|
|
|
2249
|
|
|
|
|
|
|
$test->is($o->writable_method('two'), 1, "method two (from beta) is writable w/ insert and update columns on insert"); |
2250
|
|
|
|
|
|
|
$test->is($o->loaded(1), 1, 'loaded is 1'); |
2251
|
|
|
|
|
|
|
$test->is($o->writable_method('two'), 0, "method two (from beta) is not writable w/ insert and update columns on update, loaded"); |
2252
|
|
|
|
|
|
|
$test->is($o->loaded(0), 0, 'loaded is zero'); |
2253
|
|
|
|
|
|
|
$test->is($o->committed(1), 1, 'committed is 1'); |
2254
|
|
|
|
|
|
|
$test->is($o->writable_method('two'), 0, "method two (from beta) is not writable w/ insert and update columns on update, committed"); |
2255
|
|
|
|
|
|
|
$test->is($o->loaded(1), 1, 'loaded is 1'); |
2256
|
|
|
|
|
|
|
$test->is($o->committed(1), 1, 'committed is 1'); |
2257
|
|
|
|
|
|
|
$test->is($o->force_insert(1), 1, 'force_insert is 1'); |
2258
|
|
|
|
|
|
|
$test->is($o->writable_method('two'), 1, "method two (from beta) is writable w/ insert and update columns on insert, force_insert"); |
2259
|
|
|
|
|
|
|
$test->is($o->loaded(0), 0, 'loaded is 0'); |
2260
|
|
|
|
|
|
|
$test->is($o->committed(0), 0, 'committed is 0'); |
2261
|
|
|
|
|
|
|
$test->is($o->writable_method('two'), 1, "method two (from beta) is writable w/ insert and update columns on insert, force_insert"); |
2262
|
|
|
|
|
|
|
$test->is($o->force_insert(0), 0, 'force_insert is 0'); |
2263
|
|
|
|
|
|
|
|
2264
|
|
|
|
|
|
|
$test->is($o->writable_method('three'), 0, "method three (from gamma) is not writable w/ insert and update columns on insert"); |
2265
|
|
|
|
|
|
|
$test->is($o->loaded(1), 1, 'loaded is 1'); |
2266
|
|
|
|
|
|
|
$test->is($o->writable_method('three'), 1, "method three (from gamma) is writable w/ insert and update columns on update, loaded"); |
2267
|
|
|
|
|
|
|
$test->is($o->loaded(0), 0, 'loaded is zero'); |
2268
|
|
|
|
|
|
|
$test->is($o->committed(1), 1, 'committed is 1'); |
2269
|
|
|
|
|
|
|
$test->is($o->writable_method('three'), 1, "method three (from gamma) is writable w/ insert and update columns on update, committed"); |
2270
|
|
|
|
|
|
|
$test->is($o->loaded(1), 1, 'loaded is 1'); |
2271
|
|
|
|
|
|
|
$test->is($o->committed(1), 1, 'committed is 1'); |
2272
|
|
|
|
|
|
|
$test->is($o->force_insert(1), 1, 'force_insert is 1'); |
2273
|
|
|
|
|
|
|
$test->is($o->writable_method('three'), 0, "method three (from gamma) is not writable w/ insert and update columns on insert, force_insert"); |
2274
|
|
|
|
|
|
|
$test->is($o->loaded(0), 0, 'loaded is 0'); |
2275
|
|
|
|
|
|
|
$test->is($o->committed(0), 0, 'committed is 0'); |
2276
|
|
|
|
|
|
|
$test->is($o->writable_method('three'), 0, "method three (from gamma) is not writable w/ insert and update columns on insert, force_insert"); |
2277
|
|
|
|
|
|
|
$test->is($o->force_insert(0), 0, 'force_insert is 0'); |
2278
|
|
|
|
|
|
|
|
2279
|
|
|
|
|
|
|
=end btest(writable_method) |
2280
|
|
|
|
|
|
|
|
2281
|
|
|
|
|
|
|
=cut |
2282
|
|
|
|
|
|
|
|
2283
|
|
|
|
|
|
|
sub writable_method { |
2284
|
38
|
|
|
38
|
1
|
1238
|
my $self = shift; |
2285
|
|
|
|
|
|
|
|
2286
|
38
|
100
|
|
|
|
117
|
return $self->error("Cannot determine if writable on a class", "BOP-62") unless ref $self; |
2287
|
|
|
|
|
|
|
|
2288
|
37
|
100
|
|
|
|
101
|
my $method = shift or return $self->error("Cannot determine if writable w/o method", "BOP-63"); |
2289
|
|
|
|
|
|
|
|
2290
|
36
|
100
|
|
|
|
109
|
my $table = $self->primary_table or return $self->error("Cannot determine if writable with no table", "BOP-64"); |
2291
|
|
|
|
|
|
|
|
2292
|
35
|
|
|
|
|
50
|
my @bindables; |
2293
|
|
|
|
|
|
|
|
2294
|
|
|
|
|
|
|
#we're updating, if this object has previously been loaded or committed and if we don't force inserts |
2295
|
35
|
100
|
100
|
|
|
102
|
if (! $self->force_insert && ($self->loaded || $self->committed)){ |
|
|
|
66
|
|
|
|
|
2296
|
15
|
|
|
|
|
54
|
@bindables = $table->update_columns; |
2297
|
|
|
|
|
|
|
} else { |
2298
|
20
|
|
|
|
|
78
|
@bindables = $table->insert_columns; |
2299
|
|
|
|
|
|
|
} |
2300
|
|
|
|
|
|
|
|
2301
|
35
|
|
|
|
|
68
|
@bindables = map {$table->alias_column($_)} @bindables; |
|
45
|
|
|
|
|
403
|
|
2302
|
|
|
|
|
|
|
|
2303
|
35
|
|
|
|
|
76
|
foreach my $bindable (@bindables) { |
2304
|
35
|
100
|
|
|
|
162
|
return 1 if $bindable eq $method |
2305
|
|
|
|
|
|
|
} |
2306
|
|
|
|
|
|
|
|
2307
|
20
|
|
|
|
|
102
|
return 0; |
2308
|
|
|
|
|
|
|
|
2309
|
|
|
|
|
|
|
} |
2310
|
|
|
|
|
|
|
|
2311
|
|
|
|
|
|
|
=pod |
2312
|
|
|
|
|
|
|
|
2313
|
|
|
|
|
|
|
=item load |
2314
|
|
|
|
|
|
|
|
2315
|
|
|
|
|
|
|
the load method loads an object from the database. The arguments passed must be the |
2316
|
|
|
|
|
|
|
primary_column specified in your primary table, in that order. |
2317
|
|
|
|
|
|
|
|
2318
|
|
|
|
|
|
|
__PACKAGE__->add_primarytable( |
2319
|
|
|
|
|
|
|
. |
2320
|
|
|
|
|
|
|
. |
2321
|
|
|
|
|
|
|
. |
2322
|
|
|
|
|
|
|
'primary_column' => 'id' |
2323
|
|
|
|
|
|
|
); |
2324
|
|
|
|
|
|
|
|
2325
|
|
|
|
|
|
|
my $obj = Some::Package->load($id); |
2326
|
|
|
|
|
|
|
|
2327
|
|
|
|
|
|
|
__PACKAGE__->add_primarytable( |
2328
|
|
|
|
|
|
|
. |
2329
|
|
|
|
|
|
|
. |
2330
|
|
|
|
|
|
|
. |
2331
|
|
|
|
|
|
|
'primary_column' => [qw(foo bar baz)] |
2332
|
|
|
|
|
|
|
); |
2333
|
|
|
|
|
|
|
|
2334
|
|
|
|
|
|
|
my $obj = Some::Package->load($foo, $bar, $baz); |
2335
|
|
|
|
|
|
|
|
2336
|
|
|
|
|
|
|
The arguments passed must be in the same order they were defined. |
2337
|
|
|
|
|
|
|
|
2338
|
|
|
|
|
|
|
Returns an error if no object found that matches |
2339
|
|
|
|
|
|
|
|
2340
|
|
|
|
|
|
|
=cut |
2341
|
|
|
|
|
|
|
|
2342
|
|
|
|
|
|
|
=pod |
2343
|
|
|
|
|
|
|
|
2344
|
|
|
|
|
|
|
=begin btest(load) |
2345
|
|
|
|
|
|
|
|
2346
|
|
|
|
|
|
|
=end btest(load) |
2347
|
|
|
|
|
|
|
|
2348
|
|
|
|
|
|
|
=cut |
2349
|
|
|
|
|
|
|
|
2350
|
|
|
|
|
|
|
sub load { |
2351
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
2352
|
|
|
|
|
|
|
|
2353
|
0
|
0
|
|
|
|
0
|
return $class->error("Cannot load with no ID!", "BOP-09") unless @_; |
2354
|
|
|
|
|
|
|
|
2355
|
0
|
0
|
|
|
|
0
|
my $table = $class->primary_table or return $class->error("Cannot load with no table", "BOP-01"); |
2356
|
|
|
|
|
|
|
|
2357
|
0
|
|
|
|
|
0
|
my %input = (); |
2358
|
0
|
|
|
|
|
0
|
@input{$table->primary_cols} = @_; |
2359
|
|
|
|
|
|
|
|
2360
|
0
|
|
|
|
|
0
|
return $class->load_where([%input], {'singleton' => 1}); |
2361
|
|
|
|
|
|
|
|
2362
|
|
|
|
|
|
|
} |
2363
|
|
|
|
|
|
|
|
2364
|
|
|
|
|
|
|
=pod |
2365
|
|
|
|
|
|
|
|
2366
|
|
|
|
|
|
|
=item load_or_new |
2367
|
|
|
|
|
|
|
|
2368
|
|
|
|
|
|
|
Does what it sounds like, it tries to load an object, and if it fails, it creates a new B |
2369
|
|
|
|
|
|
|
object instead. Basically, this allows some lazy object creation for things like stateless |
2370
|
|
|
|
|
|
|
applications (such as cgis) that don't know in advance what they're operating on, and don't really |
2371
|
|
|
|
|
|
|
care. So you can try to load an object if values were passed back to you, and if they weren't |
2372
|
|
|
|
|
|
|
then you create an automatically create a new one for yourself. |
2373
|
|
|
|
|
|
|
|
2374
|
|
|
|
|
|
|
=cut |
2375
|
|
|
|
|
|
|
|
2376
|
|
|
|
|
|
|
=pod |
2377
|
|
|
|
|
|
|
|
2378
|
|
|
|
|
|
|
=begin btest(load_or_new) |
2379
|
|
|
|
|
|
|
|
2380
|
|
|
|
|
|
|
=end btest(load_or_new) |
2381
|
|
|
|
|
|
|
|
2382
|
|
|
|
|
|
|
=cut |
2383
|
|
|
|
|
|
|
|
2384
|
|
|
|
|
|
|
sub load_or_new { |
2385
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
2386
|
|
|
|
|
|
|
|
2387
|
0
|
|
0
|
|
|
0
|
return $class->load(@_) || $class->new(); |
2388
|
|
|
|
|
|
|
}; |
2389
|
|
|
|
|
|
|
|
2390
|
|
|
|
|
|
|
=pod |
2391
|
|
|
|
|
|
|
|
2392
|
|
|
|
|
|
|
=item load_many |
2393
|
|
|
|
|
|
|
|
2394
|
|
|
|
|
|
|
Convenience method. If you have a class that only uses one primary column (a unique ID, for instance) and |
2395
|
|
|
|
|
|
|
you want to load certain objects with given IDs, you can use load_many. |
2396
|
|
|
|
|
|
|
|
2397
|
|
|
|
|
|
|
my $objects = $self->load_many(1,2,3,4,5); |
2398
|
|
|
|
|
|
|
|
2399
|
|
|
|
|
|
|
=cut |
2400
|
|
|
|
|
|
|
|
2401
|
|
|
|
|
|
|
=pod |
2402
|
|
|
|
|
|
|
|
2403
|
|
|
|
|
|
|
=begin btest(load_many) |
2404
|
|
|
|
|
|
|
|
2405
|
|
|
|
|
|
|
=end btest(load_many) |
2406
|
|
|
|
|
|
|
|
2407
|
|
|
|
|
|
|
=cut |
2408
|
|
|
|
|
|
|
|
2409
|
|
|
|
|
|
|
sub load_many { |
2410
|
|
|
|
|
|
|
|
2411
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
2412
|
0
|
|
|
|
|
0
|
my @ids = @_; |
2413
|
|
|
|
|
|
|
|
2414
|
0
|
0
|
|
|
|
0
|
return $class->error("Cannot load many w/o ids", "BOP-67") unless @ids; |
2415
|
|
|
|
|
|
|
|
2416
|
0
|
0
|
|
|
|
0
|
my $table = $class->primary_table() or return $class->error("Cannot load many w/o primary table", "BOP-65"); |
2417
|
|
|
|
|
|
|
|
2418
|
0
|
|
|
|
|
0
|
my @cols = $table->primary_cols(); |
2419
|
0
|
0
|
|
|
|
0
|
if (@cols > 1) { |
2420
|
0
|
|
|
|
|
0
|
return $class->error("Cannot load many w/multiple primary columns", "BOP-66"); |
2421
|
|
|
|
|
|
|
}; |
2422
|
|
|
|
|
|
|
|
2423
|
0
|
|
|
|
|
0
|
return $class->load_where($cols[0] => \@ids); |
2424
|
|
|
|
|
|
|
|
2425
|
|
|
|
|
|
|
} |
2426
|
|
|
|
|
|
|
|
2427
|
|
|
|
|
|
|
=pod |
2428
|
|
|
|
|
|
|
|
2429
|
|
|
|
|
|
|
=item load_next |
2430
|
|
|
|
|
|
|
|
2431
|
|
|
|
|
|
|
=cut |
2432
|
|
|
|
|
|
|
|
2433
|
|
|
|
|
|
|
sub load_next { |
2434
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
2435
|
0
|
|
0
|
|
|
0
|
my $clauses = shift || {}; |
2436
|
|
|
|
|
|
|
|
2437
|
0
|
0
|
|
|
|
0
|
my $iterator = $class->iterator or return $class->error("Cannot load next w/o iterator", "BOP-83"); |
2438
|
|
|
|
|
|
|
|
2439
|
0
|
|
|
|
|
0
|
return $class->load_all( |
2440
|
|
|
|
|
|
|
{ |
2441
|
|
|
|
|
|
|
'iterator' => 1, |
2442
|
|
|
|
|
|
|
'_loading_next' => 1, |
2443
|
|
|
|
|
|
|
%$clauses, |
2444
|
|
|
|
|
|
|
}, |
2445
|
|
|
|
|
|
|
@_ |
2446
|
|
|
|
|
|
|
); |
2447
|
|
|
|
|
|
|
}; |
2448
|
|
|
|
|
|
|
|
2449
|
|
|
|
|
|
|
=pod |
2450
|
|
|
|
|
|
|
|
2451
|
|
|
|
|
|
|
=begin btest(load_next) |
2452
|
|
|
|
|
|
|
|
2453
|
|
|
|
|
|
|
=end btest(load_next) |
2454
|
|
|
|
|
|
|
|
2455
|
|
|
|
|
|
|
=cut |
2456
|
|
|
|
|
|
|
|
2457
|
|
|
|
|
|
|
|
2458
|
|
|
|
|
|
|
=pod |
2459
|
|
|
|
|
|
|
|
2460
|
|
|
|
|
|
|
=item create |
2461
|
|
|
|
|
|
|
|
2462
|
|
|
|
|
|
|
Convenience method. Instantiates a brand new object and then immediately commits it to the |
2463
|
|
|
|
|
|
|
database. |
2464
|
|
|
|
|
|
|
|
2465
|
|
|
|
|
|
|
=cut |
2466
|
|
|
|
|
|
|
|
2467
|
|
|
|
|
|
|
=pod |
2468
|
|
|
|
|
|
|
|
2469
|
|
|
|
|
|
|
=begin btest(create) |
2470
|
|
|
|
|
|
|
|
2471
|
|
|
|
|
|
|
=end btest(create) |
2472
|
|
|
|
|
|
|
|
2473
|
|
|
|
|
|
|
=cut |
2474
|
|
|
|
|
|
|
|
2475
|
|
|
|
|
|
|
sub create { |
2476
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
2477
|
|
|
|
|
|
|
|
2478
|
0
|
0
|
|
|
|
0
|
my $self = $class->new(@_) or return; |
2479
|
|
|
|
|
|
|
|
2480
|
0
|
0
|
|
|
|
0
|
$self->commit or return $class->error($self->errvals); |
2481
|
|
|
|
|
|
|
|
2482
|
0
|
|
|
|
|
0
|
return $self; |
2483
|
|
|
|
|
|
|
} |
2484
|
|
|
|
|
|
|
|
2485
|
|
|
|
|
|
|
=pod |
2486
|
|
|
|
|
|
|
|
2487
|
|
|
|
|
|
|
=item load_all |
2488
|
|
|
|
|
|
|
|
2489
|
|
|
|
|
|
|
load_all loads all objects of a given package and returns them in an arrayref. |
2490
|
|
|
|
|
|
|
|
2491
|
|
|
|
|
|
|
my $objects = Some::Package->load_all(); |
2492
|
|
|
|
|
|
|
|
2493
|
|
|
|
|
|
|
load_all optionally takes an arbitrary number of arguments, where the first is a hashref that defines a set of constraints |
2494
|
|
|
|
|
|
|
and the rest are column values to bind to those constraints. |
2495
|
|
|
|
|
|
|
|
2496
|
|
|
|
|
|
|
my $objects = Some::Package->load_all( |
2497
|
|
|
|
|
|
|
{ |
2498
|
|
|
|
|
|
|
'where' => 'name = ? and company = ?', |
2499
|
|
|
|
|
|
|
'order by' => 'id' |
2500
|
|
|
|
|
|
|
}, |
2501
|
|
|
|
|
|
|
'Jim', 'FooFram' |
2502
|
|
|
|
|
|
|
); |
2503
|
|
|
|
|
|
|
|
2504
|
|
|
|
|
|
|
Will return an arrayref containing all objects with a name of "Jim" and a company of "FooFram" |
2505
|
|
|
|
|
|
|
|
2506
|
|
|
|
|
|
|
A list of all valid constraints is provided in the Basset::DB::Table object. |
2507
|
|
|
|
|
|
|
|
2508
|
|
|
|
|
|
|
Note that load_all is faster than loading objects individually, since it combines its SQL to minimize the number of queries. |
2509
|
|
|
|
|
|
|
However, all queries dones internally to auto-instantiated relationships will still be performed one at a time, and not in aggregate. |
2510
|
|
|
|
|
|
|
|
2511
|
|
|
|
|
|
|
B - with load_all, you are B to pass in actually column names, not aliases attribute names. You would pass in |
2512
|
|
|
|
|
|
|
aliased attribute names to load_where. |
2513
|
|
|
|
|
|
|
|
2514
|
|
|
|
|
|
|
Returns an empty arrayref if no objects found. |
2515
|
|
|
|
|
|
|
|
2516
|
|
|
|
|
|
|
The loader can also accept various 'flag' attributes passed in the constraints hash. The flags will not be passed onto the SQL generator. |
2517
|
|
|
|
|
|
|
|
2518
|
|
|
|
|
|
|
=over 8 |
2519
|
|
|
|
|
|
|
|
2520
|
|
|
|
|
|
|
=item iterator |
2521
|
|
|
|
|
|
|
|
2522
|
|
|
|
|
|
|
The iterator flag allows you to load up objects in sequence using load_next. |
2523
|
|
|
|
|
|
|
|
2524
|
|
|
|
|
|
|
my $objs = Some::Class->load_all(); |
2525
|
|
|
|
|
|
|
foreach my $o (@$objs) { |
2526
|
|
|
|
|
|
|
$o->do_something; |
2527
|
|
|
|
|
|
|
}; |
2528
|
|
|
|
|
|
|
|
2529
|
|
|
|
|
|
|
is equivalent to: |
2530
|
|
|
|
|
|
|
|
2531
|
|
|
|
|
|
|
Some::Class->load_all({'iterator' => 1}); |
2532
|
|
|
|
|
|
|
while (my $o = Some::Class->load_nex) { |
2533
|
|
|
|
|
|
|
$o->do_something; |
2534
|
|
|
|
|
|
|
}; |
2535
|
|
|
|
|
|
|
|
2536
|
|
|
|
|
|
|
The advantage is that you won't have all of the objects in memory at one time. Note that if you |
2537
|
|
|
|
|
|
|
subsequently call a load* method in the same class that you will wipe out the current iterator. |
2538
|
|
|
|
|
|
|
|
2539
|
|
|
|
|
|
|
=item constructor |
2540
|
|
|
|
|
|
|
|
2541
|
|
|
|
|
|
|
A hashref of constructor args. As data is loaded from the database, objects will be created and initialized with the data loaded. But |
2542
|
|
|
|
|
|
|
sometimes you need to load objects and populate in new values or override existing values with new ones. That's where the constructor |
2543
|
|
|
|
|
|
|
comes in. It will override the values of those attributes in the database with new ones. |
2544
|
|
|
|
|
|
|
|
2545
|
|
|
|
|
|
|
my $objs = Some::Class->load_all( |
2546
|
|
|
|
|
|
|
{ |
2547
|
|
|
|
|
|
|
'constructor' => { |
2548
|
|
|
|
|
|
|
'foo' => 'bar' |
2549
|
|
|
|
|
|
|
} |
2550
|
|
|
|
|
|
|
} |
2551
|
|
|
|
|
|
|
); |
2552
|
|
|
|
|
|
|
|
2553
|
|
|
|
|
|
|
Now all objects in $objs will have their foo attribute set to 'bar' |
2554
|
|
|
|
|
|
|
|
2555
|
|
|
|
|
|
|
=item singleton |
2556
|
|
|
|
|
|
|
|
2557
|
|
|
|
|
|
|
Sometimes, you build up a complicated query but know that you'll only get back one object. If you pass in the 'singleton' flag, then you'll |
2558
|
|
|
|
|
|
|
only get back a single object instead of an arrayref containing a single object. |
2559
|
|
|
|
|
|
|
|
2560
|
|
|
|
|
|
|
=item transform |
2561
|
|
|
|
|
|
|
|
2562
|
|
|
|
|
|
|
Will transform the loaded object into one of its related objects declared via a has_a or has_many |
2563
|
|
|
|
|
|
|
relationship. |
2564
|
|
|
|
|
|
|
|
2565
|
|
|
|
|
|
|
Some::User->has_a('pelican' => 'Some::Pelican'); |
2566
|
|
|
|
|
|
|
|
2567
|
|
|
|
|
|
|
my $pelican = Some::User->load_all({'where' => 'user_id = ?', 'transform' => 'pelican'}); |
2568
|
|
|
|
|
|
|
|
2569
|
|
|
|
|
|
|
Directly using this as a loader flag is dubious at best, it is most useful with relationships. |
2570
|
|
|
|
|
|
|
|
2571
|
|
|
|
|
|
|
=item force_arrayref |
2572
|
|
|
|
|
|
|
|
2573
|
|
|
|
|
|
|
There are several flags that will return the resutls of load_all in a different format (key or singleton, for example), |
2574
|
|
|
|
|
|
|
but this makes subclassing difficult. You can't easily override the load_all method, since you don't know what SUPER's implementation will return |
2575
|
|
|
|
|
|
|
to you. So you can pass the force_arrayref flag. That will return a list with the actual original arrayref first, and the value to return to the user second. |
2576
|
|
|
|
|
|
|
Along these lines: |
2577
|
|
|
|
|
|
|
|
2578
|
|
|
|
|
|
|
package Some::Subclass; |
2579
|
|
|
|
|
|
|
|
2580
|
|
|
|
|
|
|
sub load_all { |
2581
|
|
|
|
|
|
|
#not quite right...this wipes out the existing clauses hashref. |
2582
|
|
|
|
|
|
|
my ($values, $return) = shift->SUPER::load_all({'force_arrayref' => 1}, @_); |
2583
|
|
|
|
|
|
|
|
2584
|
|
|
|
|
|
|
foreach my $value (@$values) { |
2585
|
|
|
|
|
|
|
#do interesting thing; |
2586
|
|
|
|
|
|
|
} |
2587
|
|
|
|
|
|
|
|
2588
|
|
|
|
|
|
|
return $return; |
2589
|
|
|
|
|
|
|
} |
2590
|
|
|
|
|
|
|
|
2591
|
|
|
|
|
|
|
=back |
2592
|
|
|
|
|
|
|
|
2593
|
|
|
|
|
|
|
=cut |
2594
|
|
|
|
|
|
|
|
2595
|
|
|
|
|
|
|
=pod |
2596
|
|
|
|
|
|
|
|
2597
|
|
|
|
|
|
|
=begin btest(load_all) |
2598
|
|
|
|
|
|
|
|
2599
|
|
|
|
|
|
|
=end btest(load_all) |
2600
|
|
|
|
|
|
|
|
2601
|
|
|
|
|
|
|
=cut |
2602
|
|
|
|
|
|
|
|
2603
|
|
|
|
|
|
|
sub load_all { |
2604
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
2605
|
|
|
|
|
|
|
|
2606
|
0
|
|
|
|
|
0
|
my $clauses = {}; |
2607
|
0
|
|
|
|
|
0
|
my @args = (); |
2608
|
|
|
|
|
|
|
|
2609
|
0
|
0
|
|
|
|
0
|
if (@_){ |
2610
|
0
|
|
|
|
|
0
|
$clauses = shift; |
2611
|
0
|
|
|
|
|
0
|
@args = @_; |
2612
|
|
|
|
|
|
|
}; |
2613
|
|
|
|
|
|
|
|
2614
|
0
|
|
|
|
|
0
|
my $tables = $class->tables; |
2615
|
|
|
|
|
|
|
|
2616
|
0
|
|
|
|
|
0
|
my $omit_tables = undef; |
2617
|
|
|
|
|
|
|
|
2618
|
0
|
0
|
|
|
|
0
|
if ($clauses->{'tables'}) { |
2619
|
0
|
|
|
|
|
0
|
$tables = [@{$class->tables}, @{$clauses->{'tables'}}]; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
2620
|
0
|
|
|
|
|
0
|
$omit_tables = $clauses->{'tables'}; |
2621
|
0
|
|
|
|
|
0
|
delete $clauses->{'tables'}; |
2622
|
|
|
|
|
|
|
} |
2623
|
|
|
|
|
|
|
|
2624
|
0
|
0
|
|
|
|
0
|
return $class->error("Cannot load with no table", "BOP-01") unless @$tables; |
2625
|
|
|
|
|
|
|
|
2626
|
0
|
|
0
|
|
|
0
|
my $iterated = $clauses->{'iterator'} || 0; |
2627
|
0
|
|
|
|
|
0
|
delete $clauses->{'iterator'}; |
2628
|
|
|
|
|
|
|
|
2629
|
0
|
0
|
|
|
|
0
|
my $tableClass = $class->pkg_for_type('table') or return; |
2630
|
|
|
|
|
|
|
|
2631
|
0
|
0
|
|
|
|
0
|
my $multiselect_query = $tableClass->multiselect_query( |
2632
|
|
|
|
|
|
|
'tables' => $tables, |
2633
|
|
|
|
|
|
|
'omit_columns_from_tables' => $omit_tables, |
2634
|
|
|
|
|
|
|
'use_aliases' => 1, |
2635
|
|
|
|
|
|
|
) or return $class->error($tableClass->errvals); |
2636
|
|
|
|
|
|
|
|
2637
|
0
|
0
|
|
|
|
0
|
my $query = $tableClass->attach_to_query( |
2638
|
|
|
|
|
|
|
$multiselect_query, |
2639
|
|
|
|
|
|
|
$clauses |
2640
|
|
|
|
|
|
|
) or return $class->error($tableClass->errvals); |
2641
|
|
|
|
|
|
|
|
2642
|
0
|
0
|
|
|
|
0
|
$class->iterator(undef) unless $clauses->{'_loading_next'}; |
2643
|
|
|
|
|
|
|
|
2644
|
0
|
0
|
0
|
|
|
0
|
my $stmt = $class->iterator || $class->arbitrary_sql( |
2645
|
|
|
|
|
|
|
'query' => $query, |
2646
|
|
|
|
|
|
|
'vars' => \@args, |
2647
|
|
|
|
|
|
|
'iterator' => 1, |
2648
|
|
|
|
|
|
|
) or return; |
2649
|
|
|
|
|
|
|
|
2650
|
0
|
|
|
|
|
0
|
my @objs = (); |
2651
|
|
|
|
|
|
|
|
2652
|
0
|
0
|
0
|
|
|
0
|
if ($iterated && ! $class->iterator) { |
2653
|
0
|
|
|
|
|
0
|
$class->iterator($stmt); |
2654
|
0
|
|
|
|
|
0
|
return $stmt; |
2655
|
|
|
|
|
|
|
} |
2656
|
|
|
|
|
|
|
|
2657
|
0
|
|
|
|
|
0
|
my $load_cache = $class->central_load_cache; |
2658
|
|
|
|
|
|
|
|
2659
|
0
|
|
|
|
|
0
|
while (my $stuff = $stmt->fetchrow_hashref('NAME_lc')){ |
2660
|
|
|
|
|
|
|
|
2661
|
0
|
0
|
|
|
|
0
|
my $obj = $class->new('loading' => 1, 'in_db' => 1, %$stuff, %{$clauses->{'constructor'}}, 'loaded' => 1) |
|
0
|
|
|
|
|
0
|
|
2662
|
|
|
|
|
|
|
or return $class->error("Cannot create object : " . $class->error, "BOP-06"); |
2663
|
0
|
|
|
|
|
0
|
$obj->loading(0); |
2664
|
|
|
|
|
|
|
|
2665
|
0
|
|
|
|
|
0
|
my $primary_identifier = $obj->primary_identifier('string'); |
2666
|
|
|
|
|
|
|
|
2667
|
0
|
0
|
|
|
|
0
|
if (defined $load_cache->{$primary_identifier}) { |
2668
|
0
|
|
|
|
|
0
|
$obj = $load_cache->{$primary_identifier}; |
2669
|
|
|
|
|
|
|
} |
2670
|
|
|
|
|
|
|
else { |
2671
|
|
|
|
|
|
|
|
2672
|
0
|
|
|
|
|
0
|
$load_cache->{$primary_identifier} = $obj; |
2673
|
0
|
|
|
|
|
0
|
weaken($load_cache->{$primary_identifier}); |
2674
|
|
|
|
|
|
|
|
2675
|
0
|
0
|
0
|
|
|
0
|
$obj->setup() or return $class->error("Setup failed in object : " . $obj->error, $obj->errcode || "BOP-47"); |
2676
|
|
|
|
|
|
|
} |
2677
|
|
|
|
|
|
|
|
2678
|
|
|
|
|
|
|
#no matter what, we nuke our instantiated relationships, they can no longer be trusted. |
2679
|
0
|
|
|
|
|
0
|
$obj->instantiated_relationships({}); |
2680
|
|
|
|
|
|
|
|
2681
|
0
|
0
|
|
|
|
0
|
if (my $transform = $clauses->{'transform'}) { |
2682
|
0
|
|
|
|
|
0
|
my $transformed = $obj->$transform(); |
2683
|
0
|
0
|
0
|
|
|
0
|
return $class->error("Cannot transform object into non-object", "BOP-91") |
2684
|
|
|
|
|
|
|
unless $obj->is_relationship($transform) && ref $transformed; |
2685
|
0
|
|
|
|
|
0
|
$obj = $transformed; |
2686
|
|
|
|
|
|
|
}; |
2687
|
|
|
|
|
|
|
|
2688
|
0
|
|
|
|
|
0
|
push @objs, $obj; |
2689
|
|
|
|
|
|
|
|
2690
|
0
|
0
|
|
|
|
0
|
if ($iterated) { |
2691
|
0
|
|
|
|
|
0
|
return $obj; |
2692
|
|
|
|
|
|
|
}; |
2693
|
|
|
|
|
|
|
}; |
2694
|
|
|
|
|
|
|
|
2695
|
0
|
0
|
|
|
|
0
|
$stmt->finish() |
2696
|
|
|
|
|
|
|
or return $class->error($stmt->errstr, "BOP-10"); |
2697
|
|
|
|
|
|
|
|
2698
|
0
|
0
|
0
|
|
|
0
|
if ($iterated && ! @objs) { |
2699
|
0
|
|
|
|
|
0
|
$class->iterator(undef); |
2700
|
0
|
|
|
|
|
0
|
return; |
2701
|
|
|
|
|
|
|
}; |
2702
|
|
|
|
|
|
|
|
2703
|
0
|
0
|
|
|
|
0
|
if ($clauses->{'singleton'}) { |
2704
|
0
|
0
|
|
|
|
0
|
my $return = $objs[0] or return $class->error("Cannot load single object - no objects returned", "BOP-84"); |
2705
|
0
|
0
|
|
|
|
0
|
return $clauses->{'force_arrayref'} ? (\@objs, $return) : $return; |
2706
|
|
|
|
|
|
|
} |
2707
|
|
|
|
|
|
|
else { |
2708
|
0
|
|
|
|
|
0
|
my $return; |
2709
|
0
|
0
|
|
|
|
0
|
if (my $key = $clauses->{'key'}) { |
2710
|
0
|
|
|
|
|
0
|
my %objs = map {$_->$key(), $_} @objs; |
|
0
|
|
|
|
|
0
|
|
2711
|
0
|
|
|
|
|
0
|
$return = \%objs; |
2712
|
|
|
|
|
|
|
} else { |
2713
|
0
|
|
|
|
|
0
|
$return = \@objs; |
2714
|
|
|
|
|
|
|
} |
2715
|
0
|
0
|
|
|
|
0
|
return $clauses->{'force_arrayref'} ? (\@objs, $return) : $return; |
2716
|
|
|
|
|
|
|
} |
2717
|
|
|
|
|
|
|
|
2718
|
|
|
|
|
|
|
}; |
2719
|
|
|
|
|
|
|
|
2720
|
|
|
|
|
|
|
=pod |
2721
|
|
|
|
|
|
|
|
2722
|
|
|
|
|
|
|
=item exists |
2723
|
|
|
|
|
|
|
|
2724
|
|
|
|
|
|
|
Query to quickly determine if a given object (or set of objects) exists in the database. The objects will not be loaded. |
2725
|
|
|
|
|
|
|
Returns a count of the number of objects that exist. |
2726
|
|
|
|
|
|
|
|
2727
|
|
|
|
|
|
|
my $itsthere = Basset::User->exists(1); #user id 1 exists in the database |
2728
|
|
|
|
|
|
|
|
2729
|
|
|
|
|
|
|
=cut |
2730
|
|
|
|
|
|
|
|
2731
|
|
|
|
|
|
|
=pod |
2732
|
|
|
|
|
|
|
|
2733
|
|
|
|
|
|
|
=begin btest(exists) |
2734
|
|
|
|
|
|
|
|
2735
|
|
|
|
|
|
|
=end btest(exists) |
2736
|
|
|
|
|
|
|
|
2737
|
|
|
|
|
|
|
=cut |
2738
|
|
|
|
|
|
|
|
2739
|
|
|
|
|
|
|
sub exists { |
2740
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
2741
|
|
|
|
|
|
|
|
2742
|
0
|
0
|
|
|
|
0
|
my $table = $class->primary_table or return $class->error("Cannot load with no table", "BOP-01"); |
2743
|
|
|
|
|
|
|
|
2744
|
|
|
|
|
|
|
#our default where clause - built on the primary keys |
2745
|
0
|
|
|
|
|
0
|
my $where = join(' and ', map {"$_ = ?"} $table->primary_cols); |
|
0
|
|
|
|
|
0
|
|
2746
|
|
|
|
|
|
|
|
2747
|
0
|
|
|
|
|
0
|
my @args = @_; |
2748
|
|
|
|
|
|
|
#if we have arguments, our clause should be on the primary key. No clause otherwise. |
2749
|
0
|
0
|
|
|
|
0
|
my $clauses = @args ? {'where' => $where} : {}; |
2750
|
|
|
|
|
|
|
|
2751
|
|
|
|
|
|
|
#override the clause with any passed clause |
2752
|
0
|
0
|
|
|
|
0
|
if (ref $args[0] eq 'HASH'){ |
2753
|
0
|
|
|
|
|
0
|
$clauses = shift @args; |
2754
|
|
|
|
|
|
|
}; |
2755
|
|
|
|
|
|
|
|
2756
|
0
|
0
|
|
|
|
0
|
my $query = $table->attach_to_query( |
2757
|
|
|
|
|
|
|
$table->count_query, |
2758
|
|
|
|
|
|
|
$clauses |
2759
|
|
|
|
|
|
|
) or return $class->error($table->errvals); |
2760
|
|
|
|
|
|
|
|
2761
|
0
|
0
|
|
|
|
0
|
my $data = $class->arbitrary_sql( |
2762
|
|
|
|
|
|
|
'query' => $query, |
2763
|
|
|
|
|
|
|
'vars' => [@args], |
2764
|
|
|
|
|
|
|
'into' => 'hash', |
2765
|
|
|
|
|
|
|
) or return; |
2766
|
|
|
|
|
|
|
|
2767
|
0
|
|
0
|
|
|
0
|
return $data->[0]->{'count'} || 0; |
2768
|
|
|
|
|
|
|
|
2769
|
|
|
|
|
|
|
}; |
2770
|
|
|
|
|
|
|
|
2771
|
|
|
|
|
|
|
=pod |
2772
|
|
|
|
|
|
|
|
2773
|
|
|
|
|
|
|
=item delete |
2774
|
|
|
|
|
|
|
|
2775
|
|
|
|
|
|
|
This will delete an object from the database |
2776
|
|
|
|
|
|
|
|
2777
|
|
|
|
|
|
|
$object->delete(); |
2778
|
|
|
|
|
|
|
|
2779
|
|
|
|
|
|
|
The object itself will not be affected, except for the fact that its deleted flag will be set. |
2780
|
|
|
|
|
|
|
|
2781
|
|
|
|
|
|
|
=cut |
2782
|
|
|
|
|
|
|
|
2783
|
|
|
|
|
|
|
=pod |
2784
|
|
|
|
|
|
|
|
2785
|
|
|
|
|
|
|
=begin btest(delete) |
2786
|
|
|
|
|
|
|
|
2787
|
|
|
|
|
|
|
=end btest(delete) |
2788
|
|
|
|
|
|
|
|
2789
|
|
|
|
|
|
|
=cut |
2790
|
|
|
|
|
|
|
|
2791
|
|
|
|
|
|
|
sub delete { |
2792
|
|
|
|
|
|
|
|
2793
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
2794
|
|
|
|
|
|
|
|
2795
|
|
|
|
|
|
|
#if we haven't loaded the object, we have nothing to delete, so we just pretend |
2796
|
0
|
0
|
|
|
|
0
|
unless ($self->in_db) { |
2797
|
0
|
|
|
|
|
0
|
$self->deleted(1); |
2798
|
0
|
|
|
|
|
0
|
return $self; |
2799
|
|
|
|
|
|
|
}; |
2800
|
|
|
|
|
|
|
|
2801
|
0
|
0
|
0
|
|
|
0
|
if ($self->tied_to_parent && ! $self->should_be_deleted) { |
2802
|
0
|
|
|
|
|
0
|
return $self; |
2803
|
|
|
|
|
|
|
}; |
2804
|
|
|
|
|
|
|
|
2805
|
0
|
|
|
|
|
0
|
$self->deleting(1); |
2806
|
|
|
|
|
|
|
|
2807
|
0
|
0
|
|
|
|
0
|
my $table = $self->primary_table or return $self->error("Cannot delete with no table", "BOP-01"); |
2808
|
|
|
|
|
|
|
|
2809
|
0
|
0
|
|
|
|
0
|
$self->begin() or return; |
2810
|
|
|
|
|
|
|
|
2811
|
0
|
|
|
|
|
0
|
my $query = $table->attach_to_query( |
2812
|
|
|
|
|
|
|
$table->delete_query(), |
2813
|
|
|
|
|
|
|
{ |
2814
|
0
|
0
|
|
|
|
0
|
'where' => join(' and ', map {"$_ = ?"} $table->primary_cols) |
2815
|
|
|
|
|
|
|
} |
2816
|
|
|
|
|
|
|
) or return $self->error($table->errvals); |
2817
|
|
|
|
|
|
|
|
2818
|
0
|
0
|
|
|
|
0
|
my @values = map {$self->$_()} $table->alias_column($table->delete_bindables) or return; |
|
0
|
|
|
|
|
0
|
|
2819
|
|
|
|
|
|
|
|
2820
|
0
|
0
|
|
|
|
0
|
$self->arbitrary_sql( |
2821
|
|
|
|
|
|
|
'query' => $query, |
2822
|
|
|
|
|
|
|
'vars' => \@values, |
2823
|
|
|
|
|
|
|
'table' => $table, |
2824
|
|
|
|
|
|
|
'cols' => [$table->delete_bindables] |
2825
|
|
|
|
|
|
|
) or return; |
2826
|
|
|
|
|
|
|
|
2827
|
0
|
0
|
|
|
|
0
|
$self->delete_relationships or return; |
2828
|
|
|
|
|
|
|
|
2829
|
0
|
0
|
|
|
|
0
|
$self->end or return; |
2830
|
|
|
|
|
|
|
|
2831
|
0
|
|
|
|
|
0
|
$self->deleting(0); |
2832
|
|
|
|
|
|
|
|
2833
|
0
|
|
|
|
|
0
|
$self->deleted(1); |
2834
|
|
|
|
|
|
|
|
2835
|
0
|
|
|
|
|
0
|
return $self; |
2836
|
|
|
|
|
|
|
|
2837
|
|
|
|
|
|
|
}; |
2838
|
|
|
|
|
|
|
|
2839
|
|
|
|
|
|
|
|
2840
|
|
|
|
|
|
|
=pod |
2841
|
|
|
|
|
|
|
|
2842
|
|
|
|
|
|
|
=item load_where |
2843
|
|
|
|
|
|
|
|
2844
|
|
|
|
|
|
|
Simple wrapper around load_all. Takes key/value pairs. |
2845
|
|
|
|
|
|
|
|
2846
|
|
|
|
|
|
|
my $users = Some::Class->load_where( |
2847
|
|
|
|
|
|
|
'user' => 3, |
2848
|
|
|
|
|
|
|
'location' => 'mountains', |
2849
|
|
|
|
|
|
|
'weather' => 'sunny' |
2850
|
|
|
|
|
|
|
); |
2851
|
|
|
|
|
|
|
|
2852
|
|
|
|
|
|
|
This is exactly equivalent to: |
2853
|
|
|
|
|
|
|
|
2854
|
|
|
|
|
|
|
my $users = Some::Class->load_all( |
2855
|
|
|
|
|
|
|
{ |
2856
|
|
|
|
|
|
|
'where' => 'user = ? and location = ? and weather = ?' |
2857
|
|
|
|
|
|
|
}, |
2858
|
|
|
|
|
|
|
3, 'mountains', 'sunny' |
2859
|
|
|
|
|
|
|
); |
2860
|
|
|
|
|
|
|
|
2861
|
|
|
|
|
|
|
It just looks prettier and hides more of the SQL. |
2862
|
|
|
|
|
|
|
|
2863
|
|
|
|
|
|
|
Even better, you can also stick in an array for multiple value loads. |
2864
|
|
|
|
|
|
|
|
2865
|
|
|
|
|
|
|
my $users = Some::Class->load_where( |
2866
|
|
|
|
|
|
|
'state' => 'PA', |
2867
|
|
|
|
|
|
|
'last_name' => [qw(Smith Jones Johnson)] |
2868
|
|
|
|
|
|
|
); |
2869
|
|
|
|
|
|
|
|
2870
|
|
|
|
|
|
|
Is exactly the same as: |
2871
|
|
|
|
|
|
|
|
2872
|
|
|
|
|
|
|
my $users = Some::Class->load_all( |
2873
|
|
|
|
|
|
|
{ |
2874
|
|
|
|
|
|
|
'where' => 'last_name in (?,?,?) and state = ?' |
2875
|
|
|
|
|
|
|
}, |
2876
|
|
|
|
|
|
|
qw(Smith Jones Johnson), 'PA', |
2877
|
|
|
|
|
|
|
); |
2878
|
|
|
|
|
|
|
|
2879
|
|
|
|
|
|
|
There is an alternative syntax, you may pass in one arrayref and one hashref. The arrayref becomes your |
2880
|
|
|
|
|
|
|
where clause, the second contains additional loader args (such as 'order by', 'limit', etc.) |
2881
|
|
|
|
|
|
|
|
2882
|
|
|
|
|
|
|
my $users = Some::Class->load_where( |
2883
|
|
|
|
|
|
|
#where array |
2884
|
|
|
|
|
|
|
[ |
2885
|
|
|
|
|
|
|
'state' => 'PA', |
2886
|
|
|
|
|
|
|
'last_name' => [qw(Smith Jones Johnson)] |
2887
|
|
|
|
|
|
|
], |
2888
|
|
|
|
|
|
|
#extra loader hash |
2889
|
|
|
|
|
|
|
{ |
2890
|
|
|
|
|
|
|
'order by' => 'state desc', |
2891
|
|
|
|
|
|
|
}, |
2892
|
|
|
|
|
|
|
); |
2893
|
|
|
|
|
|
|
|
2894
|
|
|
|
|
|
|
Is exactly the same as: |
2895
|
|
|
|
|
|
|
|
2896
|
|
|
|
|
|
|
my $users = Some::Class->load_all( |
2897
|
|
|
|
|
|
|
{ |
2898
|
|
|
|
|
|
|
'where' => 'last_name in (?,?,?) and state = ?', |
2899
|
|
|
|
|
|
|
'order by' => state desc', |
2900
|
|
|
|
|
|
|
}, |
2901
|
|
|
|
|
|
|
qw(Smith Jones Johnson), 'PA', |
2902
|
|
|
|
|
|
|
); |
2903
|
|
|
|
|
|
|
|
2904
|
|
|
|
|
|
|
|
2905
|
|
|
|
|
|
|
=cut |
2906
|
|
|
|
|
|
|
|
2907
|
|
|
|
|
|
|
sub load_where { |
2908
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
2909
|
|
|
|
|
|
|
|
2910
|
0
|
0
|
|
|
|
0
|
my @clauses = @_ or return $class->error("Cannot load_where w/o clauses", "BOP-68"); |
2911
|
|
|
|
|
|
|
|
2912
|
0
|
|
|
|
|
0
|
my $additional_clauses = {}; |
2913
|
|
|
|
|
|
|
|
2914
|
0
|
0
|
|
|
|
0
|
if (ref $clauses[0] eq 'HASH') { |
2915
|
0
|
|
|
|
|
0
|
$class->notify('warnings', 'load_where with a hashref argument is deprecated. Please load with an array instead.'); |
2916
|
0
|
|
|
|
|
0
|
$clauses[0] = [%{$clauses[0]}]; |
|
0
|
|
|
|
|
0
|
|
2917
|
|
|
|
|
|
|
}; |
2918
|
|
|
|
|
|
|
|
2919
|
0
|
0
|
|
|
|
0
|
if (ref $clauses[0] eq 'ARRAY') { |
2920
|
0
|
0
|
|
|
|
0
|
$additional_clauses = @clauses == 2 ? pop @clauses : {}; #last one is additional clauses |
2921
|
0
|
|
|
|
|
0
|
@clauses = @{$clauses[0]}; |
|
0
|
|
|
|
|
0
|
|
2922
|
|
|
|
|
|
|
} |
2923
|
|
|
|
|
|
|
|
2924
|
0
|
|
|
|
|
0
|
my ($clause, @values) = $class->primary_table->construct_where_clause( |
2925
|
0
|
0
|
|
|
|
0
|
[@{$class->tables}, $additional_clauses->{'tables'} ? @{$additional_clauses->{'tables'}} : ()], |
|
0
|
|
|
|
|
0
|
|
2926
|
|
|
|
|
|
|
@clauses |
2927
|
|
|
|
|
|
|
); |
2928
|
|
|
|
|
|
|
|
2929
|
0
|
0
|
|
|
|
0
|
return $class->error($class->primary_table->errvals) unless defined $clause; |
2930
|
|
|
|
|
|
|
|
2931
|
0
|
|
|
|
|
0
|
return $class->load_all({%$additional_clauses, 'where' => $clause}, @values); |
2932
|
|
|
|
|
|
|
|
2933
|
|
|
|
|
|
|
}; |
2934
|
|
|
|
|
|
|
|
2935
|
|
|
|
|
|
|
=pod |
2936
|
|
|
|
|
|
|
|
2937
|
|
|
|
|
|
|
=begin btest(load_where) |
2938
|
|
|
|
|
|
|
|
2939
|
|
|
|
|
|
|
=end btest(load_where) |
2940
|
|
|
|
|
|
|
|
2941
|
|
|
|
|
|
|
=cut |
2942
|
|
|
|
|
|
|
|
2943
|
|
|
|
|
|
|
=pod |
2944
|
|
|
|
|
|
|
|
2945
|
|
|
|
|
|
|
=item load_one_where |
2946
|
|
|
|
|
|
|
|
2947
|
|
|
|
|
|
|
convenience method. Simply wrappers a load_where call while passing the singleton parameter |
2948
|
|
|
|
|
|
|
|
2949
|
|
|
|
|
|
|
=cut |
2950
|
|
|
|
|
|
|
|
2951
|
|
|
|
|
|
|
sub load_one_where { |
2952
|
0
|
|
|
0
|
1
|
0
|
my $class = shift; |
2953
|
0
|
0
|
0
|
|
|
0
|
if (ref $_[0] && ref $_[1] eq 'HASH') { |
2954
|
0
|
|
|
|
|
0
|
$_[1]->{'singleton'} = 1; |
2955
|
0
|
|
|
|
|
0
|
return $class->load_where(@_); |
2956
|
|
|
|
|
|
|
} |
2957
|
|
|
|
|
|
|
else { |
2958
|
0
|
|
|
|
|
0
|
return $class->load_where(\@_, {'singleton' => 1}); |
2959
|
|
|
|
|
|
|
}; |
2960
|
|
|
|
|
|
|
} |
2961
|
|
|
|
|
|
|
|
2962
|
|
|
|
|
|
|
=pod |
2963
|
|
|
|
|
|
|
|
2964
|
|
|
|
|
|
|
=begin btest(load_one_where) |
2965
|
|
|
|
|
|
|
|
2966
|
|
|
|
|
|
|
=end btest(load_one_where) |
2967
|
|
|
|
|
|
|
|
2968
|
|
|
|
|
|
|
=cut |
2969
|
|
|
|
|
|
|
|
2970
|
|
|
|
|
|
|
=pod |
2971
|
|
|
|
|
|
|
|
2972
|
|
|
|
|
|
|
=item arbitrary_sql |
2973
|
|
|
|
|
|
|
|
2974
|
|
|
|
|
|
|
The arbitrary_sql method does what it sounds like, it executes arbitrary sql code. You're expected |
2975
|
|
|
|
|
|
|
to pass at least one parameter: |
2976
|
|
|
|
|
|
|
|
2977
|
|
|
|
|
|
|
query => 'some sql query'; #such as select col1, col2 from table1 |
2978
|
|
|
|
|
|
|
|
2979
|
|
|
|
|
|
|
If you want to bind any variables to the query, put them in the vars parameter: |
2980
|
|
|
|
|
|
|
|
2981
|
|
|
|
|
|
|
query => 'select count(*) from table where id = ?', |
2982
|
|
|
|
|
|
|
vars => '7' |
2983
|
|
|
|
|
|
|
|
2984
|
|
|
|
|
|
|
Normally, you'd pass in an arrayref to vars, but if it's just one, you can skip it |
2985
|
|
|
|
|
|
|
|
2986
|
|
|
|
|
|
|
vars => '7' |
2987
|
|
|
|
|
|
|
or |
2988
|
|
|
|
|
|
|
vars => ['7'] |
2989
|
|
|
|
|
|
|
|
2990
|
|
|
|
|
|
|
query => 'select count(*) from table where id = ? and type = ?', |
2991
|
|
|
|
|
|
|
vars => ['7', 'animal'] |
2992
|
|
|
|
|
|
|
|
2993
|
|
|
|
|
|
|
Binding is done without SQL types, unless you pass in a Basset::DB::Table object and the columns as well, which contains the column types: |
2994
|
|
|
|
|
|
|
|
2995
|
|
|
|
|
|
|
my $t = Basset::DB::Table->new( {table definitions} ); |
2996
|
|
|
|
|
|
|
table => $t |
2997
|
|
|
|
|
|
|
cols => ['id', 'type'] |
2998
|
|
|
|
|
|
|
|
2999
|
|
|
|
|
|
|
Insertion queries (insert, update, etc.) will return 1 upon success |
3000
|
|
|
|
|
|
|
|
3001
|
|
|
|
|
|
|
If you're running a select, show, set, or desc query, then you end up loading data. It will always be returned in an arrayref containing |
3002
|
|
|
|
|
|
|
the rows. Normally, each row is a hashref, loaded with the ->fetchrow_hashref method from DBI. You can also choose to load |
3003
|
|
|
|
|
|
|
into an array, then pass in into: |
3004
|
|
|
|
|
|
|
|
3005
|
|
|
|
|
|
|
'into' => 'array' |
3006
|
|
|
|
|
|
|
|
3007
|
|
|
|
|
|
|
If you pass anything other than 'into' => 'array', then 'into' => 'hash' is assumed. |
3008
|
|
|
|
|
|
|
|
3009
|
|
|
|
|
|
|
my $data = $class->arbitrary_sql( |
3010
|
|
|
|
|
|
|
'query' => 'select id, name from names where id in (?, ?) and name in (?, ?)', |
3011
|
|
|
|
|
|
|
'vars' => [qw(7 8 Jim Koka)], |
3012
|
|
|
|
|
|
|
); |
3013
|
|
|
|
|
|
|
|
3014
|
|
|
|
|
|
|
foreach my $h (@$data){ |
3015
|
|
|
|
|
|
|
print {$_->{id} . " : " . $_->name . "\n"} sort keys %$h; |
3016
|
|
|
|
|
|
|
}; |
3017
|
|
|
|
|
|
|
|
3018
|
|
|
|
|
|
|
Alternatively, if you're memory conscious, you can pass in the 'iterator' flag. This will return the actual executed statement handle, |
3019
|
|
|
|
|
|
|
so you can call fetchrow_array, fetchrow_hashref, etc. on it yourself. |
3020
|
|
|
|
|
|
|
|
3021
|
|
|
|
|
|
|
my $sth = $class->arbitrary_sql( |
3022
|
|
|
|
|
|
|
'query' => 'select id, name from names where id in (?, ?) and name in (?, ?)', |
3023
|
|
|
|
|
|
|
'vars' => [qw(7 8 Jim Koka)], |
3024
|
|
|
|
|
|
|
'iterator' => 1, |
3025
|
|
|
|
|
|
|
); |
3026
|
|
|
|
|
|
|
|
3027
|
|
|
|
|
|
|
Another example: |
3028
|
|
|
|
|
|
|
|
3029
|
|
|
|
|
|
|
my $rc = $class->arbitrary_sql( |
3030
|
|
|
|
|
|
|
'query' => 'insert into names (id, name) values (?,?)', |
3031
|
|
|
|
|
|
|
'vars' => ['18', 'Jim 3'], |
3032
|
|
|
|
|
|
|
'table => $names_table, |
3033
|
|
|
|
|
|
|
'cols' => [qw(id name)] |
3034
|
|
|
|
|
|
|
); |
3035
|
|
|
|
|
|
|
|
3036
|
|
|
|
|
|
|
# $rc == 1 |
3037
|
|
|
|
|
|
|
|
3038
|
|
|
|
|
|
|
=cut |
3039
|
|
|
|
|
|
|
|
3040
|
|
|
|
|
|
|
=pod |
3041
|
|
|
|
|
|
|
|
3042
|
|
|
|
|
|
|
=begin btest(arbitrary_sql) |
3043
|
|
|
|
|
|
|
|
3044
|
|
|
|
|
|
|
=end btest(arbitrary_sql) |
3045
|
|
|
|
|
|
|
|
3046
|
|
|
|
|
|
|
=cut |
3047
|
|
|
|
|
|
|
|
3048
|
|
|
|
|
|
|
sub arbitrary_sql { |
3049
|
|
|
|
|
|
|
|
3050
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
3051
|
|
|
|
|
|
|
|
3052
|
0
|
|
|
|
|
0
|
my %init = @_; |
3053
|
|
|
|
|
|
|
|
3054
|
0
|
0
|
0
|
|
|
0
|
return $self->error("Cannot execute arbitrary SQL w/o SQL", "BOP-38") |
3055
|
|
|
|
|
|
|
unless $init{'query'} || $init{'stmt'}; |
3056
|
|
|
|
|
|
|
|
3057
|
|
|
|
|
|
|
# table and cols are used to bind a column to a particular type, so you either need to provide both of them |
3058
|
|
|
|
|
|
|
# or neither |
3059
|
0
|
0
|
0
|
|
|
0
|
return $self->error("Cannot use table w/o cols", "BOP-39") |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
3060
|
|
|
|
|
|
|
if (($init{'table'} && ! $init{'cols'}) || (! $init{'table'} && $init{'cols'})); |
3061
|
|
|
|
|
|
|
|
3062
|
|
|
|
|
|
|
#assume that we want a hash, if nothing's passed |
3063
|
0
|
|
0
|
|
|
0
|
$init{'into'} ||= 'hash'; #default to a hash |
3064
|
|
|
|
|
|
|
|
3065
|
0
|
0
|
0
|
|
|
0
|
my $driver = $init{'driver'} || $self->driver or return; |
3066
|
|
|
|
|
|
|
|
3067
|
|
|
|
|
|
|
#certain queries return stuff. If so, grab it. |
3068
|
0
|
|
|
|
|
0
|
my $arbitrary_selectables = $self->arbitrary_selectables(); |
3069
|
|
|
|
|
|
|
|
3070
|
0
|
|
|
|
|
0
|
my $selecting_query = 0; |
3071
|
|
|
|
|
|
|
|
3072
|
0
|
0
|
0
|
|
|
0
|
if ($init{'selecting_query'} || $init{'query'} =~ /^\s*$arbitrary_selectables/i) { |
3073
|
0
|
|
|
|
|
0
|
$selecting_query = 1; |
3074
|
|
|
|
|
|
|
} |
3075
|
|
|
|
|
|
|
|
3076
|
0
|
0
|
|
|
|
0
|
$self->begin() or return; |
3077
|
|
|
|
|
|
|
|
3078
|
0
|
0
|
|
|
|
0
|
my $errormethod = $selecting_query ? 'error' : 'fatalerror'; |
3079
|
|
|
|
|
|
|
|
3080
|
0
|
0
|
0
|
|
|
0
|
my $stmt = $init{'stmt'} || $driver->prepare_cached($init{'query'}) |
3081
|
|
|
|
|
|
|
or return $self->$errormethod($driver->errstr(), "BOP-05"); |
3082
|
|
|
|
|
|
|
|
3083
|
|
|
|
|
|
|
#if we have vars, then we're binding |
3084
|
0
|
0
|
|
|
|
0
|
if ($init{'vars'}){ |
3085
|
|
|
|
|
|
|
#allow the user to pass in a single value as a scalar, not in an arrayref |
3086
|
0
|
0
|
|
|
|
0
|
$init{'vars'} = [$init{'vars'}] unless ref $init{'vars'}; |
3087
|
|
|
|
|
|
|
|
3088
|
|
|
|
|
|
|
# my $place = 0; |
3089
|
|
|
|
|
|
|
|
3090
|
|
|
|
|
|
|
#bind our places. If we have table and cols, then we know the type to bind to. Otherwise, use undef. |
3091
|
|
|
|
|
|
|
# foreach my $col (@{$init{'vars'}}) { |
3092
|
0
|
|
|
|
|
0
|
my $max = @{$init{'vars'}}; |
|
0
|
|
|
|
|
0
|
|
3093
|
0
|
0
|
|
|
|
0
|
my $definition = $init{'table'}->definition if $init{'table'}; |
3094
|
0
|
0
|
|
|
|
0
|
$self->notify('debug', $init{'query'} . "\nVARS: " . join(', ', map {defined($_) ? $_ : 'NULL'} @{$init{'vars'}})); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
3095
|
0
|
|
|
|
|
0
|
for (my $place = 0; $place < $max; $place++) { |
3096
|
|
|
|
|
|
|
#$self->notify('debug', $init{'vars'}->[$place]); |
3097
|
0
|
0
|
|
|
|
0
|
$stmt->bind_param( |
|
|
0
|
|
|
|
|
|
3098
|
|
|
|
|
|
|
$place + 1, #place |
3099
|
|
|
|
|
|
|
$init{'vars'}->[$place], #value |
3100
|
|
|
|
|
|
|
$init{'table'} #sql type if we have a table, undef otherwise |
3101
|
|
|
|
|
|
|
? $driver->sql_type($definition->{$init{'cols'}->[$place]}) |
3102
|
|
|
|
|
|
|
: undef |
3103
|
|
|
|
|
|
|
) or return $self->$errormethod($stmt->errstr, "BOP-03"); |
3104
|
|
|
|
|
|
|
# $place++; |
3105
|
|
|
|
|
|
|
}; |
3106
|
|
|
|
|
|
|
} else { |
3107
|
|
|
|
|
|
|
#otherwise, just notify with the query |
3108
|
0
|
|
|
|
|
0
|
$self->notify('debug', $init{'query'}); |
3109
|
|
|
|
|
|
|
} |
3110
|
|
|
|
|
|
|
|
3111
|
0
|
0
|
|
|
|
0
|
$stmt->execute() or return $self->$errormethod($stmt->errstr, "BOP-04"); |
3112
|
|
|
|
|
|
|
|
3113
|
0
|
0
|
|
|
|
0
|
$self->end() or return; |
3114
|
|
|
|
|
|
|
|
3115
|
0
|
0
|
|
|
|
0
|
return $stmt if $init{'iterator'};# && $selecting_query; |
3116
|
|
|
|
|
|
|
|
3117
|
0
|
0
|
|
|
|
0
|
if ($selecting_query){ |
3118
|
|
|
|
|
|
|
|
3119
|
0
|
|
|
|
|
0
|
my @data = (); |
3120
|
|
|
|
|
|
|
|
3121
|
|
|
|
|
|
|
#into determines our fetchmethod |
3122
|
0
|
0
|
|
|
|
0
|
my $fetchmethod = $init{'into'} =~ /^array$/i ? 'fetchrow_arrayref' : 'fetchrow_hashref'; #default to hashes |
3123
|
|
|
|
|
|
|
|
3124
|
0
|
|
|
|
|
0
|
while (my $stuff = $stmt->$fetchmethod()){ |
3125
|
|
|
|
|
|
|
#push @data, $stuff; |
3126
|
0
|
0
|
|
|
|
0
|
if ($fetchmethod eq 'fetchrow_hashref'){ |
3127
|
|
|
|
|
|
|
# $stuff = {map {lc $_, $stuff->{$_}} keys %$stuff}; |
3128
|
|
|
|
|
|
|
#push @data, {%$stuff}; |
3129
|
0
|
|
|
|
|
0
|
push @data, {map {lc $_, $stuff->{$_}} keys %$stuff}; |
|
0
|
|
|
|
|
0
|
|
3130
|
|
|
|
|
|
|
} |
3131
|
|
|
|
|
|
|
else { |
3132
|
0
|
|
|
|
|
0
|
push @data, [@$stuff]; |
3133
|
|
|
|
|
|
|
}; |
3134
|
|
|
|
|
|
|
}; |
3135
|
|
|
|
|
|
|
|
3136
|
0
|
0
|
|
|
|
0
|
$stmt->finish() |
3137
|
|
|
|
|
|
|
or return $self->error($stmt->errstr, "BOP-10"); |
3138
|
|
|
|
|
|
|
|
3139
|
0
|
|
|
|
|
0
|
return \@data; |
3140
|
|
|
|
|
|
|
}; |
3141
|
|
|
|
|
|
|
|
3142
|
0
|
0
|
|
|
|
0
|
$stmt->finish() |
3143
|
|
|
|
|
|
|
or return $self->error($stmt->errstr, "BOP-10"); |
3144
|
|
|
|
|
|
|
|
3145
|
0
|
|
|
|
|
0
|
return 1; |
3146
|
|
|
|
|
|
|
}; |
3147
|
|
|
|
|
|
|
|
3148
|
|
|
|
|
|
|
=pod |
3149
|
|
|
|
|
|
|
|
3150
|
|
|
|
|
|
|
=item driver |
3151
|
|
|
|
|
|
|
|
3152
|
|
|
|
|
|
|
The driver method is just a shortcut wrapper for Basset::DB->new(); Only give it the same arguments in the same |
3153
|
|
|
|
|
|
|
format as you would give to Basset::DB->new() itself. The driver object returned will be cached here for all time, |
3154
|
|
|
|
|
|
|
unless you explicitly wipe it out or set it to something else. |
3155
|
|
|
|
|
|
|
|
3156
|
|
|
|
|
|
|
If the driver hasn't been accessed in the last 5 minutes, then it pings the database handle |
3157
|
|
|
|
|
|
|
before returning the driver to ensure that it's still live. If the ping fails and the driver |
3158
|
|
|
|
|
|
|
has no transaction stack, then you transparently just get back a new driver. |
3159
|
|
|
|
|
|
|
|
3160
|
|
|
|
|
|
|
But if the ping fails AND the driver had an active transaction stack, then you get back an error. |
3161
|
|
|
|
|
|
|
Calling ->driver again will create a new handle, but you would presumably have an error condition |
3162
|
|
|
|
|
|
|
to deal with. |
3163
|
|
|
|
|
|
|
|
3164
|
|
|
|
|
|
|
=cut |
3165
|
|
|
|
|
|
|
|
3166
|
|
|
|
|
|
|
=pod |
3167
|
|
|
|
|
|
|
|
3168
|
|
|
|
|
|
|
=begin btest(driver) |
3169
|
|
|
|
|
|
|
|
3170
|
|
|
|
|
|
|
=end btest(driver) |
3171
|
|
|
|
|
|
|
|
3172
|
|
|
|
|
|
|
=cut |
3173
|
|
|
|
|
|
|
|
3174
|
|
|
|
|
|
|
__PACKAGE__->add_class_attr('_driver'); |
3175
|
|
|
|
|
|
|
|
3176
|
|
|
|
|
|
|
sub driver { |
3177
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
3178
|
|
|
|
|
|
|
|
3179
|
0
|
0
|
|
|
|
0
|
return $self->local_driver if $self->local_driver; |
3180
|
|
|
|
|
|
|
|
3181
|
0
|
0
|
|
|
|
0
|
if (@_) { |
|
|
0
|
|
|
|
|
|
3182
|
0
|
|
|
|
|
0
|
return $self->_driver(shift); |
3183
|
|
|
|
|
|
|
} elsif (my $driver = $self->_driver) { |
3184
|
|
|
|
|
|
|
#if ($ENV{'MOD_PERL'} && ! $driver->ping) { |
3185
|
0
|
0
|
|
|
|
0
|
if (! $driver->ping) { |
3186
|
0
|
0
|
|
|
|
0
|
if ($driver->stack) { |
3187
|
0
|
|
|
|
|
0
|
$self->notify("warnings", "Silently disconnecting stale driver with transaction stack"); |
3188
|
|
|
|
|
|
|
} |
3189
|
0
|
|
|
|
|
0
|
$driver->recreate_handle; |
3190
|
|
|
|
|
|
|
}; |
3191
|
0
|
|
|
|
|
0
|
return $driver; |
3192
|
|
|
|
|
|
|
} else { |
3193
|
0
|
0
|
|
|
|
0
|
my $driver = $self->factory('type' => 'driver') or return; |
3194
|
0
|
|
|
|
|
0
|
return $self->_driver($driver); |
3195
|
|
|
|
|
|
|
} |
3196
|
|
|
|
|
|
|
}; |
3197
|
|
|
|
|
|
|
|
3198
|
|
|
|
|
|
|
=pod |
3199
|
|
|
|
|
|
|
|
3200
|
|
|
|
|
|
|
=item local_driver |
3201
|
|
|
|
|
|
|
|
3202
|
|
|
|
|
|
|
Normally, you're always talking to one database with all of your objects in all of your classes. And in a perfect world, that would |
3203
|
|
|
|
|
|
|
always be the case. However, you may need to speak to more than one database at a time, and that's where local_driver comes in. Much like |
3204
|
|
|
|
|
|
|
->error, this is a method that may be called on either an object or a class to specify a localized driver for that class or object. |
3205
|
|
|
|
|
|
|
|
3206
|
|
|
|
|
|
|
To make all Sub::Class objects talk to a different database: |
3207
|
|
|
|
|
|
|
|
3208
|
|
|
|
|
|
|
Sub::Class->local_driver( |
3209
|
|
|
|
|
|
|
Sub::Class->factory( |
3210
|
|
|
|
|
|
|
'type' => 'driver', |
3211
|
|
|
|
|
|
|
'dsn' => 'dbi:Pg:dbname=otherdatabase' |
3212
|
|
|
|
|
|
|
) |
3213
|
|
|
|
|
|
|
); |
3214
|
|
|
|
|
|
|
|
3215
|
|
|
|
|
|
|
To make just one talk to a different database: |
3216
|
|
|
|
|
|
|
|
3217
|
|
|
|
|
|
|
my $obj = Sub::Class->new( |
3218
|
|
|
|
|
|
|
'local_driver' => Sub::Class->factory( |
3219
|
|
|
|
|
|
|
'type' => 'driver', |
3220
|
|
|
|
|
|
|
'dsn' => 'dbi:Pg:dbname=otherdatabase' |
3221
|
|
|
|
|
|
|
) |
3222
|
|
|
|
|
|
|
); |
3223
|
|
|
|
|
|
|
|
3224
|
|
|
|
|
|
|
B that you are expected to maintain a local driver yourself - it will not be pinged, cleaned up, removed, or anything. You, the |
3225
|
|
|
|
|
|
|
programmer, are inserting in a special case and are expected to pick up after yourself. |
3226
|
|
|
|
|
|
|
|
3227
|
|
|
|
|
|
|
=cut |
3228
|
|
|
|
|
|
|
|
3229
|
|
|
|
|
|
|
__PACKAGE__->add_trickle_class_attr('_pkg_local_driver'); |
3230
|
|
|
|
|
|
|
__PACKAGE__->add_attr('_obj_local_driver'); |
3231
|
|
|
|
|
|
|
|
3232
|
|
|
|
|
|
|
sub local_driver { |
3233
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
3234
|
0
|
0
|
|
|
|
0
|
my $localmethod = ref $self ? "_obj_local_driver" : "_pkg_local_driver"; |
3235
|
|
|
|
|
|
|
|
3236
|
0
|
|
|
|
|
0
|
return $self->$localmethod(@_); |
3237
|
|
|
|
|
|
|
} |
3238
|
|
|
|
|
|
|
|
3239
|
|
|
|
|
|
|
=pod |
3240
|
|
|
|
|
|
|
|
3241
|
|
|
|
|
|
|
=item begin |
3242
|
|
|
|
|
|
|
|
3243
|
|
|
|
|
|
|
Database transactions are stack based. ->begin adds onto the stack, ->end removes from the stack. |
3244
|
|
|
|
|
|
|
See Basset::DB for more info. |
3245
|
|
|
|
|
|
|
|
3246
|
|
|
|
|
|
|
You may now begin and end your transaction as normal. Please be aware of the fact that in the current |
3247
|
|
|
|
|
|
|
implementation, beginning a transaction locks the database driver for ALL objects in the system. |
3248
|
|
|
|
|
|
|
|
3249
|
|
|
|
|
|
|
You don't need to begin if you're only committing a single object - individual classes |
3250
|
|
|
|
|
|
|
are expected to do their own locking, stack handling, unlocking, etc. as necessary. You will need to |
3251
|
|
|
|
|
|
|
begin and end if you're doing multiple commits of different objects (or if you're writing your |
3252
|
|
|
|
|
|
|
own module). For example, |
3253
|
|
|
|
|
|
|
|
3254
|
|
|
|
|
|
|
my $user = Basset::User->load(1); |
3255
|
|
|
|
|
|
|
my $user2 = Basset::User->load(2); |
3256
|
|
|
|
|
|
|
|
3257
|
|
|
|
|
|
|
$user->begin(); #start up a transaction stack |
3258
|
|
|
|
|
|
|
|
3259
|
|
|
|
|
|
|
$user->name('Jim'); #set user's name, doesn't need to be in the transaction |
3260
|
|
|
|
|
|
|
$user2->name('Koka'); #set user's name, doesn't need to be in the transaction |
3261
|
|
|
|
|
|
|
|
3262
|
|
|
|
|
|
|
$user->commit(); #doesn't actually commit to the database, it's in a transaction |
3263
|
|
|
|
|
|
|
$user2->commit(); #doesn't actually commit to the database, it's in a transaction |
3264
|
|
|
|
|
|
|
|
3265
|
|
|
|
|
|
|
$user->end(); #closes the transaction stack, now commits |
3266
|
|
|
|
|
|
|
|
3267
|
|
|
|
|
|
|
See Basset::DB for more information about begin, end, fail, etc. |
3268
|
|
|
|
|
|
|
|
3269
|
|
|
|
|
|
|
=cut |
3270
|
|
|
|
|
|
|
|
3271
|
|
|
|
|
|
|
=pod |
3272
|
|
|
|
|
|
|
|
3273
|
|
|
|
|
|
|
=begin btest(begin) |
3274
|
|
|
|
|
|
|
|
3275
|
|
|
|
|
|
|
=end btest(begin) |
3276
|
|
|
|
|
|
|
|
3277
|
|
|
|
|
|
|
=cut |
3278
|
|
|
|
|
|
|
|
3279
|
|
|
|
|
|
|
sub begin { |
3280
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
3281
|
|
|
|
|
|
|
|
3282
|
0
|
0
|
|
|
|
0
|
my $driver = $self->driver or return; |
3283
|
|
|
|
|
|
|
|
3284
|
0
|
|
0
|
|
|
0
|
return $driver->begin() || $self->error($driver->errvals); |
3285
|
|
|
|
|
|
|
|
3286
|
|
|
|
|
|
|
} |
3287
|
|
|
|
|
|
|
|
3288
|
|
|
|
|
|
|
=pod |
3289
|
|
|
|
|
|
|
|
3290
|
|
|
|
|
|
|
=item end |
3291
|
|
|
|
|
|
|
|
3292
|
|
|
|
|
|
|
Database transactions are stack based. ->begin adds onto the stack, ->end removes from the stack. |
3293
|
|
|
|
|
|
|
See Basset::DB for more info. |
3294
|
|
|
|
|
|
|
|
3295
|
|
|
|
|
|
|
=cut |
3296
|
|
|
|
|
|
|
|
3297
|
|
|
|
|
|
|
=pod |
3298
|
|
|
|
|
|
|
|
3299
|
|
|
|
|
|
|
=begin btest(end) |
3300
|
|
|
|
|
|
|
|
3301
|
|
|
|
|
|
|
=end btest(end) |
3302
|
|
|
|
|
|
|
|
3303
|
|
|
|
|
|
|
=cut |
3304
|
|
|
|
|
|
|
|
3305
|
|
|
|
|
|
|
sub end { |
3306
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
3307
|
|
|
|
|
|
|
|
3308
|
0
|
0
|
|
|
|
0
|
my $driver = $self->driver or return; |
3309
|
|
|
|
|
|
|
|
3310
|
0
|
|
0
|
|
|
0
|
return $driver->end() || $self->error($driver->errvals); |
3311
|
|
|
|
|
|
|
}; |
3312
|
|
|
|
|
|
|
|
3313
|
|
|
|
|
|
|
=pod |
3314
|
|
|
|
|
|
|
|
3315
|
|
|
|
|
|
|
=item fail |
3316
|
|
|
|
|
|
|
|
3317
|
|
|
|
|
|
|
Database transactions are stack based. ->fail is a shortcut to shutdown and rollback your |
3318
|
|
|
|
|
|
|
transaction |
3319
|
|
|
|
|
|
|
|
3320
|
|
|
|
|
|
|
=cut |
3321
|
|
|
|
|
|
|
|
3322
|
|
|
|
|
|
|
=pod |
3323
|
|
|
|
|
|
|
|
3324
|
|
|
|
|
|
|
=begin btest(fail) |
3325
|
|
|
|
|
|
|
|
3326
|
|
|
|
|
|
|
=end btest(fail) |
3327
|
|
|
|
|
|
|
|
3328
|
|
|
|
|
|
|
=cut |
3329
|
|
|
|
|
|
|
|
3330
|
|
|
|
|
|
|
sub fail { |
3331
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
3332
|
|
|
|
|
|
|
|
3333
|
0
|
0
|
|
|
|
0
|
my $driver = $self->driver or return; |
3334
|
|
|
|
|
|
|
|
3335
|
0
|
|
0
|
|
|
0
|
return $driver->fail || $self->error($driver->errvals); |
3336
|
|
|
|
|
|
|
|
3337
|
|
|
|
|
|
|
}; |
3338
|
|
|
|
|
|
|
|
3339
|
|
|
|
|
|
|
=pod |
3340
|
|
|
|
|
|
|
|
3341
|
|
|
|
|
|
|
=item finish |
3342
|
|
|
|
|
|
|
|
3343
|
|
|
|
|
|
|
Database transactions are stack based. ->finish is a shortcut to immediately finish your |
3344
|
|
|
|
|
|
|
transaction |
3345
|
|
|
|
|
|
|
|
3346
|
|
|
|
|
|
|
=cut |
3347
|
|
|
|
|
|
|
|
3348
|
|
|
|
|
|
|
=pod |
3349
|
|
|
|
|
|
|
|
3350
|
|
|
|
|
|
|
=begin btest(finish) |
3351
|
|
|
|
|
|
|
|
3352
|
|
|
|
|
|
|
=end btest(finish) |
3353
|
|
|
|
|
|
|
|
3354
|
|
|
|
|
|
|
=cut |
3355
|
|
|
|
|
|
|
|
3356
|
|
|
|
|
|
|
sub finish { |
3357
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
3358
|
|
|
|
|
|
|
|
3359
|
0
|
0
|
|
|
|
0
|
my $driver = $self->driver or return; |
3360
|
|
|
|
|
|
|
|
3361
|
0
|
|
0
|
|
|
0
|
return $driver->finish || $self->error($driver->errvals); |
3362
|
|
|
|
|
|
|
}; |
3363
|
|
|
|
|
|
|
|
3364
|
|
|
|
|
|
|
=pod |
3365
|
|
|
|
|
|
|
|
3366
|
|
|
|
|
|
|
=item wipe |
3367
|
|
|
|
|
|
|
|
3368
|
|
|
|
|
|
|
Database transactions are stack based. ->wipe clears out your transaction stack. |
3369
|
|
|
|
|
|
|
|
3370
|
|
|
|
|
|
|
=cut |
3371
|
|
|
|
|
|
|
|
3372
|
|
|
|
|
|
|
=pod |
3373
|
|
|
|
|
|
|
|
3374
|
|
|
|
|
|
|
=begin btest(wipe) |
3375
|
|
|
|
|
|
|
|
3376
|
|
|
|
|
|
|
=end btest(wipe) |
3377
|
|
|
|
|
|
|
|
3378
|
|
|
|
|
|
|
=cut |
3379
|
|
|
|
|
|
|
|
3380
|
|
|
|
|
|
|
sub wipe { |
3381
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
3382
|
|
|
|
|
|
|
|
3383
|
0
|
0
|
|
|
|
0
|
my $driver = $self->driver or return; |
3384
|
|
|
|
|
|
|
|
3385
|
0
|
|
0
|
|
|
0
|
return $driver->wipe || $self->error($driver->errvals); |
3386
|
|
|
|
|
|
|
|
3387
|
|
|
|
|
|
|
}; |
3388
|
|
|
|
|
|
|
|
3389
|
|
|
|
|
|
|
=pod |
3390
|
|
|
|
|
|
|
|
3391
|
|
|
|
|
|
|
=item fatalerror |
3392
|
|
|
|
|
|
|
|
3393
|
|
|
|
|
|
|
Setting a fatalerror message causes your transaction to fail. Note that you must explicitly pass a |
3394
|
|
|
|
|
|
|
defined value for the transaction stack to be wiped. |
3395
|
|
|
|
|
|
|
|
3396
|
|
|
|
|
|
|
If you need to unfail a failed transaction (say, you know how to recover from the error), then you should call |
3397
|
|
|
|
|
|
|
unfail on the driver and continue. |
3398
|
|
|
|
|
|
|
|
3399
|
|
|
|
|
|
|
$driver->unfail(); |
3400
|
|
|
|
|
|
|
# interesting things |
3401
|
|
|
|
|
|
|
|
3402
|
|
|
|
|
|
|
=cut |
3403
|
|
|
|
|
|
|
|
3404
|
|
|
|
|
|
|
=pod |
3405
|
|
|
|
|
|
|
|
3406
|
|
|
|
|
|
|
=begin btest(fatalerror) |
3407
|
|
|
|
|
|
|
|
3408
|
|
|
|
|
|
|
my $o = __PACKAGE__->new(); |
3409
|
|
|
|
|
|
|
$test->ok($o, "got object"); |
3410
|
|
|
|
|
|
|
|
3411
|
|
|
|
|
|
|
$test->is($o->committing(1), 1, "set committing to 1"); |
3412
|
|
|
|
|
|
|
$test->is($o->deleting(1), 1, "set deleting to 1"); |
3413
|
|
|
|
|
|
|
|
3414
|
|
|
|
|
|
|
$test->is(scalar($o->fatalerror("fatalerror", "some code")), undef, "set fatalerror"); |
3415
|
|
|
|
|
|
|
$test->is($o->errcode, "some code", "proper error code"); |
3416
|
|
|
|
|
|
|
$test->is($o->committing, 0, "wiped out committing flag"); |
3417
|
|
|
|
|
|
|
$test->is($o->deleting, 0, "wiped out deleting flag"); |
3418
|
|
|
|
|
|
|
|
3419
|
|
|
|
|
|
|
$test->is(scalar(__PACKAGE__->fatalerror("pkg error", "pkg error code")), undef, "set pkg error"); |
3420
|
|
|
|
|
|
|
$test->is(__PACKAGE__->errcode, "pkg error code", "proper package error code"); |
3421
|
|
|
|
|
|
|
$test->is($o->errcode, "some code", "object retains error code"); |
3422
|
|
|
|
|
|
|
|
3423
|
|
|
|
|
|
|
=end btest(fatalerror) |
3424
|
|
|
|
|
|
|
|
3425
|
|
|
|
|
|
|
=cut |
3426
|
|
|
|
|
|
|
|
3427
|
|
|
|
|
|
|
sub fatalerror { |
3428
|
2
|
|
|
2
|
1
|
5
|
my $self = shift; |
3429
|
|
|
|
|
|
|
|
3430
|
2
|
|
|
|
|
11
|
my $driver = $self->_driver; |
3431
|
|
|
|
|
|
|
|
3432
|
2
|
0
|
33
|
|
|
8
|
if (defined $driver && @_ && defined $_[0]) { |
|
|
|
33
|
|
|
|
|
3433
|
0
|
|
|
|
|
0
|
$driver->failed(1); |
3434
|
0
|
|
|
|
|
0
|
$driver->end(); |
3435
|
|
|
|
|
|
|
} |
3436
|
|
|
|
|
|
|
|
3437
|
2
|
100
|
|
|
|
12
|
$self->committing(0) if ref $self; |
3438
|
2
|
100
|
|
|
|
10
|
$self->deleting(0) if ref $self; |
3439
|
|
|
|
|
|
|
|
3440
|
2
|
|
|
|
|
12
|
return $self->error(@_); |
3441
|
|
|
|
|
|
|
}; |
3442
|
|
|
|
|
|
|
|
3443
|
|
|
|
|
|
|
=pod |
3444
|
|
|
|
|
|
|
|
3445
|
|
|
|
|
|
|
=item setup |
3446
|
|
|
|
|
|
|
|
3447
|
|
|
|
|
|
|
The setup method is called immediately after the object is loaded and initialized in load_all. Basset::Object::Persistent's |
3448
|
|
|
|
|
|
|
method is empty and does nothing. It's designed to be used in subclasses in locations where you need to alter something in |
3449
|
|
|
|
|
|
|
an object after it's loaded from the database and set up properly. Say if you do further initialization |
3450
|
|
|
|
|
|
|
or load in from an object or something. |
3451
|
|
|
|
|
|
|
|
3452
|
|
|
|
|
|
|
=cut |
3453
|
|
|
|
|
|
|
|
3454
|
|
|
|
|
|
|
sub setup { |
3455
|
0
|
|
|
0
|
1
|
|
return shift; |
3456
|
|
|
|
|
|
|
}; |
3457
|
|
|
|
|
|
|
|
3458
|
|
|
|
|
|
|
=pod |
3459
|
|
|
|
|
|
|
|
3460
|
|
|
|
|
|
|
=begin btest(setup) |
3461
|
|
|
|
|
|
|
|
3462
|
|
|
|
|
|
|
=end btest(setup) |
3463
|
|
|
|
|
|
|
|
3464
|
|
|
|
|
|
|
=cut |
3465
|
|
|
|
|
|
|
|
3466
|
|
|
|
|
|
|
=pod |
3467
|
|
|
|
|
|
|
|
3468
|
|
|
|
|
|
|
=item cleanup |
3469
|
|
|
|
|
|
|
|
3470
|
|
|
|
|
|
|
The cleanup method is called immediately before the object is committed in commit. Basset::Object::Persistent's |
3471
|
|
|
|
|
|
|
method is empty and does nothing. It's designed to be used in subclasses in locations where you need to alter something in |
3472
|
|
|
|
|
|
|
an object immediately before it's committed to the database. |
3473
|
|
|
|
|
|
|
|
3474
|
|
|
|
|
|
|
=cut |
3475
|
|
|
|
|
|
|
|
3476
|
|
|
|
|
|
|
sub cleanup { |
3477
|
0
|
|
|
0
|
1
|
|
return shift; |
3478
|
|
|
|
|
|
|
}; |
3479
|
|
|
|
|
|
|
|
3480
|
|
|
|
|
|
|
=pod |
3481
|
|
|
|
|
|
|
|
3482
|
|
|
|
|
|
|
=begin btest(cleanup) |
3483
|
|
|
|
|
|
|
|
3484
|
|
|
|
|
|
|
=end btest(cleanup) |
3485
|
|
|
|
|
|
|
|
3486
|
|
|
|
|
|
|
=cut |
3487
|
|
|
|
|
|
|
|
3488
|
|
|
|
|
|
|
=pod |
3489
|
|
|
|
|
|
|
|
3490
|
|
|
|
|
|
|
=back |
3491
|
|
|
|
|
|
|
|
3492
|
|
|
|
|
|
|
=cut |
3493
|
|
|
|
|
|
|
|
3494
|
|
|
|
|
|
|
1; |