1 |
package Class::Tangram; |
2 |
|
3 |
# Copyright (c) 2001, 2002 Sam Vilain. All right reserved. This file |
4 |
# is licensed under the terms of the Perl Artistic license. |
5 |
|
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 |
package MyObject; |
14 |
|
15 |
use base qw(Class::Tangram); |
16 |
|
17 |
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 |
|
28 |
$object->set_foo("Something"); # dies - not an integer |
29 |
|
30 |
=head1 DESCRIPTION |
31 |
|
32 |
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 |
|
69 |
For example, |
70 |
|
71 |
package MyObject; |
72 |
use base qw(Class::Tangram); |
73 |
|
74 |
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 |
|
93 |
# '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 |
|
100 |
# false: 'tag' must be defined but may be empty |
101 |
tag => { required => '' }, |
102 |
}, |
103 |
|
104 |
# fields allowed by Class::Tangram but not ever |
105 |
# stored by Tangram - no type checking by default |
106 |
transient => [ qw(_tangible) ], |
107 |
}; |
108 |
|
109 |
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 |
|
122 |
From Class::Tangram 1.12 onwards, no use of perl's C<AUTOLOAD> |
123 |
functionality is used. |
124 |
|
125 |
=cut |
126 |
|
127 |
use strict; |
128 |
use Carp qw(croak cluck); |
129 |
|
130 |
use vars qw($VERSION %defaults); |
131 |
|
132 |
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 |
|
138 |
# $types{$class}->{$attribute} is the tangram type of each attribute |
139 |
my (%types); |
140 |
|
141 |
# $attribute_options{$class}->{$attribute} is the hash passed to tangram |
142 |
# for the given attribute |
143 |
my (%attribute_options); |
144 |
|
145 |
# $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 |
# $required_attributes{$class}->{$attribute} records which attributes |
159 |
# are required... used only by new() at present. |
160 |
my (%required_attributes); |
161 |
|
162 |
# if a class is abstract, complain if one is constructed. |
163 |
my (%abstract); |
164 |
|
165 |
=head1 METHODS |
166 |
|
167 |
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 |
=over 4 |
174 |
|
175 |
=item Class-E<gt>new (attribute1 =E<gt> value, attribute2 =E<gt> value) |
176 |
|
177 |
Sets up a new object of type C<Class>, with attributes set to the |
178 |
values supplied. |
179 |
|
180 |
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 |
|
184 |
=cut |
185 |
|
186 |
sub new ($@) |
187 |
{ |
188 |
my $invocant = shift; |
189 |
my $class = ref $invocant || $invocant; |
190 |
|
191 |
(my @values, @_) = @_; |
192 |
|
193 |
# Setup the object |
194 |
my $self = { }; |
195 |
bless $self, $class; |
196 |
|
197 |
# auto-load schema as necessary |
198 |
exists $types{$class} or import_schema($class); |
199 |
|
200 |
croak "Attempt to instantiate an abstract type" |
201 |
if ($abstract{$class}); |
202 |
|
203 |
if (ref $invocant) |
204 |
{ |
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 |
} |
214 |
|
215 |
$self->_fill_init_default(); |
216 |
$self->_check_required(); |
217 |
|
218 |
return $self; |
219 |
|
220 |
} |
221 |
|
222 |
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 |
|
245 |
} else { |
246 |
# something else, an object or a scalar |
247 |
$self->$setter($default); |
248 |
} |
249 |
} |
250 |
} |
251 |
|
252 |
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 |
} |
271 |
croak("object missing required attribute(s): " |
272 |
.join(', ',@missing).'.') if @missing; |
273 |
} |
274 |
} |
275 |
} |
276 |
|
277 |
=back |
278 |
|
279 |
=head2 Accessing & Setting Attributes |
280 |
|
281 |
=over |
282 |
|
283 |
=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 |
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 |
=cut |
292 |
|
293 |
sub set { |
294 |
my $self = shift; |
295 |
|
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 |
UNIVERSAL::isa($self, "Class::Tangram") or croak "type mismatch"; |
300 |
my $class = ref $self; |
301 |
exists $check{$class} or import_schema($class); |
302 |
croak "set must be called with an even number of arguments" |
303 |
if (scalar(@_) & 1); |
304 |
|
305 |
while (my ($name, $value) = splice @_, 0, 2) { |
306 |
|
307 |
my $setter = "set_".$name; |
308 |
|
309 |
croak "attempt to set an illegal field $name in a $class" |
310 |
unless $self->can($setter); |
311 |
|
312 |
$self->$setter($value); |
313 |
} |
314 |
} |
315 |
|
316 |
=item $instance->get("attribute") |
317 |
|
318 |
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 |
|
324 |
=cut |
325 |
|
326 |
sub get { |
327 |
my $self = shift; |
328 |
croak "get what?" unless @_; |
329 |
UNIVERSAL::isa($self, "Class::Tangram") or croak "type mismatch"; |
330 |
|
331 |
my $class = ref $self; |
332 |
exists $check{$class} or import_schema($class); |
333 |
|
334 |
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 |
} |
347 |
} |
348 |
|
349 |
return @return; |
350 |
} |
351 |
|
352 |
=item $instance->attribute($value) |
353 |
|
354 |
If C<$value> is not given, then |
355 |
this is equivalent to C<$instance-E<gt>get_attribute> |
356 |
|
357 |
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 |
|
364 |
=item $instance->get_attribute |
365 |
|
366 |
=item $instance->set_attribute($value) |
367 |
|
368 |
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 |
|
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 |
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 |
|
396 |
=cut |
397 |
|
398 |
=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 |
|
456 |
=item init_default |
457 |
|
458 |
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 |
|
465 |
=item destroy_func |
466 |
|
467 |
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 |
|
472 |
=back |
473 |
|
474 |
=head2 Default Type Checking |
475 |
|
476 |
# 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 |
|
515 |
=over |
516 |
|
517 |
=item check_X (\$value) |
518 |
|
519 |
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 |
|
524 |
=item check_string |
525 |
|
526 |
checks that the supplied value is less than 255 characters long. |
527 |
|
528 |
=cut |
529 |
|
530 |
sub check_string { |
531 |
croak "string too long" |
532 |
if (length ${$_[0]} > 255); |
533 |
} |
534 |
|
535 |
=item check_int |
536 |
|
537 |
checks that the value is a (possibly signed) integer |
538 |
|
539 |
=cut |
540 |
|
541 |
my $int_re = qr/^-?\d+$/; |
542 |
sub check_int { |
543 |
croak "not an integer" |
544 |
if (${$_[0]} !~ m/$int_re/o); |
545 |
} |
546 |
|
547 |
=item check_real |
548 |
|
549 |
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 |
|
553 |
=cut |
554 |
|
555 |
my $real_re = qr/^-?\d*(\.\d*)?(e-?\d*)?$/; |
556 |
sub check_real { |
557 |
croak "not a real number" |
558 |
if (${$_[0]} !~ m/$real_re/o); |
559 |
} |
560 |
|
561 |
=item check_obj |
562 |
|
563 |
checks that the supplied variable is a reference to a blessed object |
564 |
|
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 |
if ((ref ${$_[0]}) =~ m/$obj_re/o); |
572 |
} |
573 |
|
574 |
=item check_flat_array |
575 |
|
576 |
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 |
|
580 |
=cut |
581 |
|
582 |
sub check_flat_array { |
583 |
croak "not a flat array" |
584 |
if (ref ${$_[0]} ne "ARRAY"); |
585 |
croak "flat array may not contain references" |
586 |
if (map { (ref $_ ? "1" : ()) } @{$_[0]}); |
587 |
} |
588 |
|
589 |
=item check_array |
590 |
|
591 |
checks that $value is a ref ARRAY, and that each element in the array |
592 |
is a reference to a blessed object. |
593 |
|
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 |
if ((ref $a) =~ m/$obj_re/o); |
602 |
} |
603 |
} |
604 |
|
605 |
=item check_set |
606 |
|
607 |
checks that $value->isa("Set::Object") |
608 |
|
609 |
=cut |
610 |
|
611 |
sub check_set { |
612 |
croak "set type not passed a Set::Object" |
613 |
unless (UNIVERSAL::isa(${$_[0]}, "Set::Object")); |
614 |
} |
615 |
|
616 |
=item check_rawdate |
617 |
|
618 |
checks that $value is of the form YYYY-MM-DD, or YYYYMMDD, or YYMMDD. |
619 |
|
620 |
=cut |
621 |
|
622 |
# YYYY-MM-DD HH:MM:SS |
623 |
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 |
} |
643 |
|
644 |
=item check_rawdatetime |
645 |
|
646 |
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 |
|
650 |
=cut |
651 |
|
652 |
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 |
} |
665 |
|
666 |
=item check_dmdatetime |
667 |
|
668 |
checks that $value is of the form YYYYMMDDHH:MM:SS, or those allowed |
669 |
for rawdatetime. |
670 |
|
671 |
=cut |
672 |
|
673 |
sub check_dmdatetime { |
674 |
croak "invalid SQL rawdatetime dude" |
675 |
unless (${$_[0]} =~ m/^\d{10}:\d\d:\d\d$|$rawdatetime_re/o); |
676 |
} |
677 |
|
678 |
=item check_flat_hash |
679 |
|
680 |
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 |
|
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 |
=item check_hash |
696 |
|
697 |
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 |
|
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 |
=item check_nothing |
714 |
|
715 |
checks whether Australians like sport |
716 |
|
717 |
=cut |
718 |
|
719 |
sub check_nothing { } |
720 |
|
721 |
=back |
722 |
|
723 |
=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 |
Available functions: |
734 |
|
735 |
=over |
736 |
|
737 |
=item destroy_array |
738 |
|
739 |
empties an array |
740 |
|
741 |
=cut |
742 |
|
743 |
sub destroy_array { |
744 |
my $self = shift; |
745 |
my $attr = shift; |
746 |
my $t = tied $self->{$attr}; |
747 |
@{$self->{$attr}} = () |
748 |
unless (defined $t and $t =~ m,Tangram::CollOnDemand,); |
749 |
delete $self->{$attr}; |
750 |
} |
751 |
|
752 |
=item destroy_set |
753 |
|
754 |
Calls Set::Object::clear to clear the set |
755 |
|
756 |
=cut |
757 |
|
758 |
sub destroy_set { |
759 |
my $self = shift; |
760 |
my $attr = shift; |
761 |
|
762 |
my $t = tied $self->{$attr}; |
763 |
return if (defined $t and $t =~ m,Tangram::CollOnDemand,); |
764 |
if (ref $self->{$attr} eq "Set::Object") { |
765 |
$self->{$attr}->clear; |
766 |
} |
767 |
delete $self->{$attr}; |
768 |
} |
769 |
|
770 |
=item destroy_hash |
771 |
|
772 |
empties a hash |
773 |
|
774 |
=cut |
775 |
|
776 |
sub destroy_hash { |
777 |
my $self = shift; |
778 |
my $attr = shift; |
779 |
my $t = tied $self->{$attr}; |
780 |
%{$self->{$attr}} = () |
781 |
unless (defined $t and $t =~ m,Tangram::CollOnDemand,); |
782 |
delete $self->{$attr}; |
783 |
} |
784 |
|
785 |
=item destroy_ref |
786 |
|
787 |
destroys a reference. |
788 |
|
789 |
=cut |
790 |
|
791 |
sub destroy_ref { |
792 |
my $self = shift; |
793 |
delete $self->{shift}; |
794 |
} |
795 |
|
796 |
=back |
797 |
|
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 |
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 |
|
813 |
Available functions: |
814 |
|
815 |
=over |
816 |
|
817 |
=item parse_string |
818 |
|
819 |
parses SQL types of: |
820 |
|
821 |
=over |
822 |
|
823 |
=cut |
824 |
|
825 |
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 |
sub parse_string { |
835 |
|
836 |
my $attribute = shift; |
837 |
my $option = shift; |
838 |
|
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 |
=item CHAR(N), VARCHAR(N) |
846 |
|
847 |
closure checks length of string is less than N characters |
848 |
|
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 |
=item TINYBLOB, BLOB, LONGBLOB |
859 |
|
860 |
checks max. length of string to be 255, 65535 or 16777215 chars |
861 |
respectively. Also works with "TEXT" instead of "BLOB" |
862 |
|
863 |
=cut |
864 |
|
865 |
} elsif ($option->{sql} =~ m/^\s*(?:tiny|long|medium)? |
866 |
(?:blob|text)/ix) { |
867 |
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 |
if (${$_[0]} and length ${$_[0]} > $max_length); |
872 |
}; |
873 |
|
874 |
=item SET("members", "of", "set") |
875 |
|
876 |
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 |
|
879 |
=cut |
880 |
|
881 |
} 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 |
return sub { |
889 |
for my $x (split /\s*,\s*/, ${$_[0]}) { |
890 |
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 |
=item ENUM("possible", "values") |
897 |
|
898 |
checks that the value passed is one of the allowed values. |
899 |
|
900 |
=cut |
901 |
|
902 |
} 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 |
return sub { |
910 |
croak ("invalid enum value ${$_[0]} must be (" |
911 |
. join(",", keys %values). ")") |
912 |
if (not exists $values{lc(${$_[0]})}); |
913 |
} |
914 |
|
915 |
|
916 |
} else { |
917 |
die ("Please build support for your string SQL type in " |
918 |
."Class::Tangram (".$option->{sql}.")"); |
919 |
} |
920 |
} |
921 |
|
922 |
=back |
923 |
|
924 |
=back |
925 |
|
926 |
=back |
927 |
|
928 |
=head2 Quick Object Dumping and Destruction |
929 |
|
930 |
=over |
931 |
|
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 |
my $self = shift; |
946 |
|
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 |
my $self = shift; |
974 |
|
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 |
my $self = shift; |
1012 |
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 |
=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 |
|
1400 |
=back |
1401 |
|
1402 |
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 |
=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 |
=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 |
=head1 BUGS/TODO |
1555 |
|
1556 |
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 |
|
1567 |
Allow C<init_default> values to be set in a default import function? |
1568 |
|
1569 |
ie |
1570 |
|
1571 |
use MyClassTangramObject -defaults => { foo => "bar" }; |
1572 |
|
1573 |
=head1 AUTHOR |
1574 |
|
1575 |
Sam Vilain, <sam@vilain.net> |
1576 |
|
1577 |
=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 |
=cut |
1588 |
|
1589 |
69; |
1590 |
|
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)) } ); |