1 |
cvsjoko |
1.1 |
package Class::Tangram; |
2 |
|
|
|
3 |
joko |
1.3 |
# Copyright (c) 2001, 2002 Sam Vilain. All right reserved. This file |
4 |
|
|
# is licensed under the terms of the Perl Artistic license. |
5 |
cvsjoko |
1.1 |
|
6 |
|
|
=head1 NAME |
7 |
|
|
|
8 |
|
|
Class::Tangram - create constructors, accessor and update methods for |
9 |
|
|
objects from a Tangram-compatible object specification. |
10 |
|
|
|
11 |
|
|
=head1 SYNOPSIS |
12 |
|
|
|
13 |
joko |
1.3 |
package MyObject; |
14 |
cvsjoko |
1.1 |
|
15 |
|
|
use base qw(Class::Tangram); |
16 |
|
|
|
17 |
joko |
1.3 |
our $fields => { int => [ qw(foo bar) ], |
18 |
|
|
string => [ qw(baz quux) ] }; |
19 |
|
|
|
20 |
|
|
package main; |
21 |
|
|
|
22 |
|
|
my $object = MyObject->new(foo => 2, baz => "hello"); |
23 |
|
|
|
24 |
|
|
print $object->baz(); # prints "hello" |
25 |
|
|
|
26 |
|
|
$object->set_quux("Something"); |
27 |
cvsjoko |
1.1 |
|
28 |
joko |
1.3 |
$object->set_foo("Something"); # dies - not an integer |
29 |
cvsjoko |
1.1 |
|
30 |
joko |
1.3 |
=head1 DESCRIPTION |
31 |
cvsjoko |
1.1 |
|
32 |
joko |
1.3 |
Class::Tangram is a tool for defining objects attributes. Simply |
33 |
|
|
define your object's fields/attributes using the same syntax |
34 |
|
|
introduced in _A Guided Tour of Tangram_, and you get objects that |
35 |
|
|
work As You'd Expect(tm). |
36 |
|
|
|
37 |
|
|
Class::Tangram has no dependancy upon Tangram, and vice versa. |
38 |
|
|
Neither requires anything special of your objects, nor do they insert |
39 |
|
|
any special fields into your objects. This is a very important |
40 |
|
|
feature with innumerable benefits, and few (if any) other object |
41 |
|
|
persistence tools have this feature. |
42 |
|
|
|
43 |
|
|
So, fluff aside, let's run through how you use Class::Tangram to make |
44 |
|
|
objects. |
45 |
|
|
|
46 |
|
|
First, you decide upon the attributes your object is going to have. |
47 |
|
|
You might do this using UML, or you might pick an existing database |
48 |
|
|
table and declare each column to be an attribute (you can leave out |
49 |
|
|
"id"; that one is implicit). |
50 |
|
|
|
51 |
|
|
Your object should use Class::Tangram as a base class; |
52 |
|
|
|
53 |
|
|
use base qw(Class::Tangram) |
54 |
|
|
|
55 |
|
|
or for older versions of perl: |
56 |
|
|
|
57 |
|
|
use Class::Tangram; |
58 |
|
|
use vars qw(@ISA); |
59 |
|
|
@ISA = qw(Class::Tangram) |
60 |
|
|
|
61 |
|
|
You should then define a C<$fields> variable in the scope of the |
62 |
|
|
package, that is a B<hash> from attribute B<types> (see |
63 |
|
|
L<Tangram::Type>) to either an B<array> of B<attribute names>, or |
64 |
|
|
another B<hash> from B<attribute names> to B<options hashes> (or |
65 |
|
|
C<undef>). The layout of this structure coincides with the C<fields> |
66 |
|
|
portion of a tangram schema (see L<Tangram::Schema>). Note: the term |
67 |
|
|
`schema' is used frequently to refer to the C<$fields> structure. |
68 |
cvsjoko |
1.1 |
|
69 |
joko |
1.3 |
For example, |
70 |
cvsjoko |
1.1 |
|
71 |
joko |
1.3 |
package MyObject; |
72 |
|
|
use base qw(Class::Tangram); |
73 |
cvsjoko |
1.1 |
|
74 |
joko |
1.3 |
our $fields = { |
75 |
|
|
int => { |
76 |
|
|
juiciness => undef, |
77 |
|
|
segments => { |
78 |
|
|
# this code reference is called when this |
79 |
|
|
# attribute is set, to check the value is |
80 |
|
|
# OK |
81 |
|
|
check_func => sub { |
82 |
|
|
die "too many segments" |
83 |
|
|
if (${(shift)} > 30); |
84 |
|
|
}, |
85 |
|
|
# the default for this attribute. |
86 |
|
|
init_default => 7, |
87 |
|
|
}, |
88 |
|
|
}, |
89 |
|
|
ref => { |
90 |
|
|
grower => undef, |
91 |
|
|
}, |
92 |
cvsjoko |
1.1 |
|
93 |
joko |
1.3 |
# 'required' attributes - insist that these fields are |
94 |
|
|
# set, both with constructor and set()/set_X methods |
95 |
|
|
string => { |
96 |
|
|
# true: 'type' must have non-empty value (for |
97 |
|
|
# strings) or be logically true (for other types) |
98 |
|
|
type => { required => 1 }, |
99 |
cvsjoko |
1.1 |
|
100 |
joko |
1.3 |
# false: 'tag' must be defined but may be empty |
101 |
|
|
tag => { required => '' }, |
102 |
|
|
}, |
103 |
cvsjoko |
1.1 |
|
104 |
joko |
1.3 |
# fields allowed by Class::Tangram but not ever |
105 |
|
|
# stored by Tangram - no type checking by default |
106 |
|
|
transient => [ qw(_tangible) ], |
107 |
|
|
}; |
108 |
cvsjoko |
1.1 |
|
109 |
joko |
1.3 |
It is of critical importance to your sanity that you understand how |
110 |
|
|
anonymous hashes and anonymous arrays work in Perl. Some additional |
111 |
|
|
features are used above that have not yet been introduced, but you |
112 |
|
|
should be able to look at the above data structure and see that it |
113 |
|
|
satisfies the conditions stated in the paragraph before it. If it is |
114 |
|
|
hazy, I recommend reading L<perlref> or L<perlreftut>. |
115 |
|
|
|
116 |
|
|
When the schema for the object is first imported (see L<Schema |
117 |
|
|
import>), Class::Tangram defines accessor functions for each of the |
118 |
|
|
attributes defined in the schema. These accessor functions are then |
119 |
|
|
available as C<$object-E<gt>function> on created objects. By virtue |
120 |
|
|
of inheritance, various other methods are available. |
121 |
cvsjoko |
1.1 |
|
122 |
joko |
1.3 |
From Class::Tangram 1.12 onwards, no use of perl's C<AUTOLOAD> |
123 |
|
|
functionality is used. |
124 |
cvsjoko |
1.1 |
|
125 |
|
|
=cut |
126 |
|
|
|
127 |
|
|
use strict; |
128 |
|
|
use Carp qw(croak cluck); |
129 |
|
|
|
130 |
joko |
1.3 |
use vars qw($VERSION %defaults); |
131 |
cvsjoko |
1.1 |
|
132 |
joko |
1.3 |
use Set::Object; |
133 |
|
|
|
134 |
|
|
use Pod::Constants -trim => 1, |
135 |
|
|
'MODULE RELEASE' => sub { ($VERSION) = /(\d+\.\d+)/ }, |
136 |
|
|
'Default Type Checking' => sub { %defaults = eval; }; |
137 |
cvsjoko |
1.1 |
|
138 |
|
|
# $types{$class}->{$attribute} is the tangram type of each attribute |
139 |
|
|
my (%types); |
140 |
|
|
|
141 |
joko |
1.3 |
# $attribute_options{$class}->{$attribute} is the hash passed to tangram |
142 |
|
|
# for the given attribute |
143 |
|
|
my (%attribute_options); |
144 |
|
|
|
145 |
cvsjoko |
1.1 |
# $check{$class}->{$attribute}->($value) is a function that will die |
146 |
|
|
# if $value is not alright, see check_X functions |
147 |
|
|
my (%check); |
148 |
|
|
|
149 |
|
|
# Destructors for each attribute. They are called as |
150 |
|
|
# $cleaners{$class}->{$attribute}->($self, $attribute); |
151 |
|
|
my (%cleaners); |
152 |
|
|
|
153 |
|
|
# init_default values for each attribute. These could be hash refs, |
154 |
|
|
# array refs, code refs, or simple scalars. They will be stored as |
155 |
|
|
# $init_defaults{$class}->{$attribute} |
156 |
|
|
my (%init_defaults); |
157 |
|
|
|
158 |
joko |
1.3 |
# $required_attributes{$class}->{$attribute} records which attributes |
159 |
|
|
# are required... used only by new() at present. |
160 |
|
|
my (%required_attributes); |
161 |
|
|
|
162 |
cvsjoko |
1.1 |
# if a class is abstract, complain if one is constructed. |
163 |
|
|
my (%abstract); |
164 |
|
|
|
165 |
|
|
=head1 METHODS |
166 |
|
|
|
167 |
joko |
1.3 |
The following methods are available for all Class::Tangram objects |
168 |
|
|
|
169 |
|
|
=head2 Constructor |
170 |
|
|
|
171 |
|
|
A Constructor is a method that returns a new instance of an object. |
172 |
|
|
|
173 |
cvsjoko |
1.1 |
=over 4 |
174 |
|
|
|
175 |
|
|
=item Class-E<gt>new (attribute1 =E<gt> value, attribute2 =E<gt> value) |
176 |
|
|
|
177 |
joko |
1.3 |
Sets up a new object of type C<Class>, with attributes set to the |
178 |
|
|
values supplied. |
179 |
cvsjoko |
1.1 |
|
180 |
joko |
1.3 |
Can also be used as an object method (normal use is as a "class |
181 |
|
|
method"), in which case it returns a B<copy> of the object, without |
182 |
|
|
any deep copying. |
183 |
cvsjoko |
1.1 |
|
184 |
|
|
=cut |
185 |
|
|
|
186 |
|
|
sub new ($@) |
187 |
|
|
{ |
188 |
|
|
my $invocant = shift; |
189 |
|
|
my $class = ref $invocant || $invocant; |
190 |
|
|
|
191 |
joko |
1.3 |
(my @values, @_) = @_; |
192 |
cvsjoko |
1.1 |
|
193 |
|
|
# Setup the object |
194 |
|
|
my $self = { }; |
195 |
|
|
bless $self, $class; |
196 |
|
|
|
197 |
joko |
1.3 |
# auto-load schema as necessary |
198 |
|
|
exists $types{$class} or import_schema($class); |
199 |
cvsjoko |
1.1 |
|
200 |
|
|
croak "Attempt to instantiate an abstract type" |
201 |
|
|
if ($abstract{$class}); |
202 |
|
|
|
203 |
joko |
1.3 |
if (ref $invocant) |
204 |
cvsjoko |
1.1 |
{ |
205 |
|
|
# The copy constructor; this could be better :) |
206 |
|
|
# this has the side effect of much auto-vivification. |
207 |
|
|
%$self = %$invocant; |
208 |
|
|
$self->set (@values); # override with @values |
209 |
|
|
} |
210 |
|
|
else |
211 |
|
|
{ |
212 |
|
|
$self->set (@values); # start with @values |
213 |
joko |
1.3 |
} |
214 |
|
|
|
215 |
|
|
$self->_fill_init_default(); |
216 |
|
|
$self->_check_required(); |
217 |
cvsjoko |
1.1 |
|
218 |
joko |
1.3 |
return $self; |
219 |
|
|
|
220 |
|
|
} |
221 |
cvsjoko |
1.1 |
|
222 |
joko |
1.3 |
sub _fill_init_default { |
223 |
|
|
my $self = shift; |
224 |
|
|
my $class = ref $self or die "_fill_init_default usage error"; |
225 |
|
|
|
226 |
|
|
# fill in fields that have defaults |
227 |
|
|
while ( my ($attribute, $default) = |
228 |
|
|
each %{$init_defaults{$class}} ) { |
229 |
|
|
|
230 |
|
|
next if (exists $self->{$attribute}); |
231 |
|
|
|
232 |
|
|
my $setter = "set_$attribute"; |
233 |
|
|
if (ref $default eq "CODE") { |
234 |
|
|
# sub { }, attribute gets return value |
235 |
|
|
$self->$setter( $default->($self) ); |
236 |
|
|
|
237 |
|
|
} elsif (ref $default eq "HASH") { |
238 |
|
|
# hash ref, copy hash |
239 |
|
|
$self->$setter( { %{ $default } } ); |
240 |
|
|
|
241 |
|
|
} elsif (ref $default eq "ARRAY") { |
242 |
|
|
# array ref, copy array |
243 |
|
|
$self->$setter( [ @{ $default } ] ); |
244 |
cvsjoko |
1.1 |
|
245 |
joko |
1.3 |
} else { |
246 |
|
|
# something else, an object or a scalar |
247 |
|
|
$self->$setter($default); |
248 |
|
|
} |
249 |
|
|
} |
250 |
|
|
} |
251 |
cvsjoko |
1.1 |
|
252 |
joko |
1.3 |
sub _check_required { |
253 |
|
|
my $self = shift; |
254 |
|
|
my $class = ref $self; |
255 |
|
|
|
256 |
|
|
# make sure field is not undef if 'required' option is set |
257 |
|
|
if (my $required = $required_attributes{$class}) { |
258 |
|
|
|
259 |
|
|
# find the immediate caller outside of this package |
260 |
|
|
my $i = 0; |
261 |
|
|
$i++ while UNIVERSAL::isa($self, scalar(caller($i))||";->"); |
262 |
|
|
|
263 |
|
|
# give Tangram some lenience - it is exempt from the effects |
264 |
|
|
# of the "required" option |
265 |
|
|
unless ( caller($i) =~ m/^Tangram::/ ) { |
266 |
|
|
my @missing; |
267 |
|
|
while ( my ($attribute, $value) = each %$required ) { |
268 |
|
|
push(@missing, $attribute) |
269 |
|
|
if ! exists $self->{$attribute}; |
270 |
cvsjoko |
1.1 |
} |
271 |
joko |
1.3 |
croak("object missing required attribute(s): " |
272 |
|
|
.join(', ',@missing).'.') if @missing; |
273 |
cvsjoko |
1.1 |
} |
274 |
|
|
} |
275 |
|
|
} |
276 |
|
|
|
277 |
joko |
1.3 |
=back |
278 |
|
|
|
279 |
|
|
=head2 Accessing & Setting Attributes |
280 |
|
|
|
281 |
|
|
=over |
282 |
|
|
|
283 |
cvsjoko |
1.1 |
=item $instance->set(attribute => $value, ...) |
284 |
|
|
|
285 |
|
|
Sets the attributes of the given instance to the given values. croaks |
286 |
|
|
if there is a problem with the values. |
287 |
|
|
|
288 |
joko |
1.3 |
This function simply calls C<$instance-E<gt>set_attribute($value)> for |
289 |
|
|
each of the C<attribute =E<gt> $value> pairs passed to it. |
290 |
|
|
|
291 |
cvsjoko |
1.1 |
=cut |
292 |
|
|
|
293 |
joko |
1.3 |
sub set { |
294 |
|
|
my $self = shift; |
295 |
cvsjoko |
1.1 |
|
296 |
|
|
# yes, this is a lot to do. yes, it's slow. But I'm fairly |
297 |
|
|
# certain that this could be handled efficiently if it were to be |
298 |
|
|
# moved inside the Perl interpreter or an XS module |
299 |
joko |
1.3 |
UNIVERSAL::isa($self, "Class::Tangram") or croak "type mismatch"; |
300 |
cvsjoko |
1.1 |
my $class = ref $self; |
301 |
|
|
exists $check{$class} or import_schema($class); |
302 |
joko |
1.3 |
croak "set must be called with an even number of arguments" |
303 |
|
|
if (scalar(@_) & 1); |
304 |
cvsjoko |
1.1 |
|
305 |
joko |
1.3 |
while (my ($name, $value) = splice @_, 0, 2) { |
306 |
cvsjoko |
1.1 |
|
307 |
joko |
1.3 |
my $setter = "set_".$name; |
308 |
cvsjoko |
1.1 |
|
309 |
joko |
1.3 |
croak "attempt to set an illegal field $name in a $class" |
310 |
|
|
unless $self->can($setter); |
311 |
cvsjoko |
1.1 |
|
312 |
joko |
1.3 |
$self->$setter($value); |
313 |
cvsjoko |
1.1 |
} |
314 |
|
|
} |
315 |
|
|
|
316 |
joko |
1.3 |
=item $instance->get("attribute") |
317 |
cvsjoko |
1.1 |
|
318 |
joko |
1.3 |
Gets the value of C<$attribute>. This simply calls |
319 |
|
|
C<$instance-E<gt>get_attribute>. If multiple attributes are listed, |
320 |
|
|
then a list of the attribute values is returned in order. Note that |
321 |
|
|
you get back the results of the scalar context C<get_attribute> call |
322 |
|
|
in this case. |
323 |
cvsjoko |
1.1 |
|
324 |
|
|
=cut |
325 |
|
|
|
326 |
joko |
1.3 |
sub get { |
327 |
|
|
my $self = shift; |
328 |
|
|
croak "get what?" unless @_; |
329 |
|
|
UNIVERSAL::isa($self, "Class::Tangram") or croak "type mismatch"; |
330 |
|
|
|
331 |
cvsjoko |
1.1 |
my $class = ref $self; |
332 |
|
|
exists $check{$class} or import_schema($class); |
333 |
|
|
|
334 |
joko |
1.3 |
my $multiget = (scalar(@_) != 1); |
335 |
|
|
|
336 |
|
|
my @return; |
337 |
|
|
while ( my $field = shift ) { |
338 |
|
|
my $getter = "get_".$field; |
339 |
|
|
croak "attempt to read an illegal field $field in a $class" |
340 |
|
|
unless $self->can($getter); |
341 |
|
|
|
342 |
|
|
if ( $multiget ) { |
343 |
|
|
push @return, scalar($self->$getter()); |
344 |
|
|
} else { |
345 |
|
|
return $self->$getter(); |
346 |
cvsjoko |
1.1 |
} |
347 |
|
|
} |
348 |
|
|
|
349 |
joko |
1.3 |
return @return; |
350 |
cvsjoko |
1.1 |
} |
351 |
|
|
|
352 |
|
|
=item $instance->attribute($value) |
353 |
|
|
|
354 |
joko |
1.3 |
If C<$value> is not given, then |
355 |
|
|
this is equivalent to C<$instance-E<gt>get_attribute> |
356 |
cvsjoko |
1.1 |
|
357 |
joko |
1.3 |
If C<$value> is given, then this is equivalent to |
358 |
|
|
C<$instance-E<gt>set_attribute($value)>. This usage issues a warning |
359 |
|
|
if warnings are on; you should change your code to use the |
360 |
|
|
set_attribute syntax for better readability. OO veterans will tell |
361 |
|
|
you that for maintainability object method names should always be a |
362 |
|
|
verb. |
363 |
cvsjoko |
1.1 |
|
364 |
|
|
=item $instance->get_attribute |
365 |
|
|
|
366 |
|
|
=item $instance->set_attribute($value) |
367 |
|
|
|
368 |
joko |
1.3 |
The normative way of getting and setting attributes. If you wish to |
369 |
|
|
override the behaviour of an object when getting or setting an |
370 |
|
|
attribute, override these functions. They will be called when you use |
371 |
|
|
C<$instance-E<gt>attribute>, C<$instance-E<gt>get()>, constructors, |
372 |
|
|
etc. |
373 |
cvsjoko |
1.1 |
|
374 |
|
|
=item $instance->attribute_includes(@objects) |
375 |
|
|
|
376 |
|
|
=item $instance->attribute_insert(@objects) |
377 |
|
|
|
378 |
|
|
=item $instance->attribute_size |
379 |
|
|
|
380 |
|
|
=item $instance->attribute_clear |
381 |
|
|
|
382 |
|
|
=item $instance->attribute_remove(@objects) |
383 |
|
|
|
384 |
joko |
1.3 |
This suite of functions applies to attributes that are sets (C<iset> |
385 |
|
|
or C<set>). It could in theory also apply generally to all |
386 |
|
|
collections - ie also arrays (C<iarray> or C<array>), and hashes |
387 |
|
|
(C<hash>, C<ihash>). This will be implemented subject to user demand. |
388 |
|
|
|
389 |
|
|
=back |
390 |
|
|
|
391 |
|
|
B<Note:> The above functions can be overridden, but they may not be |
392 |
|
|
called with the C<$self-E<gt>SUPER::> superclass chaining method. |
393 |
|
|
This is because they are not defined within the scope of |
394 |
|
|
Class::Tangram, only your package. |
395 |
cvsjoko |
1.1 |
|
396 |
|
|
=cut |
397 |
|
|
|
398 |
joko |
1.3 |
=head1 ATTRIBUTE TYPE CHECKING |
399 |
|
|
|
400 |
|
|
Class::Tangram provides type checking of attributes when attributes |
401 |
|
|
are set - either using the default C<set_attribute> functions, or |
402 |
|
|
created via the C<new> constructor. |
403 |
|
|
|
404 |
|
|
The checking has default behaviour for each type of attribute (see |
405 |
|
|
L<Default Type Checking>), and can be extended arbitrarily via a |
406 |
|
|
per-attribute C<check_func>, described below. Critical attributes can |
407 |
|
|
be marked as such with the C<required> flag. |
408 |
|
|
|
409 |
|
|
The specification of this type checking is placed in the class schema, |
410 |
|
|
in the per-attribute B<options hash>. This is a Class::Tangram |
411 |
|
|
extension to the Tangram schema structure. |
412 |
|
|
|
413 |
|
|
=over |
414 |
|
|
|
415 |
|
|
=item check_func |
416 |
|
|
|
417 |
|
|
A function that is called with a B<reference> to the new value in |
418 |
|
|
C<$_[0]>. It should call C<die()> if the value is bad. Note that |
419 |
|
|
this check_func will never be passed an undefined value; this is |
420 |
|
|
covered by the "required" option, below. |
421 |
|
|
|
422 |
|
|
In the example schema (above), the attribute C<segments> has a |
423 |
|
|
C<check_func> that prevents setting the value to anything greater than |
424 |
|
|
30. Note that it does not prevent you from setting the value to |
425 |
|
|
something that is not an integer; if you define a C<check_func>, it |
426 |
|
|
replaces the default. |
427 |
|
|
|
428 |
|
|
=item required |
429 |
|
|
|
430 |
|
|
If this option is set to a true value, then the attribute must be set |
431 |
|
|
to a true value to pass type checking. For string attributes, this |
432 |
|
|
means that the string must be defined and non-empty (so "0" is true). |
433 |
|
|
For other attribute types, the normal Perl definition of logical truth |
434 |
|
|
is used. |
435 |
|
|
|
436 |
|
|
If the required option is defined but logically false, (ie "" or 0), |
437 |
|
|
then the attribute must also be defined, but may be set to a logically |
438 |
|
|
false value. |
439 |
|
|
|
440 |
|
|
If the required option is undefined, then the attribute may be set to |
441 |
|
|
an undefined value. |
442 |
|
|
|
443 |
|
|
For integration with tangram, the C<new()> function has a special |
444 |
|
|
hack; if it is being invoked from within Tangram, then the required |
445 |
|
|
test is skipped. |
446 |
|
|
|
447 |
|
|
=back |
448 |
|
|
|
449 |
|
|
=head2 Other per-attribute options |
450 |
|
|
|
451 |
|
|
Any of the following options may be inserted into the per-attribute |
452 |
|
|
B<options hash>: |
453 |
|
|
|
454 |
|
|
=over |
455 |
cvsjoko |
1.1 |
|
456 |
joko |
1.3 |
=item init_default |
457 |
cvsjoko |
1.1 |
|
458 |
joko |
1.3 |
This value specifies the default value of the attribute when |
459 |
|
|
it is created with C<new()>. It is a scalar value, it is |
460 |
|
|
copied to the fresh object. If it is a code reference, that |
461 |
|
|
code reference is called and its return value inserted into |
462 |
|
|
the attribute. If it is an ARRAY or HASH reference, then |
463 |
|
|
that array or hash is COPIED into the attribute. |
464 |
cvsjoko |
1.1 |
|
465 |
joko |
1.3 |
=item destroy_func |
466 |
cvsjoko |
1.1 |
|
467 |
joko |
1.3 |
If anything special needs to happen to this attribute before the |
468 |
|
|
object is destroyed (or when someone calls |
469 |
|
|
C<$object-E<gt>clear_refs()>), then define this. It is called as |
470 |
|
|
C<$sub-E<gt>($object, "attribute")>. |
471 |
cvsjoko |
1.1 |
|
472 |
joko |
1.3 |
=back |
473 |
cvsjoko |
1.1 |
|
474 |
joko |
1.3 |
=head2 Default Type Checking |
475 |
cvsjoko |
1.1 |
|
476 |
joko |
1.3 |
# The following list is eval'ed from this documentation |
477 |
|
|
# when Class::Tangram loads, and used as default attribute |
478 |
|
|
# options for the specified types. So, eg, the default |
479 |
|
|
# "init_default" for "set" types is a subroutine that |
480 |
|
|
# returns a new Set::Object container. |
481 |
|
|
|
482 |
|
|
# "parse" is special - it is passed the options hash given |
483 |
|
|
# by the user and should return (\&check_func, |
484 |
|
|
# \&destroy_func). This is how the magical string type |
485 |
|
|
# checking is performed - see the entry for parse_string(), |
486 |
|
|
# below. |
487 |
|
|
|
488 |
|
|
int => { check_func => \&check_int }, |
489 |
|
|
real => { check_func => \&check_real }, |
490 |
|
|
string => { parse => \&parse_string }, |
491 |
|
|
ref => { check_func => \&check_obj, |
492 |
|
|
destroy_func => \&destroy_ref }, |
493 |
|
|
array => { check_func => \&check_array, |
494 |
|
|
destroy_func => \&destroy_array }, |
495 |
|
|
iarray => { check_func => \&check_array, |
496 |
|
|
destroy_func => \&destroy_array }, |
497 |
|
|
flat_array => { check_func => \&check_flat_array }, |
498 |
|
|
set => { check_func => \&check_set, |
499 |
|
|
destroy_func => \&destroy_set, |
500 |
|
|
init_default => sub { Set::Object->new() } }, |
501 |
|
|
iset => { check_func => \&check_set, |
502 |
|
|
destroy_func => \&destroy_set, |
503 |
|
|
init_default => sub { Set::Object->new() } }, |
504 |
|
|
dmdatetime => { check_func => \&check_dmdatetime }, |
505 |
|
|
rawdatetime => { check_func => \&check_rawdatetime }, |
506 |
|
|
rawdate => { check_func => \&check_rawdate }, |
507 |
|
|
rawtime => { check_func => \&check_rawtime }, |
508 |
|
|
flat_hash => { check_func => \&check_flat_hash }, |
509 |
|
|
transient => { check_func => \&check_nothing }, |
510 |
|
|
hash => { check_func => \&check_hash, |
511 |
|
|
destroy_func => \&destroy_hash, |
512 |
|
|
get_func => \&get_hash }, |
513 |
|
|
perl_dump => { check_func => \&check_nothing } |
514 |
cvsjoko |
1.1 |
|
515 |
joko |
1.3 |
=over |
516 |
cvsjoko |
1.1 |
|
517 |
|
|
=item check_X (\$value) |
518 |
|
|
|
519 |
joko |
1.3 |
This series of internal functions are built-in C<check_func> functions |
520 |
|
|
defined for all of the standard Tangram attribute types. |
521 |
|
|
|
522 |
|
|
=over |
523 |
cvsjoko |
1.1 |
|
524 |
joko |
1.3 |
=item check_string |
525 |
cvsjoko |
1.1 |
|
526 |
joko |
1.3 |
checks that the supplied value is less than 255 characters long. |
527 |
cvsjoko |
1.1 |
|
528 |
|
|
=cut |
529 |
|
|
|
530 |
|
|
sub check_string { |
531 |
joko |
1.3 |
croak "string too long" |
532 |
cvsjoko |
1.1 |
if (length ${$_[0]} > 255); |
533 |
|
|
} |
534 |
|
|
|
535 |
joko |
1.3 |
=item check_int |
536 |
cvsjoko |
1.1 |
|
537 |
joko |
1.3 |
checks that the value is a (possibly signed) integer |
538 |
cvsjoko |
1.1 |
|
539 |
|
|
=cut |
540 |
|
|
|
541 |
|
|
my $int_re = qr/^-?\d+$/; |
542 |
|
|
sub check_int { |
543 |
joko |
1.3 |
croak "not an integer" |
544 |
|
|
if (${$_[0]} !~ m/$int_re/o); |
545 |
cvsjoko |
1.1 |
} |
546 |
|
|
|
547 |
joko |
1.3 |
=item check_real |
548 |
cvsjoko |
1.1 |
|
549 |
joko |
1.3 |
checks that the value is a real number, by stringifying it and |
550 |
|
|
matching it against (C<m/^-?\d*(\.\d*)?(e-?\d*)?$/>). Inefficient? |
551 |
|
|
Yes. Patches welcome. |
552 |
cvsjoko |
1.1 |
|
553 |
|
|
=cut |
554 |
|
|
|
555 |
|
|
my $real_re = qr/^-?\d*(\.\d*)?(e-?\d*)?$/; |
556 |
|
|
sub check_real { |
557 |
joko |
1.3 |
croak "not a real number" |
558 |
|
|
if (${$_[0]} !~ m/$real_re/o); |
559 |
cvsjoko |
1.1 |
} |
560 |
|
|
|
561 |
joko |
1.3 |
=item check_obj |
562 |
cvsjoko |
1.1 |
|
563 |
joko |
1.3 |
checks that the supplied variable is a reference to a blessed object |
564 |
cvsjoko |
1.1 |
|
565 |
|
|
=cut |
566 |
|
|
|
567 |
|
|
# this pattern matches a regular reference |
568 |
|
|
my $obj_re = qr/^(?:HASH|ARRAY|SCALAR)?$/; |
569 |
|
|
sub check_obj { |
570 |
|
|
croak "not an object reference" |
571 |
joko |
1.3 |
if ((ref ${$_[0]}) =~ m/$obj_re/o); |
572 |
cvsjoko |
1.1 |
} |
573 |
|
|
|
574 |
joko |
1.3 |
=item check_flat_array |
575 |
cvsjoko |
1.1 |
|
576 |
joko |
1.3 |
checks that $value is a ref ARRAY and that all elements are unblessed |
577 |
|
|
scalars. Does NOT currently check that all values are of the correct |
578 |
|
|
type (int vs real vs string, etc) |
579 |
cvsjoko |
1.1 |
|
580 |
|
|
=cut |
581 |
|
|
|
582 |
|
|
sub check_flat_array { |
583 |
|
|
croak "not a flat array" |
584 |
|
|
if (ref ${$_[0]} ne "ARRAY"); |
585 |
joko |
1.3 |
croak "flat array may not contain references" |
586 |
|
|
if (map { (ref $_ ? "1" : ()) } @{$_[0]}); |
587 |
cvsjoko |
1.1 |
} |
588 |
|
|
|
589 |
joko |
1.3 |
=item check_array |
590 |
cvsjoko |
1.1 |
|
591 |
joko |
1.3 |
checks that $value is a ref ARRAY, and that each element in the array |
592 |
|
|
is a reference to a blessed object. |
593 |
cvsjoko |
1.1 |
|
594 |
|
|
=cut |
595 |
|
|
|
596 |
|
|
sub check_array { |
597 |
|
|
croak "array attribute not passed an array ref" |
598 |
|
|
if (ref ${$_[0]} ne "ARRAY"); |
599 |
|
|
for my $a (@{${$_[0]}}) { |
600 |
|
|
croak "member in array not an object reference" |
601 |
joko |
1.3 |
if ((ref $a) =~ m/$obj_re/o); |
602 |
cvsjoko |
1.1 |
} |
603 |
|
|
} |
604 |
|
|
|
605 |
joko |
1.3 |
=item check_set |
606 |
cvsjoko |
1.1 |
|
607 |
joko |
1.3 |
checks that $value->isa("Set::Object") |
608 |
cvsjoko |
1.1 |
|
609 |
|
|
=cut |
610 |
|
|
|
611 |
|
|
sub check_set { |
612 |
|
|
croak "set type not passed a Set::Object" |
613 |
joko |
1.3 |
unless (UNIVERSAL::isa(${$_[0]}, "Set::Object")); |
614 |
cvsjoko |
1.1 |
} |
615 |
|
|
|
616 |
joko |
1.3 |
=item check_rawdate |
617 |
cvsjoko |
1.1 |
|
618 |
joko |
1.3 |
checks that $value is of the form YYYY-MM-DD, or YYYYMMDD, or YYMMDD. |
619 |
cvsjoko |
1.1 |
|
620 |
|
|
=cut |
621 |
|
|
|
622 |
|
|
# YYYY-MM-DD HH:MM:SS |
623 |
joko |
1.3 |
my $rawdate_re = qr/^(?: \d{4}-\d{2}-\d{2} |
624 |
|
|
| (?:\d\d){3,4} |
625 |
|
|
)$/x; |
626 |
|
|
sub check_rawdate { |
627 |
|
|
croak "invalid SQL rawdate" |
628 |
|
|
unless (${$_[0]} =~ m/$rawdate_re/o); |
629 |
|
|
} |
630 |
|
|
|
631 |
|
|
=item check_rawtime |
632 |
|
|
|
633 |
|
|
checks that $value is of the form HH:MM(:SS)? |
634 |
|
|
|
635 |
|
|
=cut |
636 |
|
|
|
637 |
|
|
# YYYY-MM-DD HH:MM:SS |
638 |
|
|
my $rawtime_re = qr/^\d{1,2}:\d{2}(?::\d{2})?$/; |
639 |
|
|
sub check_rawtime { |
640 |
|
|
croak "invalid SQL rawtime" |
641 |
|
|
unless (${$_[0]} =~ m/$rawtime_re/o); |
642 |
cvsjoko |
1.1 |
} |
643 |
|
|
|
644 |
joko |
1.3 |
=item check_rawdatetime |
645 |
cvsjoko |
1.1 |
|
646 |
joko |
1.3 |
checks that $value is of the form YYYY-MM-DD HH:MM(:SS)? (the time |
647 |
|
|
and/or the date can be missing), or a string of numbers between 6 and |
648 |
|
|
14 numbers long. |
649 |
cvsjoko |
1.1 |
|
650 |
|
|
=cut |
651 |
|
|
|
652 |
joko |
1.3 |
my $rawdatetime_re = qr/^(?: |
653 |
|
|
# YYYY-MM-DD HH:MM:SS |
654 |
|
|
(?: (?:\d{4}-\d{2}-\d{2}\s+)? |
655 |
|
|
\d{1,2}:\d{2}(?::\d{2})? |
656 |
|
|
| \d{4}-\d{2}-\d{2} |
657 |
|
|
) |
658 |
|
|
| # YYMMDD, etc |
659 |
|
|
(?:\d\d){3,7} |
660 |
|
|
)$/x; |
661 |
|
|
sub check_rawdatetime { |
662 |
|
|
croak "invalid SQL rawdatetime dude" |
663 |
|
|
unless (${$_[0]} =~ m/$rawdatetime_re/o); |
664 |
cvsjoko |
1.1 |
} |
665 |
|
|
|
666 |
joko |
1.3 |
=item check_dmdatetime |
667 |
cvsjoko |
1.1 |
|
668 |
joko |
1.3 |
checks that $value is of the form YYYYMMDDHH:MM:SS, or those allowed |
669 |
|
|
for rawdatetime. |
670 |
cvsjoko |
1.1 |
|
671 |
|
|
=cut |
672 |
|
|
|
673 |
joko |
1.3 |
sub check_dmdatetime { |
674 |
|
|
croak "invalid SQL rawdatetime dude" |
675 |
|
|
unless (${$_[0]} =~ m/^\d{10}:\d\d:\d\d$|$rawdatetime_re/o); |
676 |
cvsjoko |
1.1 |
} |
677 |
|
|
|
678 |
joko |
1.3 |
=item check_flat_hash |
679 |
cvsjoko |
1.1 |
|
680 |
joko |
1.3 |
checks that $value is a ref HASH and all values are scalars. Does NOT |
681 |
|
|
currently check that all values are of the correct type (int vs real |
682 |
|
|
vs string, etc) |
683 |
cvsjoko |
1.1 |
|
684 |
|
|
=cut |
685 |
|
|
|
686 |
|
|
sub check_flat_hash { |
687 |
|
|
croak "not a hash" |
688 |
|
|
unless (ref ${$_[0]} eq "HASH"); |
689 |
|
|
while (my ($k, $v) = each %${$_[0]}) { |
690 |
|
|
croak "hash not flat" |
691 |
|
|
if (ref $k or ref $v); |
692 |
|
|
} |
693 |
|
|
} |
694 |
|
|
|
695 |
joko |
1.3 |
=item check_hash |
696 |
cvsjoko |
1.1 |
|
697 |
joko |
1.3 |
checks that $value is a ref HASH, that every key in the hash is a |
698 |
|
|
scalar, and that every value is a blessed object. |
699 |
cvsjoko |
1.1 |
|
700 |
|
|
=cut |
701 |
|
|
|
702 |
|
|
sub check_hash { |
703 |
|
|
croak "not a hash" |
704 |
|
|
unless (ref ${$_[0]} eq "HASH"); |
705 |
|
|
while (my ($k, $v) = each %${$_[0]}) { |
706 |
|
|
croak "hash key not flat" |
707 |
|
|
if (ref $k); |
708 |
|
|
croak "hash value not an object" |
709 |
|
|
if (ref $v !~ m/$obj_re/); |
710 |
|
|
} |
711 |
|
|
} |
712 |
|
|
|
713 |
joko |
1.3 |
=item check_nothing |
714 |
cvsjoko |
1.1 |
|
715 |
joko |
1.3 |
checks whether Australians like sport |
716 |
cvsjoko |
1.1 |
|
717 |
|
|
=cut |
718 |
|
|
|
719 |
|
|
sub check_nothing { } |
720 |
|
|
|
721 |
joko |
1.3 |
=back |
722 |
|
|
|
723 |
cvsjoko |
1.1 |
=item destroy_X ($instance, $attr) |
724 |
|
|
|
725 |
|
|
Similar story with the check_X series of functions, these are called |
726 |
|
|
during object destruction on every attribute that has a reference that |
727 |
|
|
might need breaking. Note: B<these functions all assume that |
728 |
|
|
attributes belonging to an object that is being destroyed may be |
729 |
|
|
destroyed also>. In other words, do not allow distinct objects to |
730 |
|
|
share Set::Object containers or hash references in their attributes, |
731 |
|
|
otherwise when one gets destroyed the others will lose their data. |
732 |
|
|
|
733 |
joko |
1.3 |
Available functions: |
734 |
|
|
|
735 |
|
|
=over |
736 |
cvsjoko |
1.1 |
|
737 |
joko |
1.3 |
=item destroy_array |
738 |
|
|
|
739 |
|
|
empties an array |
740 |
cvsjoko |
1.1 |
|
741 |
|
|
=cut |
742 |
|
|
|
743 |
|
|
sub destroy_array { |
744 |
joko |
1.3 |
my $self = shift; |
745 |
|
|
my $attr = shift; |
746 |
cvsjoko |
1.1 |
my $t = tied $self->{$attr}; |
747 |
joko |
1.3 |
@{$self->{$attr}} = () |
748 |
|
|
unless (defined $t and $t =~ m,Tangram::CollOnDemand,); |
749 |
cvsjoko |
1.1 |
delete $self->{$attr}; |
750 |
|
|
} |
751 |
|
|
|
752 |
joko |
1.3 |
=item destroy_set |
753 |
cvsjoko |
1.1 |
|
754 |
joko |
1.3 |
Calls Set::Object::clear to clear the set |
755 |
cvsjoko |
1.1 |
|
756 |
|
|
=cut |
757 |
|
|
|
758 |
|
|
sub destroy_set { |
759 |
joko |
1.3 |
my $self = shift; |
760 |
|
|
my $attr = shift; |
761 |
cvsjoko |
1.1 |
|
762 |
|
|
my $t = tied $self->{$attr}; |
763 |
joko |
1.3 |
return if (defined $t and $t =~ m,Tangram::CollOnDemand,); |
764 |
cvsjoko |
1.1 |
if (ref $self->{$attr} eq "Set::Object") { |
765 |
|
|
$self->{$attr}->clear; |
766 |
|
|
} |
767 |
|
|
delete $self->{$attr}; |
768 |
|
|
} |
769 |
|
|
|
770 |
joko |
1.3 |
=item destroy_hash |
771 |
cvsjoko |
1.1 |
|
772 |
joko |
1.3 |
empties a hash |
773 |
cvsjoko |
1.1 |
|
774 |
|
|
=cut |
775 |
|
|
|
776 |
|
|
sub destroy_hash { |
777 |
joko |
1.3 |
my $self = shift; |
778 |
|
|
my $attr = shift; |
779 |
cvsjoko |
1.1 |
my $t = tied $self->{$attr}; |
780 |
joko |
1.3 |
%{$self->{$attr}} = () |
781 |
|
|
unless (defined $t and $t =~ m,Tangram::CollOnDemand,); |
782 |
cvsjoko |
1.1 |
delete $self->{$attr}; |
783 |
|
|
} |
784 |
|
|
|
785 |
joko |
1.3 |
=item destroy_ref |
786 |
cvsjoko |
1.1 |
|
787 |
joko |
1.3 |
destroys a reference. |
788 |
cvsjoko |
1.1 |
|
789 |
|
|
=cut |
790 |
|
|
|
791 |
|
|
sub destroy_ref { |
792 |
joko |
1.3 |
my $self = shift; |
793 |
|
|
delete $self->{shift}; |
794 |
|
|
} |
795 |
cvsjoko |
1.1 |
|
796 |
joko |
1.3 |
=back |
797 |
cvsjoko |
1.1 |
|
798 |
|
|
=item parse_X ($attribute, { schema option }) |
799 |
|
|
|
800 |
|
|
Parses the schema option field, and returns one or two closures that |
801 |
|
|
act as a check_X and a destroy_X function for the attribute. |
802 |
|
|
|
803 |
|
|
This is currently a very ugly hack, parsing the SQL type definition of |
804 |
|
|
an object. But it was bloody handy in my case for hacking this in |
805 |
joko |
1.3 |
quickly. This is probably unmanagably unportable across databases; |
806 |
|
|
but send me bug reports on it anyway, and I'll try and make the |
807 |
|
|
parsers work for as many databases as possible. |
808 |
|
|
|
809 |
|
|
This perhaps should be replaced by primitives that go the other way, |
810 |
|
|
building the SQL type definition from a more abstract definition of |
811 |
|
|
the type. |
812 |
cvsjoko |
1.1 |
|
813 |
|
|
Available functions: |
814 |
|
|
|
815 |
joko |
1.3 |
=over |
816 |
|
|
|
817 |
|
|
=item parse_string |
818 |
|
|
|
819 |
|
|
parses SQL types of: |
820 |
|
|
|
821 |
|
|
=over |
822 |
cvsjoko |
1.1 |
|
823 |
|
|
=cut |
824 |
|
|
|
825 |
joko |
1.3 |
use vars qw($quoted_part $sql_list); |
826 |
|
|
|
827 |
|
|
$quoted_part = qr/(?: \"([^\"]+)\" | \'([^\']+)\' )/x; |
828 |
|
|
$sql_list = qr/\(\s* |
829 |
|
|
( |
830 |
|
|
$quoted_part |
831 |
|
|
(?:\s*,\s* $quoted_part )* |
832 |
|
|
) \s*\)/x; |
833 |
|
|
|
834 |
cvsjoko |
1.1 |
sub parse_string { |
835 |
|
|
|
836 |
joko |
1.3 |
my $attribute = shift; |
837 |
|
|
my $option = shift; |
838 |
cvsjoko |
1.1 |
|
839 |
|
|
# simple case; return the check_string function. We don't |
840 |
|
|
# need a destructor for a string so don't return one. |
841 |
|
|
if (!$option->{sql}) { |
842 |
|
|
return \&check_string; |
843 |
|
|
} |
844 |
|
|
|
845 |
joko |
1.3 |
=item CHAR(N), VARCHAR(N) |
846 |
cvsjoko |
1.1 |
|
847 |
joko |
1.3 |
closure checks length of string is less than N characters |
848 |
cvsjoko |
1.1 |
|
849 |
|
|
=cut |
850 |
|
|
|
851 |
|
|
if ($option->{sql} =~ m/^\s*(?:var)?char\s*\(\s*(\d+)\s*\)/ix) { |
852 |
|
|
my $max_length = $1; |
853 |
|
|
return sub { |
854 |
|
|
die "string too long for $attribute" |
855 |
|
|
if (length ${$_[0]} > $max_length); |
856 |
|
|
}; |
857 |
|
|
|
858 |
joko |
1.3 |
=item TINYBLOB, BLOB, LONGBLOB |
859 |
cvsjoko |
1.1 |
|
860 |
joko |
1.3 |
checks max. length of string to be 255, 65535 or 16777215 chars |
861 |
|
|
respectively. Also works with "TEXT" instead of "BLOB" |
862 |
cvsjoko |
1.1 |
|
863 |
|
|
=cut |
864 |
|
|
|
865 |
joko |
1.3 |
} elsif ($option->{sql} =~ m/^\s*(?:tiny|long|medium)? |
866 |
|
|
(?:blob|text)/ix) { |
867 |
cvsjoko |
1.1 |
my $max_length = ($1 ? ($1 eq "tiny"?255:2**24 - 1) |
868 |
|
|
: 2**16 - 1); |
869 |
|
|
return sub { |
870 |
|
|
die "string too long for $attribute" |
871 |
joko |
1.3 |
if (${$_[0]} and length ${$_[0]} > $max_length); |
872 |
cvsjoko |
1.1 |
}; |
873 |
|
|
|
874 |
joko |
1.3 |
=item SET("members", "of", "set") |
875 |
cvsjoko |
1.1 |
|
876 |
joko |
1.3 |
checks that the value passed is valid as a SQL set type, and that all |
877 |
|
|
of the passed values are allowed to be a member of that set. |
878 |
cvsjoko |
1.1 |
|
879 |
|
|
=cut |
880 |
|
|
|
881 |
joko |
1.3 |
} elsif (my ($members) = $option->{sql} =~ |
882 |
|
|
m/^\s*set\s*$sql_list/oi) { |
883 |
|
|
|
884 |
|
|
my %members; |
885 |
|
|
$members{lc($1 || $2)} = 1 |
886 |
|
|
while ( $members =~ m/\G[,\s]*$quoted_part/cog ); |
887 |
|
|
|
888 |
cvsjoko |
1.1 |
return sub { |
889 |
joko |
1.3 |
for my $x (split /\s*,\s*/, ${$_[0]}) { |
890 |
cvsjoko |
1.1 |
croak ("SQL set badly formed or invalid member $x " |
891 |
|
|
." (SET" . join(",", keys %members). ")") |
892 |
|
|
if (not exists $members{lc($x)}); |
893 |
|
|
} |
894 |
|
|
}; |
895 |
|
|
|
896 |
joko |
1.3 |
=item ENUM("possible", "values") |
897 |
cvsjoko |
1.1 |
|
898 |
joko |
1.3 |
checks that the value passed is one of the allowed values. |
899 |
cvsjoko |
1.1 |
|
900 |
|
|
=cut |
901 |
|
|
|
902 |
joko |
1.3 |
} elsif (my ($values) = $option->{sql} =~ |
903 |
|
|
m/^\s*enum\s*$sql_list/oi ) { |
904 |
|
|
|
905 |
|
|
my %values; |
906 |
|
|
$values{lc($1 || $2)} = 1 |
907 |
|
|
while ( $values =~ m/\G[,\s]*$quoted_part/gc); |
908 |
|
|
|
909 |
cvsjoko |
1.1 |
return sub { |
910 |
|
|
croak ("invalid enum value ${$_[0]} must be (" |
911 |
|
|
. join(",", keys %values). ")") |
912 |
|
|
if (not exists $values{lc(${$_[0]})}); |
913 |
|
|
} |
914 |
|
|
|
915 |
joko |
1.3 |
|
916 |
cvsjoko |
1.1 |
} else { |
917 |
|
|
die ("Please build support for your string SQL type in " |
918 |
|
|
."Class::Tangram (".$option->{sql}.")"); |
919 |
|
|
} |
920 |
|
|
} |
921 |
|
|
|
922 |
joko |
1.3 |
=back |
923 |
cvsjoko |
1.1 |
|
924 |
joko |
1.3 |
=back |
925 |
cvsjoko |
1.1 |
|
926 |
joko |
1.3 |
=back |
927 |
cvsjoko |
1.1 |
|
928 |
joko |
1.3 |
=head2 Quick Object Dumping and Destruction |
929 |
cvsjoko |
1.1 |
|
930 |
joko |
1.3 |
=over |
931 |
cvsjoko |
1.1 |
|
932 |
|
|
=item $instance->quickdump |
933 |
|
|
|
934 |
|
|
Quickly show the blessed hash of an object, without descending into |
935 |
|
|
it. Primarily useful when you have a large interconnected graph of |
936 |
|
|
objects so don't want to use the B<x> command within the debugger. |
937 |
|
|
It also doesn't have the side effect of auto-vivifying members. |
938 |
|
|
|
939 |
|
|
This function returns a string, suitable for print()ing. It does not |
940 |
|
|
currently escape unprintable characters. |
941 |
|
|
|
942 |
|
|
=cut |
943 |
|
|
|
944 |
|
|
sub quickdump($) { |
945 |
joko |
1.3 |
my $self = shift; |
946 |
cvsjoko |
1.1 |
|
947 |
|
|
my $r = "REF ". (ref $self). "\n"; |
948 |
|
|
for my $k (sort keys %$self) { |
949 |
|
|
$r .= (" $k => " |
950 |
|
|
. ( |
951 |
|
|
tied $self->{$k} |
952 |
|
|
|| ( ref $self->{$k} |
953 |
|
|
? $self->{$k} |
954 |
|
|
: "'".$self->{$k}."'" ) |
955 |
|
|
) |
956 |
|
|
. "\n"); |
957 |
|
|
} |
958 |
|
|
return $r; |
959 |
|
|
} |
960 |
|
|
|
961 |
|
|
|
962 |
|
|
=item $instance->DESTROY |
963 |
|
|
|
964 |
|
|
This function ensures that all of your attributes have their |
965 |
|
|
destructors called. It calls the destroy_X function for attributes |
966 |
|
|
that have it defined, if that attribute exists in the instance that we |
967 |
|
|
are destroying. It calls the destroy_X functions as destroy_X($self, |
968 |
|
|
$k) |
969 |
|
|
|
970 |
|
|
=cut |
971 |
|
|
|
972 |
|
|
sub DESTROY($) { |
973 |
joko |
1.3 |
my $self = shift; |
974 |
cvsjoko |
1.1 |
|
975 |
|
|
my $class = ref $self; |
976 |
|
|
|
977 |
|
|
# if no cleaners are known for this class, it hasn't been imported |
978 |
|
|
# yet. Don't call import_schema, that would be a bad idea in a |
979 |
|
|
# destructor. |
980 |
|
|
exists $cleaners{$class} or return; |
981 |
|
|
|
982 |
|
|
# for every attribute that is defined, and has a cleaner function, |
983 |
|
|
# call the cleaner function. |
984 |
|
|
for my $k (keys %$self) { |
985 |
|
|
if (defined $cleaners{$class}->{$k} and exists $self->{$k}) { |
986 |
|
|
$cleaners{$class}->{$k}->($self, $k); |
987 |
|
|
} |
988 |
|
|
} |
989 |
|
|
$self->{_DESTROYED} = 1; |
990 |
|
|
} |
991 |
|
|
|
992 |
|
|
=item $instance->clear_refs |
993 |
|
|
|
994 |
|
|
This clears all references from this object, ie exactly what DESTROY |
995 |
|
|
normally does, but calling an object's destructor method directly is |
996 |
|
|
bad form. Also, this function has no qualms with loading the class' |
997 |
|
|
schema with import_schema() as needed. |
998 |
|
|
|
999 |
|
|
This is useful for breaking circular references, if you know you are |
1000 |
|
|
no longer going to be using an object then you can call this method, |
1001 |
|
|
which in many cases will end up cleaning up most of the objects you |
1002 |
|
|
want to get rid of. |
1003 |
|
|
|
1004 |
|
|
However, it still won't do anything about Tangram's internal reference |
1005 |
|
|
to the object, which must still be explicitly unlinked with the |
1006 |
|
|
Tangram::Storage->unload method. |
1007 |
|
|
|
1008 |
|
|
=cut |
1009 |
|
|
|
1010 |
|
|
sub clear_refs($) { |
1011 |
joko |
1.3 |
my $self = shift; |
1012 |
cvsjoko |
1.1 |
my $class = ref $self; |
1013 |
|
|
|
1014 |
|
|
exists $cleaners{$class} or import_schema($class); |
1015 |
|
|
|
1016 |
|
|
# break all ref's, sets, arrays |
1017 |
|
|
for my $k (keys %$self) { |
1018 |
|
|
if (defined $cleaners{$class}->{$k} and exists $self->{$k}) { |
1019 |
|
|
$cleaners{$class}->{$k}->($self, $k); |
1020 |
|
|
} |
1021 |
|
|
} |
1022 |
|
|
$self->{_NOREFS} = 1; |
1023 |
|
|
} |
1024 |
|
|
|
1025 |
joko |
1.3 |
=back |
1026 |
|
|
|
1027 |
|
|
=head1 FUNCTIONS |
1028 |
|
|
|
1029 |
|
|
The following functions are not intended to be called as object |
1030 |
|
|
methods. |
1031 |
|
|
|
1032 |
|
|
=head2 Schema Import |
1033 |
|
|
|
1034 |
|
|
our $fields = { int => [ qw(foo bar) ], |
1035 |
|
|
string => [ qw(baz quux) ] }; |
1036 |
|
|
|
1037 |
|
|
# Version 1.115 and below compatibility: |
1038 |
|
|
our $schema = { |
1039 |
|
|
fields => { int => [ qw(foo bar) ], |
1040 |
|
|
string => [ qw(baz quux) ] } |
1041 |
|
|
}; |
1042 |
|
|
|
1043 |
|
|
=over |
1044 |
|
|
|
1045 |
|
|
=item Class::Tangram::import_schema($class) |
1046 |
|
|
|
1047 |
|
|
Parses a tangram object field list, in C<${"${class}::fields"}> (or |
1048 |
|
|
C<${"${class}::schema"}-E<gt>{fields}> to the internal type information |
1049 |
|
|
hashes. It will also define all of the attribute accessor and update |
1050 |
|
|
methods in the C<$class> package. |
1051 |
|
|
|
1052 |
|
|
Note that calling this function twice for the same class is not |
1053 |
|
|
tested and may produce arbitrary results. Patches welcome. |
1054 |
|
|
|
1055 |
|
|
=cut |
1056 |
|
|
|
1057 |
|
|
sub import_schema($) { # Damn this function is long |
1058 |
|
|
my $class = shift; |
1059 |
|
|
|
1060 |
|
|
eval { |
1061 |
|
|
my ($fields, $bases, $abstract); |
1062 |
|
|
{ |
1063 |
|
|
|
1064 |
|
|
# Here, we go hunting around for their defined schema and |
1065 |
|
|
# options |
1066 |
|
|
no strict 'refs'; |
1067 |
|
|
local $^W=0; |
1068 |
|
|
eval { |
1069 |
|
|
$fields = (${"${class}::fields"} || |
1070 |
|
|
${"${class}::schema"}->{fields}); |
1071 |
|
|
$abstract = (${"${class}::abstract"} || |
1072 |
|
|
${"${class}::schema"}->{abstract}); |
1073 |
|
|
$bases = ${"${class}::schema"}->{bases}; |
1074 |
|
|
}; |
1075 |
|
|
if ( my @stack = @{"${class}::ISA"}) { |
1076 |
|
|
# clean "bases" information from @ISA |
1077 |
|
|
my %seen = map { $_ => 1 } $class, __PACKAGE__; |
1078 |
|
|
$bases = []; |
1079 |
|
|
while ( my $super = pop @stack ) { |
1080 |
|
|
if ( defined ${"${super}::schema"} |
1081 |
|
|
or defined ${"${super}::fields"} ) { |
1082 |
|
|
push @$bases, $super; |
1083 |
|
|
} else { |
1084 |
|
|
push @stack, grep { !$seen{$_}++ } |
1085 |
|
|
@{"${super}::ISA"}; |
1086 |
|
|
} |
1087 |
|
|
} |
1088 |
|
|
if ( !$fields and !@$bases ) { |
1089 |
|
|
die ("No schema and no Class::Tangram " |
1090 |
|
|
."superclass for $class; define " |
1091 |
|
|
."${class}::fields!"); |
1092 |
|
|
} |
1093 |
|
|
} |
1094 |
|
|
} |
1095 |
|
|
|
1096 |
|
|
# if this is an abstract type, do not allow it to be |
1097 |
|
|
# instantiated |
1098 |
|
|
if ($abstract) { |
1099 |
|
|
$abstract{$class} = 1; |
1100 |
|
|
} |
1101 |
|
|
|
1102 |
|
|
# If there are any base classes, import them first so that the |
1103 |
|
|
# check, cleaners and init_defaults can be inherited |
1104 |
|
|
if (defined $bases) { |
1105 |
|
|
(ref $bases eq "ARRAY") |
1106 |
|
|
or die "bases not an array ref for $class"; |
1107 |
|
|
|
1108 |
|
|
# Note that the order of your bases is significant, that |
1109 |
|
|
# is if you are using multiple iheritance then the later |
1110 |
|
|
# classes override the earlier ones. |
1111 |
|
|
for my $super ( @$bases ) { |
1112 |
|
|
import_schema $super unless (exists $check{$super}); |
1113 |
|
|
|
1114 |
|
|
# copy each of the per-class configuration hashes to |
1115 |
|
|
# this class as defaults. |
1116 |
|
|
my ($k, $v); |
1117 |
|
|
|
1118 |
|
|
# FIXME - this repetition of code is getting silly :) |
1119 |
|
|
$types{$class}->{$k} = $v |
1120 |
|
|
while (($k, $v) = each %{ $types{$super} } ); |
1121 |
|
|
$check{$class}->{$k} = $v |
1122 |
|
|
while (($k, $v) = each %{ $check{$super} } ); |
1123 |
|
|
$cleaners{$class}->{$k} = $v |
1124 |
|
|
while (($k, $v) = each %{ $cleaners{$super} } ); |
1125 |
|
|
$attribute_options{$class}->{$k} = $v |
1126 |
|
|
while (($k, $v) = each %{ $attribute_options{$super} } ); |
1127 |
|
|
$init_defaults{$class}->{$k} = $v |
1128 |
|
|
while (($k, $v) = each %{ $init_defaults{$super} } ); |
1129 |
|
|
$required_attributes{$class}->{$k} = $v |
1130 |
|
|
while (($k, $v) = each %{ $required_attributes{$super} } ); |
1131 |
|
|
} |
1132 |
|
|
} |
1133 |
|
|
|
1134 |
|
|
# iterate over each of the *types* of fields (string, int, ref, etc.) |
1135 |
|
|
while (my ($type, $v) = each %$fields) { |
1136 |
|
|
if (ref $v eq "ARRAY") { |
1137 |
|
|
$v = { map { $_, undef } @$v }; |
1138 |
|
|
} |
1139 |
|
|
my $def = $defaults{$type}; |
1140 |
|
|
|
1141 |
|
|
# iterate each of the *attributes* of a particular type |
1142 |
|
|
while (my ($attribute, $options) = each %$v) { |
1143 |
|
|
|
1144 |
|
|
# this is what we are finding out about each attribute |
1145 |
|
|
# $type is already set |
1146 |
|
|
my ($default, $check_func, $required, $cleaner); |
1147 |
|
|
# set defaults from what they give |
1148 |
|
|
$options ||= {}; |
1149 |
|
|
if (ref $options eq "HASH" or |
1150 |
|
|
UNIVERSAL::isa($options, 'Tangram::Type')) { |
1151 |
|
|
($check_func, $default, $required, $cleaner) |
1152 |
|
|
= @{$options}{qw(check_func init_default |
1153 |
|
|
required destroy_func)}; |
1154 |
|
|
} |
1155 |
|
|
|
1156 |
|
|
# Fill their settings with info from defaults |
1157 |
|
|
if (ref $def eq "HASH") { |
1158 |
|
|
|
1159 |
|
|
# try to magically parse their options |
1160 |
|
|
if ( $def->{parse} and !($check_func and $cleaner) ) { |
1161 |
|
|
my @a = $def->{parse}->($attribute, $options); |
1162 |
|
|
$check_func ||= $a[0]; |
1163 |
|
|
$cleaner ||= $a[1]; |
1164 |
|
|
} |
1165 |
|
|
|
1166 |
|
|
# fall back to defaults for this class |
1167 |
|
|
$check_func ||= $def->{check_func}; |
1168 |
|
|
$cleaner ||= $def->{destroy_func}; |
1169 |
|
|
$default = $def->{init_default} unless defined $default; |
1170 |
|
|
} |
1171 |
|
|
|
1172 |
|
|
# everything must be checked! |
1173 |
|
|
die "No check function for ${class}\->$attribute (type $type)" |
1174 |
|
|
unless (ref $check_func eq "CODE"); |
1175 |
|
|
|
1176 |
|
|
$types{$class}->{$attribute} = $type; |
1177 |
|
|
$check{$class}->{$attribute} = $check_func; |
1178 |
|
|
{ |
1179 |
|
|
no strict "refs"; |
1180 |
|
|
local ($^W) = 0; |
1181 |
|
|
|
1182 |
|
|
# build an appropriate "get_attribute" method, and |
1183 |
|
|
# define other per-type methods |
1184 |
|
|
my ($get_closure, $set_closure); |
1185 |
|
|
|
1186 |
|
|
# implement with closures for speed |
1187 |
|
|
if ( $type =~ m/i?set/ ) { |
1188 |
|
|
|
1189 |
|
|
# GET_$attribute (Set::Object) |
1190 |
|
|
$get_closure = sub { |
1191 |
|
|
my $self = shift; |
1192 |
|
|
if ( !defined $self->{$attribute} ) { |
1193 |
|
|
$self->{$attribute} = Set::Object->new(); |
1194 |
|
|
} |
1195 |
|
|
my $set = $self->{$attribute}; |
1196 |
|
|
( wantarray ? $set->members : $set ) |
1197 |
|
|
}; |
1198 |
|
|
|
1199 |
|
|
# and add a whole load of other functions too |
1200 |
|
|
for my $set_method (qw(includes insert size clear |
1201 |
|
|
remove)) { |
1202 |
|
|
|
1203 |
|
|
# ${attribute}_includes, etc |
1204 |
|
|
my $set_method_closure = sub { |
1205 |
|
|
my $self = shift; |
1206 |
|
|
$self->{$attribute} = Set::Object->new() |
1207 |
|
|
unless defined $self->{$attribute}; |
1208 |
|
|
return $self->{$attribute}->$set_method(@_); |
1209 |
|
|
}; |
1210 |
|
|
*{$class."::${attribute}_$set_method"} = |
1211 |
|
|
$set_method_closure unless |
1212 |
|
|
(defined &{$class."::${attribute}_$set_method"}); |
1213 |
|
|
} |
1214 |
|
|
|
1215 |
|
|
} elsif ( $type =~ m/i?array/ ) { |
1216 |
|
|
|
1217 |
|
|
# GET_$attribute (array) |
1218 |
|
|
# allow array slices, and return whole array |
1219 |
|
|
# in list context |
1220 |
|
|
$get_closure = sub { |
1221 |
|
|
my $array = ($_[0]->{$attribute} ||= []); |
1222 |
|
|
shift; |
1223 |
|
|
if ( @_ ) { |
1224 |
|
|
@{$array}[@_]; |
1225 |
|
|
} else { |
1226 |
|
|
( wantarray ? @{ $array } : $array ) |
1227 |
|
|
} |
1228 |
|
|
}; |
1229 |
|
|
|
1230 |
|
|
} elsif ( $type =~ m/i?hash/ ) { |
1231 |
|
|
# GET_$attribute (hash) |
1232 |
|
|
# allow hash slices, and return whole hash in |
1233 |
|
|
# list context |
1234 |
|
|
$get_closure = sub { |
1235 |
|
|
my $hash = ($_[0]->{$attribute} ||= {}); |
1236 |
|
|
shift; |
1237 |
|
|
if ( @_ ) { |
1238 |
|
|
@{$hash}{@_} |
1239 |
|
|
} else { |
1240 |
|
|
( wantarray ? %{ $hash } : $hash ); |
1241 |
|
|
} |
1242 |
|
|
}; |
1243 |
|
|
} else { |
1244 |
|
|
# GET_$attribute (scalar) |
1245 |
|
|
# return value only |
1246 |
|
|
$get_closure = sub { $_[0]->{$attribute}; }; |
1247 |
|
|
} |
1248 |
|
|
|
1249 |
|
|
*{$class."::get_$attribute"} = $get_closure |
1250 |
|
|
unless (defined &{$class."::get_$attribute"}); |
1251 |
|
|
|
1252 |
|
|
# SET_$attribute (all) |
1253 |
|
|
my $checkit = \$check{$class}->{$attribute}; |
1254 |
|
|
|
1255 |
|
|
# required hack for strings - duplicate the code |
1256 |
|
|
# to avoid the following string comparison for |
1257 |
|
|
# every set |
1258 |
|
|
if ( $type eq "string" ) { |
1259 |
|
|
$set_closure = sub { |
1260 |
|
|
my $self = shift; |
1261 |
|
|
my $value = shift; |
1262 |
|
|
eval { |
1263 |
|
|
if ( defined $value and length $value ) { |
1264 |
|
|
${$checkit}->(\$value); |
1265 |
|
|
} elsif ( $required ) { |
1266 |
|
|
die "value is required" |
1267 |
|
|
} elsif ( defined $required ) { |
1268 |
|
|
die "value must be defined" |
1269 |
|
|
unless defined $value; |
1270 |
|
|
} |
1271 |
|
|
}; |
1272 |
|
|
$@ && croak("value failed type check - ${class}->" |
1273 |
|
|
."set_$attribute('$value') ($@)"); |
1274 |
|
|
$self->{$attribute} = $value; |
1275 |
|
|
}; |
1276 |
|
|
} else { |
1277 |
|
|
$set_closure = sub { |
1278 |
|
|
my $self = shift; |
1279 |
|
|
my $value = shift; |
1280 |
|
|
eval { |
1281 |
|
|
if ( $value ) { |
1282 |
|
|
${$checkit}->(\$value); |
1283 |
|
|
} elsif ( $required ) { |
1284 |
|
|
die "value is required" |
1285 |
|
|
} elsif ( defined $required ) { |
1286 |
|
|
die "value must be defined" |
1287 |
|
|
unless defined $value; |
1288 |
|
|
} |
1289 |
|
|
}; |
1290 |
|
|
$@ && croak("value failed type check - ${class}->" |
1291 |
|
|
."set_$attribute('$value') ($@)"); |
1292 |
|
|
$self->{$attribute} = $value; |
1293 |
|
|
}; |
1294 |
|
|
} |
1295 |
|
|
|
1296 |
|
|
# now export them into the caller's namespace |
1297 |
|
|
my ($getter, $setter) |
1298 |
|
|
= ("get_$attribute", "set_$attribute"); |
1299 |
|
|
*{$class."::$getter"} = $get_closure |
1300 |
|
|
unless defined &{$class."::$getter"}; |
1301 |
|
|
*{$class."::$setter"} = $set_closure |
1302 |
|
|
unless defined &{$class."::$setter"}; |
1303 |
|
|
|
1304 |
|
|
*{$class."::$attribute"} = sub { |
1305 |
|
|
my $self = shift; |
1306 |
|
|
if ( @_ ) { |
1307 |
|
|
warn("The OO Police say change your call " |
1308 |
|
|
."to ->set_$attribute") if ($^W); |
1309 |
|
|
#goto $set_closure; # NO! BAD!! :-) |
1310 |
|
|
return $self->$setter(@_); |
1311 |
|
|
} else { |
1312 |
|
|
return $self->$getter(@_); |
1313 |
|
|
#goto $get_closure; |
1314 |
|
|
} |
1315 |
|
|
} unless defined &{$class."::$attribute"}; |
1316 |
|
|
} |
1317 |
|
|
|
1318 |
|
|
$cleaners{$class}->{$attribute} = $cleaner |
1319 |
|
|
if (defined $cleaner); |
1320 |
|
|
$init_defaults{$class}->{$attribute} = $default |
1321 |
|
|
if (defined $default); |
1322 |
|
|
$required_attributes{$class}->{$attribute} = $required |
1323 |
|
|
if (defined $required); |
1324 |
|
|
$attribute_options{$class}->{$attribute} = |
1325 |
|
|
( $options || {} ); |
1326 |
|
|
} |
1327 |
|
|
} |
1328 |
|
|
}; |
1329 |
|
|
|
1330 |
|
|
$@ && die "$@ while trying to import schema for $class"; |
1331 |
|
|
} |
1332 |
|
|
|
1333 |
|
|
=back |
1334 |
|
|
|
1335 |
|
|
=head2 Run-time type information |
1336 |
|
|
|
1337 |
|
|
It is possible to access the data structures that Class::Tangram uses |
1338 |
|
|
internally to verify attributes, create objects and so on. |
1339 |
|
|
|
1340 |
|
|
This should be considered a B<HIGHLY EXPERIMENTAL> interface to |
1341 |
|
|
B<INTERNALS> of Class::Tangram. |
1342 |
|
|
|
1343 |
|
|
Class::Tangram keeps seven internal hashes: |
1344 |
|
|
|
1345 |
|
|
=over |
1346 |
|
|
|
1347 |
|
|
=item C<%types> |
1348 |
|
|
|
1349 |
|
|
C<$types{$class}-E<gt>{$attribute}> is the tangram type of each attribute, |
1350 |
|
|
ie "ref", "iset", etc. See L<Tangram::Type>. |
1351 |
|
|
|
1352 |
|
|
=item C<%attribute_options> |
1353 |
|
|
|
1354 |
|
|
C<$attribute_options{$class}-E<gt>{$attribute}> is the options hash |
1355 |
|
|
for a given attribute. |
1356 |
|
|
|
1357 |
|
|
=item C<%required_attributes> |
1358 |
|
|
|
1359 |
|
|
C<$required_attributes{$class}-E<gt>{$attribute}> is the 'required' |
1360 |
|
|
option setting for a given attribute. |
1361 |
|
|
|
1362 |
|
|
=item C<%check> |
1363 |
|
|
|
1364 |
|
|
C<$check{$class}-E<gt>{$attribute}> is a function that will be passed |
1365 |
|
|
a reference to the value to be checked and either throw an exception |
1366 |
|
|
(die) or return true. |
1367 |
|
|
|
1368 |
|
|
=item C<%cleaners> |
1369 |
|
|
|
1370 |
|
|
C<$attribute_options{$class}-E<gt>{$attribute}> is a reference to a |
1371 |
|
|
destructor function for that attribute. It is called as an object |
1372 |
|
|
method on the object being destroyed, and should ensure that any |
1373 |
|
|
circular references that this object is involved in get cleared. |
1374 |
|
|
|
1375 |
|
|
=item C<%abstract> |
1376 |
|
|
|
1377 |
|
|
C<$abstract-E<gt>{$class}> is set if the class is abstract |
1378 |
|
|
|
1379 |
|
|
=item C<%init_defaults> |
1380 |
|
|
|
1381 |
|
|
C<$init_defaults{$class}-E<gt>{$attribute}> represents what an |
1382 |
|
|
attribute is set to automatically if it is not specified when an |
1383 |
|
|
object is created. If this is a scalar value, the attribute is set to |
1384 |
|
|
the value. If it is a function, then that function is called (as a |
1385 |
|
|
method) and should return the value to be placed into that attribute. |
1386 |
|
|
If it is a hash ref or an array ref, then that structure is COPIED in |
1387 |
|
|
to the new object. If you don't want that, you can do something like |
1388 |
|
|
this: |
1389 |
|
|
|
1390 |
|
|
[...] |
1391 |
|
|
flat_hash => { |
1392 |
|
|
attribute => { |
1393 |
|
|
init_default => sub { { key => "value" } }, |
1394 |
|
|
}, |
1395 |
|
|
}, |
1396 |
|
|
[...] |
1397 |
|
|
|
1398 |
|
|
Now, every new object will share the same hash for that attribute. |
1399 |
cvsjoko |
1.1 |
|
1400 |
|
|
=back |
1401 |
|
|
|
1402 |
joko |
1.3 |
There are currently four functions that allow you to access parts of |
1403 |
|
|
this information. |
1404 |
|
|
|
1405 |
|
|
=over |
1406 |
|
|
|
1407 |
|
|
=item Class::Tangram::attribute_options($class) |
1408 |
|
|
|
1409 |
|
|
Returns a hash ref to a data structure from attribute names to the |
1410 |
|
|
option hash for that attribute. |
1411 |
|
|
|
1412 |
|
|
=cut |
1413 |
|
|
|
1414 |
|
|
sub attribute_options($) { |
1415 |
|
|
my $class = shift; |
1416 |
|
|
return $attribute_options{$class}; |
1417 |
|
|
} |
1418 |
|
|
|
1419 |
|
|
=item Class::Tangram::attribute_types($class) |
1420 |
|
|
|
1421 |
|
|
Returns a hash ref from attribute names to the tangram type for that |
1422 |
|
|
attribute. |
1423 |
|
|
|
1424 |
|
|
=cut |
1425 |
|
|
|
1426 |
|
|
sub attribute_types($) { |
1427 |
|
|
my $class = shift; |
1428 |
|
|
return $types{$class}; |
1429 |
|
|
} |
1430 |
|
|
|
1431 |
|
|
=item Class::Tangram::required_attributes($class) |
1432 |
|
|
|
1433 |
|
|
Returns a hash ref from attribute names to the 'required' option setting for |
1434 |
|
|
that attribute. May also be called as a method, as in |
1435 |
|
|
C<$instance-E<gt>required_attributes>. |
1436 |
|
|
|
1437 |
|
|
=cut |
1438 |
|
|
|
1439 |
|
|
sub required_attributes($) { |
1440 |
|
|
my $class = ref $_[0] || $_[0]; |
1441 |
|
|
return $required_attributes{$class}; |
1442 |
|
|
} |
1443 |
|
|
|
1444 |
|
|
=item Class::Tangram::init_defaults($class) |
1445 |
|
|
|
1446 |
|
|
Returns a hash ref from attribute names to the default intial values for |
1447 |
|
|
that attribute. May also be called as a method, as in |
1448 |
|
|
C<$instance-E<gt>init_defaults>. |
1449 |
|
|
|
1450 |
|
|
=cut |
1451 |
|
|
|
1452 |
|
|
sub init_defaults($) { |
1453 |
|
|
my $class = ref $_[0] || $_[0]; |
1454 |
|
|
return $init_defaults{$class}; |
1455 |
|
|
} |
1456 |
|
|
|
1457 |
|
|
=item Class::Tangram::known_classes |
1458 |
|
|
|
1459 |
|
|
This function returns a list of all the classes that have had their |
1460 |
|
|
object schema imported by Class::Tangram. |
1461 |
|
|
|
1462 |
|
|
=cut |
1463 |
|
|
|
1464 |
|
|
sub known_classes { |
1465 |
|
|
return keys %types; |
1466 |
|
|
} |
1467 |
|
|
|
1468 |
|
|
=item Class::Tangram::is_abstract($class) |
1469 |
|
|
|
1470 |
|
|
This function returns true if the supplied class is abstract. |
1471 |
|
|
|
1472 |
|
|
=cut |
1473 |
|
|
|
1474 |
|
|
sub is_abstract { |
1475 |
|
|
my $class = shift; |
1476 |
|
|
$class eq "Class::Tangram" && ($class = shift); |
1477 |
|
|
|
1478 |
|
|
exists $cleaners{$class} or import_schema($class); |
1479 |
|
|
} |
1480 |
|
|
|
1481 |
|
|
=item Class->set_init_default(attribute => $value); |
1482 |
|
|
|
1483 |
|
|
Sets the default value on an attribute for newly created "Class" |
1484 |
|
|
objects, as if it had been declared with init_default. Can be called |
1485 |
|
|
as a class or an instance method. |
1486 |
|
|
|
1487 |
|
|
=cut |
1488 |
|
|
|
1489 |
|
|
sub set_init_default { |
1490 |
|
|
my $invocant = shift; |
1491 |
|
|
my $class = ref $invocant || $invocant; |
1492 |
|
|
|
1493 |
|
|
exists $init_defaults{$class} or import_schema($class); |
1494 |
|
|
|
1495 |
|
|
while ( my ($attribute, $value) = splice @_, 0, 2) { |
1496 |
|
|
$init_defaults{$class}->{$attribute} = $value; |
1497 |
|
|
} |
1498 |
|
|
} |
1499 |
|
|
|
1500 |
|
|
=back |
1501 |
|
|
|
1502 |
|
|
=cut |
1503 |
|
|
|
1504 |
|
|
# a little embedded package |
1505 |
|
|
|
1506 |
|
|
package Tangram::Transient; |
1507 |
|
|
|
1508 |
|
|
BEGIN { |
1509 |
|
|
eval "use base qw(Tangram::Type)"; |
1510 |
|
|
if ( $@ ) { |
1511 |
|
|
# no tangram |
1512 |
|
|
} else { |
1513 |
|
|
$Tangram::Schema::TYPES{transient} = bless {}, __PACKAGE__; |
1514 |
|
|
} |
1515 |
|
|
} |
1516 |
|
|
|
1517 |
|
|
sub coldefs { } |
1518 |
|
|
|
1519 |
|
|
sub get_exporter { } |
1520 |
|
|
sub get_importer { } |
1521 |
|
|
|
1522 |
|
|
sub get_import_cols { |
1523 |
|
|
# print "Get_import_cols:" , Dumper \@_; |
1524 |
|
|
return (); |
1525 |
|
|
} |
1526 |
|
|
|
1527 |
cvsjoko |
1.1 |
=head1 SEE ALSO |
1528 |
|
|
|
1529 |
|
|
L<Tangram::Schema> |
1530 |
|
|
|
1531 |
|
|
B<A guided tour of Tangram, by Sound Object Logic.> |
1532 |
|
|
|
1533 |
|
|
http://www.soundobjectlogic.com/tangram/guided_tour/fs.html |
1534 |
|
|
|
1535 |
joko |
1.3 |
=head1 DEPENDENCIES |
1536 |
|
|
|
1537 |
|
|
The following modules are required to be installed to use |
1538 |
|
|
Class::Tangram: |
1539 |
|
|
|
1540 |
|
|
Set::Object => 1.02 |
1541 |
|
|
Pod::Constants => 0.11 |
1542 |
|
|
Test::Simple => 0.18 |
1543 |
|
|
Date::Manip => 5.21 |
1544 |
|
|
|
1545 |
|
|
Test::Simple and Date::Manip are only required to run the test suite. |
1546 |
|
|
|
1547 |
|
|
If you find Class::Tangram passes the test suite with earlier versions |
1548 |
|
|
of the above modules, please send me an e-mail. |
1549 |
|
|
|
1550 |
|
|
=head2 MODULE RELEASE |
1551 |
|
|
|
1552 |
|
|
This is Class::Tangram version 1.12. |
1553 |
|
|
|
1554 |
cvsjoko |
1.1 |
=head1 BUGS/TODO |
1555 |
|
|
|
1556 |
joko |
1.3 |
There should be more functions for breaking loops; in particular, a |
1557 |
|
|
standard function called C<drop_refs($obj)>, which replaces references |
1558 |
|
|
to $obj with the appropriate C<Tangram::RefOnDemand> object so that an |
1559 |
|
|
object can be unloaded via C<Tangram::Storage->unload()> and actually |
1560 |
|
|
have a hope of being reclaimed. Another function that would be handy |
1561 |
|
|
would be a deep "mark" operation for manual mark & sweep garbage |
1562 |
|
|
collection. |
1563 |
|
|
|
1564 |
|
|
Need to think about writing some functions using C<Inline> for speed. |
1565 |
|
|
One of these days... |
1566 |
cvsjoko |
1.1 |
|
1567 |
joko |
1.3 |
Allow C<init_default> values to be set in a default import function? |
1568 |
cvsjoko |
1.1 |
|
1569 |
joko |
1.3 |
ie |
1570 |
cvsjoko |
1.1 |
|
1571 |
joko |
1.3 |
use MyClassTangramObject -defaults => { foo => "bar" }; |
1572 |
cvsjoko |
1.1 |
|
1573 |
|
|
=head1 AUTHOR |
1574 |
|
|
|
1575 |
|
|
Sam Vilain, <sam@vilain.net> |
1576 |
|
|
|
1577 |
joko |
1.3 |
=head2 CREDITS |
1578 |
|
|
|
1579 |
|
|
# Some modifications |
1580 |
|
|
# Copyright © 2001 Micro Sharp Technologies, Inc., Vancouver, WA, USA |
1581 |
|
|
# Author: Karl M. Hegbloom <karlheg@microsharp.com> |
1582 |
|
|
# Perl Artistic Licence. |
1583 |
|
|
|
1584 |
|
|
Many thanks to Charles Owens and David Wheeler for their feedback, |
1585 |
|
|
ideas, patches and bug testing. |
1586 |
|
|
|
1587 |
cvsjoko |
1.1 |
=cut |
1588 |
|
|
|
1589 |
|
|
69; |
1590 |
joko |
1.3 |
|
1591 |
|
|
__END__ |
1592 |
|
|
|
1593 |
|
|
# From old SYNOPSIS, I decided it was too long. A lot of |
1594 |
|
|
# the information here needs to be re-integrated into the |
1595 |
|
|
# POD. |
1596 |
|
|
|
1597 |
|
|
package Project; |
1598 |
|
|
|
1599 |
|
|
# here's where we build the individual object schemas into |
1600 |
|
|
# a Tangram::Schema object, which the Tangram::Storage |
1601 |
|
|
# class uses to know which tables and columns to find |
1602 |
|
|
# objects. |
1603 |
|
|
use Tangram::Schema; |
1604 |
|
|
|
1605 |
|
|
# TIMTOWTDI - this is the condensed manpage version :) |
1606 |
|
|
my $dbschema = Tangram::Schema->new |
1607 |
|
|
({ classes => |
1608 |
|
|
[ 'Orange' => { fields => $Orange::fields }, |
1609 |
|
|
'MyObject' => { fields => $MyObject::schema }, ]}); |
1610 |
|
|
|
1611 |
|
|
sub schema { $dbschema }; |
1612 |
|
|
|
1613 |
|
|
package main; |
1614 |
|
|
|
1615 |
|
|
# See Tangram::Relational for instructions on using |
1616 |
|
|
# "deploy" to create the database this connects to. You |
1617 |
|
|
# only have to do this if you want to write the objects to |
1618 |
|
|
# a database. |
1619 |
|
|
use Tangram::Relational; |
1620 |
|
|
my ($dsn, $u, $p); |
1621 |
|
|
my $storage = Tangram::Relational->connect |
1622 |
|
|
(Project->schema, $dsn, $u, $p); |
1623 |
|
|
|
1624 |
|
|
# Create an orange |
1625 |
|
|
my $orange = Orange->new( |
1626 |
|
|
juiciness => 8, |
1627 |
|
|
type => 'Florida', |
1628 |
|
|
tag => '', # required |
1629 |
|
|
); |
1630 |
|
|
|
1631 |
|
|
# Store it |
1632 |
|
|
$storage->insert($orange); |
1633 |
|
|
|
1634 |
|
|
# This is how you get values out of the objects |
1635 |
|
|
my $juiciness = $orange->juiciness; |
1636 |
|
|
|
1637 |
|
|
# a "ref" must be set to a blessed object, any object |
1638 |
|
|
my $grower = bless { name => "Joe" }, "Farmer"; |
1639 |
|
|
$orange->set_grower ($grower); |
1640 |
|
|
|
1641 |
|
|
# these are all illegal - type checking is fairly strict |
1642 |
|
|
my $orange = eval { Orange->new; }; print $@; |
1643 |
|
|
eval { $orange->set_juiciness ("Yum"); }; print $@; |
1644 |
|
|
eval { $orange->set_segments (31); }; print $@; |
1645 |
|
|
eval { $orange->set_grower ("Mr. Nice"); }; print $@; |
1646 |
|
|
|
1647 |
|
|
# Demonstrate some "required" functionality |
1648 |
|
|
eval { $orange->set_type (''); }; print $@; |
1649 |
|
|
eval { $orange->set_type (undef); }; print $@; |
1650 |
|
|
eval { $orange->set_tag (undef); }; print $@; |
1651 |
|
|
|
1652 |
|
|
# this works too, but is slower |
1653 |
|
|
$orange->get( "juiciness" ); |
1654 |
|
|
$orange->set( juiciness => 123, |
1655 |
|
|
segments => 17 ); |
1656 |
|
|
|
1657 |
|
|
# Re-configure init_default - make each new orange have a |
1658 |
|
|
# random juiciness |
1659 |
|
|
$orange->set_init_default( juiciness => sub { int(rand(45)) } ); |