/[cvs]/nfo/perl/libs/Class/Tangram-1.04.pm
ViewVC logotype

Contents of /nfo/perl/libs/Class/Tangram-1.04.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (show annotations)
Thu Oct 17 02:35:21 2002 UTC (21 years, 6 months ago) by joko
Branch: MAIN
CVS Tags: HEAD
Changes since 1.1: +1041 -1041 lines
+ Class::Tangram 1.04

1 package Class::Tangram;
2
3 # Copyright (c) 2001 Sam Vilain. All rights reserved. This program is
4 # free software; you can redistribute it and/or modify it under the
5 # same terms as Perl itself.
6
7 # Some modifications
8 # $Id: Tangram.pm,v 1.1 2002/07/27 00:20:12 cvsjoko Exp $
9 # Copyright © 2001 Micro Sharp Technologies, Inc., Vancouver, WA, USA
10 # Author: Karl M. Hegbloom <karlheg@microsharp.com>
11 # Perl Artistic Licence.
12
13 =head1 NAME
14
15 Class::Tangram - create constructors, accessor and update methods for
16 objects from a Tangram-compatible object specification.
17
18 =head1 SYNOPSIS
19
20 package Orange;
21
22 use base qw(Class::Tangram);
23 use vars qw($schema);
24 use Tangram::Ref;
25
26 # define the schema (ie, allowed attributes) of this object. See the
27 # Tangram::Schema man page for more information on the syntax here.
28 $schema = {
29 table => "oranges",
30
31 fields => {
32 int => {
33 juiciness => undef,
34 segments => {
35 # here is a new one - this code reference is called
36 # when this attribute is set; it should die() on
37 # error, as it is wrapped in an eval { } block
38 check_func => sub {
39 die "too many segments"
40 if ($ {$_[0]} > 30);
41 },
42 # the default for this attribute.
43 init_default => 7,
44 },
45 },
46 ref => {
47 grower => undef,
48 },
49 },
50 };
51 Class::Tangram::import_schema("Orange");
52
53 package Project;
54 # here's where we build the individual object schemas into a
55 # Tangram::Schema object, which the Tangram::Storage class uses to
56 # know which tables and columns to find objects.
57 use Tangram::Schema;
58
59 my $dbschema = Tangram::Schema->new
60 ({ classes => [ 'Orange' => $Orange::schema ]});
61
62 sub schema { $dbschema };
63
64 package main;
65
66 # See Tangram::Relational for instructions on using "deploy" to
67 # create the database this connects to. You only have to do this if
68 # you want to write the objects to a database.
69 use Tangram::Relational;
70 my ($dsn, $u, $p);
71 my $storage = Tangram::Relational->connect(Project->schema,
72 $dsn, $u, $p);
73
74 # OK
75 my $orange = Orange->new(juiciness => 8);
76 my $juiciness = $orange->juiciness; # returns 8
77
78 # a "ref" must be set to a blessed object
79 my $grower = bless { name => "Joe" }, "Farmer";
80 $orange->set_grower ($grower);
81
82 # these are all illegal
83 eval { $orange->set_juiciness ("Yum"); }; print $@;
84 eval { $orange->set_segments (31); }; print $@;
85 eval { $orange->set_grower ("Mr. Nice"); }; print $@;
86
87 # if you prefer
88 $orange->get( "juiciness" );
89 $orange->set( juiciness => 123 );
90
91 =head1 DESCRIPTION
92
93 Class::Tangram is a base class originally intended for use with
94 Tangram objects, that gives you free constructors, access methods,
95 update methods, and a destructor that should help in breaking circular
96 references for you. Type checking is achieved by parsing the schema
97 for the object, which is contained within the object class in an
98 exported variable C<$schema>.
99
100 After writing this I found that it was useful for merely adding type
101 checking and validation to arbitrary objects. There are several
102 modules on CPAN to do that already, but many don't have finely grained
103 type checking, and none of them integrated with Tangram.
104
105 =cut
106
107 use strict;
108 use Carp qw(croak cluck);
109
110 use vars qw($AUTOLOAD $VERSION);
111 $VERSION = "1.04";
112
113 local $AUTOLOAD;
114
115 # $types{$class}->{$attribute} is the tangram type of each attribute
116 my (%types);
117
118 # $check{$class}->{$attribute}->($value) is a function that will die
119 # if $value is not alright, see check_X functions
120 my (%check);
121
122 # Destructors for each attribute. They are called as
123 # $cleaners{$class}->{$attribute}->($self, $attribute);
124 my (%cleaners);
125
126 # init_default values for each attribute. These could be hash refs,
127 # array refs, code refs, or simple scalars. They will be stored as
128 # $init_defaults{$class}->{$attribute}
129 my (%init_defaults);
130
131 # if a class is abstract, complain if one is constructed.
132 my (%abstract);
133
134 =head1 METHODS
135
136 =over 4
137
138 =item Class-E<gt>new (attribute1 =E<gt> value, attribute2 =E<gt> value)
139
140 sets up a new object of type Class, with attributes set to the values
141 supplied.
142
143 Can also be used as an object method, in which case it returns a
144 B<copy> of the object, without any deep copying.
145
146 =cut
147
148 sub new ($@)
149 {
150 my $invocant = shift;
151 my $class = ref $invocant || $invocant;
152
153 my @values = @_;
154
155 # Setup the object
156 my $self = { };
157 bless $self, $class;
158
159 exists $check{$class} or import_schema($class);
160
161 croak "Attempt to instantiate an abstract type"
162 if ($abstract{$class});
163
164 if ($invocant ne $class)
165 {
166 # The copy constructor; this could be better :)
167 # this has the side effect of much auto-vivification.
168 %$self = %$invocant;
169 $self->set (@values); # override with @values
170 }
171 else
172 {
173 $self->set (@values); # start with @values
174
175 # now fill in fields that have defaults
176 for my $attribute (keys %{$init_defaults{$class}}) {
177
178 next if (exists $self->{$attribute});
179
180 my $default = $init_defaults{$class}->{$attribute}
181 unless tied $init_defaults{$class}->{$attribute};
182
183 if (ref $default eq "CODE") {
184 # sub { }, attribute gets return value
185 $self->{$attribute}
186 = $init_defaults{$class}->{$attribute}->();
187
188 } elsif (ref $default eq "HASH") {
189 # hash ref, copy hash
190 $self->{$attribute}
191 = { %{ $init_defaults{$class}->{$attribute} } };
192
193 } elsif (ref $default eq "ARRAY") {
194 # array ref, copy array
195 $self->{$attribute}
196 = [ @{ $init_defaults{$class}->{$attribute} } ];
197
198 } else {
199 # something else, an object or a scalar
200 $self->{$attribute}
201 = $init_defaults{$class}->{$attribute};
202 }
203 }
204 }
205 return $self;
206 }
207
208 =item $instance->set(attribute => $value, ...)
209
210 Sets the attributes of the given instance to the given values. croaks
211 if there is a problem with the values.
212
213 =cut
214
215 sub set($@) {
216 my ($self, @values) = (@_);
217
218 # yes, this is a lot to do. yes, it's slow. But I'm fairly
219 # certain that this could be handled efficiently if it were to be
220 # moved inside the Perl interpreter or an XS module
221 $self->isa("Class::Tangram") or croak "type mismatch";
222 my $class = ref $self;
223 exists $check{$class} or import_schema($class);
224
225 while (my ($name, $value) = splice @values, 0, 2) {
226 croak "attempt to set an illegal field $name in a $class"
227 if (!defined $check{$class}->{$name});
228
229 #local $@;
230
231 # these handlers die on failure
232 eval { $check{$class}->{$name}->(\$value) };
233 $@ && croak ("value failed type check - ${class}->{$name}, "
234 ."\"$value\" ($@)");
235
236 #should be ok now
237 $self->{$name} = $value;
238 }
239 }
240
241 =item $instance->get($attribute)
242
243 Gets the value of $attribute. If the attribute in question is a set,
244 and this method is called in list context, then it returns the MEMBERS
245 of the set (if called in scalar context, it returns the Set::Object
246 container).
247
248 =cut
249
250 sub get($$) {
251 my ($self, $field) = (@_);
252 $self->isa("Class::Tangram") or croak "type mismatch";
253 my $class = ref $self;
254 exists $check{$class} or import_schema($class);
255 croak "attempt to read an illegal field $field in a $class"
256 if (!defined $check{$class}->{$field});
257
258 if ($types{$class}->{$field} =~ m/^i?set$/o) {
259 if (!defined $self->{$field}) {
260 $self->{$field} = Set::Object->new();
261 }
262 if (wantarray) {
263 return $self->{$field}->members;
264 }
265 }
266
267 return $self->{$field};
268 }
269
270 =item $instance->attribute($value)
271
272 If $value is not given, then
273 this is equivalent to $instance->get("attribute")
274
275 If $value is given, then this is equivalent to
276 $instance->set("attribute", $value). This usage issues a warning; you
277 should change your code to use the set_attribute syntax for better
278 readability.
279
280 =item $instance->get_attribute
281
282 =item $instance->set_attribute($value)
283
284 Equivalent to $instance->get("attribute") and $instance->set(attribute
285 => $value), respectively.
286
287 =item $instance->attribute_includes(@objects)
288
289 =item $instance->attribute_insert(@objects)
290
291 =item $instance->attribute_size
292
293 =item $instance->attribute_clear
294
295 =item $instance->attribute_remove(@objects)
296
297 Equivalent to calling $instance->attribute->includes(@objects), etc.
298 This only works if the attribute in question is a Set::Object.
299
300 =cut
301
302 sub AUTOLOAD ($;$) {
303 my ($self, $value) = (@_);
304 $self->isa("Class::Tangram") or croak "type mismatch";
305
306 my $class = ref $self;
307 $AUTOLOAD =~ s/.*://;
308 if ($AUTOLOAD =~ m/^(set_|get_)?([^:]+)$/
309 and defined $types{$class}->{$2}) {
310
311 # perl sucks at this type of test
312 if ((defined $1 and $1 eq "set_")
313 or (!defined $1 and defined $value)) {
314
315 if ($^W && !defined $1) {
316 cluck("The OO police say change your call to "
317 ."\$obj->set_$2");
318 }
319 return set($self, $2, $value);
320 } else {
321 return get($self, $2);
322 }
323 } elsif (my ($attr, $method) =
324 ($AUTOLOAD =~ m/^(.*)_(includes|insert|
325 size|clear|remove)$/x)
326 and $types{$class}->{$1} =~ m/^i?set$/) {
327 return get($self, $attr)->$method(@_[1..$#_]);
328 } else {
329 croak("unknown method/attribute ${class}->$AUTOLOAD called");
330 }
331 }
332
333 =item $instance->getset($attribute, $value)
334
335 If you're replacing the AUTOLOAD function in your Class::Tangram
336 derived class, but would still like to use the behaviour for one or
337 two fields, then you can define functions for them to fall through to
338 the Class::Tangram method, like so:
339
340 sub attribute { $_[0]->SUPER::getset("attribute", $_[1]) }
341
342 =cut
343
344 sub getset($$;$) {
345 my ($self, $attr, $value) = (@_);
346 $self->isa("Class::Tangram") or croak "type mismatch";
347
348 if (defined $value) {
349 return set($self, $attr, $value);
350 } else {
351 return get($self, $attr);
352 }
353
354 }
355
356 =item check_X (\$value)
357
358 This series of functions checks that $value is of the type X, and
359 within applicable bounds. If there is a problem, then it will croak()
360 the error. These functions are not called from the code, but by the
361 set() method on a particular attribute.
362
363 Available functions are:
364
365 check_string - checks that the supplied value is less
366 than 255 characters long.
367
368 =cut
369
370 sub check_string {
371 croak "string ${$_[0]} too long"
372 if (length ${$_[0]} > 255);
373 }
374
375 =pod
376
377 check_int - checks that the value is a (possibly
378 signed) integer
379
380 =cut
381
382 my $int_re = qr/^-?\d+$/;
383 sub check_int {
384 croak "not an int"
385 if (${$_[0]} !~ m/$int_re/ms);
386 }
387
388 =pod
389
390 check_real - checks that the value is a real number
391 (m/^\d*(\.\d*)?(e\d*)?$/)
392
393 =cut
394
395 my $real_re = qr/^-?\d*(\.\d*)?(e-?\d*)?$/;
396 sub check_real {
397 croak "not a real"
398 if (${$_[0]} !~ m/$real_re/ms);
399 }
400
401 =pod
402
403 check_obj - checks that the supplied variable is a
404 reference to a blessed object
405
406 =cut
407
408 # this pattern matches a regular reference
409 my $obj_re = qr/^(?:HASH|ARRAY|SCALAR)?$/;
410 sub check_obj {
411 croak "not an object reference"
412 if ((ref ${$_[0]}) =~ m/$obj_re/);
413 }
414
415 =pod
416
417 check_flat_array
418 - checks that $value is a ref ARRAY
419
420 =cut
421
422 sub check_flat_array {
423 croak "not a flat array"
424 if (ref ${$_[0]} ne "ARRAY");
425 }
426
427 =pod
428
429 check_array - checks that $value is a ref ARRAY, and that
430 each element in the array is a reference to
431 a blessed object.
432
433 =cut
434
435 sub check_array {
436 croak "array attribute not passed an array ref"
437 if (ref ${$_[0]} ne "ARRAY");
438 for my $a (@{${$_[0]}}) {
439 croak "member in array not an object reference"
440 if ((ref $a) =~ m/$obj_re/);
441 }
442 }
443
444 =pod
445
446 check_set - checks that $value->isa("Set::Object")
447
448 =cut
449
450 sub check_set {
451 croak "set type not passed a Set::Object"
452 unless (ref ${$_[0]} and ${$_[0]}->isa("Set::Object"));
453 }
454
455 =pod
456
457 check_rawdatetime
458 - checks that $value is of the form
459 YYYY-MM-DD HH:MM:SS
460
461 =cut
462
463 # YYYY-MM-DD HH:MM:SS
464 my $rawdatetime_re = qr/^\d{4}-\d{2}-\d{2}\s+\d{1,2}:\d{2}:\d{2}$/;
465 sub check_rawdatetime {
466 croak "invalid SQL rawdatetime"
467 unless (${$_[0]} =~ m/$rawdatetime_re/);
468 }
469
470 =pod
471
472 check_time
473 - checks that $value is of the form
474 HH:MM(:SS)?
475
476 =cut
477
478 my $time_re = qr/^\d{1,2}:\d{2}(?::\d{2})?$/;
479 sub check_time {
480 croak "invalid SQL time"
481 unless (${$_[0]} =~ m/$time_re/);
482 }
483
484 =pod
485
486 check_timestamp
487 - checks that $value is of the form
488 (YYYY-MM-DD )?HH:MM(:SS)?
489
490 =cut
491
492 my $timestamp_re = qr/^(?:\d{4}-\d{2}-\d{2}\s+)?\d{1,2}:\d{2}(?::\d{2})?$/;
493 sub check_timestamp {
494 croak "invalid SQL timestamp"
495 unless (${$_[0]} =~ m/$timestamp_re/);
496 }
497
498 =pod
499
500 check_flat_hash
501 - checks that $value is a ref HASH
502
503 =cut
504
505 sub check_flat_hash {
506 croak "not a hash"
507 unless (ref ${$_[0]} eq "HASH");
508 while (my ($k, $v) = each %${$_[0]}) {
509 croak "hash not flat"
510 if (ref $k or ref $v);
511 }
512 }
513
514 =pod
515
516 check_hash - checks that $value is a ref HASH, that
517 every key in the hash is a scalar, and that
518 every value is a blessed object.
519
520 =cut
521
522 sub check_hash {
523 croak "not a hash"
524 unless (ref ${$_[0]} eq "HASH");
525 while (my ($k, $v) = each %${$_[0]}) {
526 croak "hash key not flat"
527 if (ref $k);
528 croak "hash value not an object"
529 if (ref $v !~ m/$obj_re/);
530 }
531 }
532
533 =pod
534
535 check_nothing - checks whether Australians like sport
536
537 =cut
538
539 sub check_nothing { }
540
541 =item destroy_X ($instance, $attr)
542
543 Similar story with the check_X series of functions, these are called
544 during object destruction on every attribute that has a reference that
545 might need breaking. Note: B<these functions all assume that
546 attributes belonging to an object that is being destroyed may be
547 destroyed also>. In other words, do not allow distinct objects to
548 share Set::Object containers or hash references in their attributes,
549 otherwise when one gets destroyed the others will lose their data.
550
551 Available functions are:
552
553 destroy_array - empties an array
554
555 =cut
556
557 sub destroy_array {
558 my ($self, $attr) = (@_);
559 my $t = tied $self->{$attr};
560 @{$self->{$attr}} = () unless ($t =~ m,Tangram::CollOnDemand,);
561 delete $self->{$attr};
562 }
563
564 =pod
565
566 destroy_set - calls Set::Object::clear to clear the set
567
568 =cut
569
570 sub destroy_set {
571 my ($self, $attr) = (@_);
572
573 # warnings suck sometimes
574 local $^W = 0;
575
576 my $t = tied $self->{$attr};
577 return if ($t =~ m,Tangram::CollOnDemand,);
578 if (ref $self->{$attr} eq "Set::Object") {
579 $self->{$attr}->clear;
580 }
581 delete $self->{$attr};
582 }
583
584 =pod
585
586 destroy_hash - empties a hash
587
588 =cut
589
590 sub destroy_hash {
591 my ($self, $attr) = (@_);
592 my $t = tied $self->{$attr};
593 %{$self->{$attr}} = () unless ($t =~ m,Tangram::CollOnDemand,);
594 delete $self->{$attr};
595 }
596
597 =pod
598
599 destroy_ref - destroys a reference. Contains a hack for
600 Tangram so that if this ref is not loaded,
601 it will not be autoloaded when it is
602 attempted to be accessed.
603
604 =cut
605
606 sub destroy_ref {
607 my ($self, $attr) = (@_);
608
609 # warnings suck sometimes
610 local $^W = 0;
611
612 # the only reason I bother with all of this is that I experienced
613 # Perl did not always call an object's destructor if you just used
614 # delete.
615 my $t = tied $self->{$attr};
616 if (defined $t and $t =~ m/OnDemand/) {
617 delete $self->{$attr};
618 } else {
619 my $ref = delete $self->{$attr};
620 }
621 }
622
623 =item parse_X ($attribute, { schema option })
624
625 Parses the schema option field, and returns one or two closures that
626 act as a check_X and a destroy_X function for the attribute.
627
628 This is currently a very ugly hack, parsing the SQL type definition of
629 an object. But it was bloody handy in my case for hacking this in
630 quickly. This is unmanagably unportable across databases. This
631 should be replaced by primitives that go the other way, building the
632 SQL type definition from a more abstract definition of the type.
633
634 Available functions:
635
636 parse_string - parses SQL types of:
637
638 =cut
639
640 sub parse_string {
641
642 my ($attribute, $option) = (@_);
643
644 # simple case; return the check_string function. We don't
645 # need a destructor for a string so don't return one.
646 if (!$option->{sql}) {
647 return \&check_string;
648 }
649
650 =pod
651
652 CHAR(N), VARCHAR(N)
653 closure checks length of string is less
654 than N characters
655
656 =cut
657
658 if ($option->{sql} =~ m/^\s*(?:var)?char\s*\(\s*(\d+)\s*\)/ix) {
659 my $max_length = $1;
660 return sub {
661 die "string too long for $attribute"
662 if (length ${$_[0]} > $max_length);
663 };
664
665 =pod
666
667 TINYBLOB, BLOB, LONGBLOB
668 TINYTEXT, TEXT, LONGTEXT
669 checks max. length of string to be 255,
670 65535 or 16777215 chars respectively
671
672 =cut
673
674 } elsif ($option->{sql} =~ m/^\s*(tiny|long|medium)?
675 (blob|text)/ix) {
676 my $max_length = ($1 ? ($1 eq "tiny"?255:2**24 - 1)
677 : 2**16 - 1);
678 return sub {
679 die "string too long for $attribute"
680 if (length ${$_[0]} > $max_length);
681 };
682
683 =pod
684
685 SET("members", "of", "set")
686 checks that the value passed is valid as
687 a SQL set type, and that all of the
688 passed values are allowed to be a member
689 of that set
690
691 =cut
692
693 } elsif ($option->{sql} =~
694 m/^\s*set\s*\(
695 (\"[^\"]+\" (?:\s*,\s*\"[^\"]+\")* \s* )
696 \)\s*$/xi) {
697 my $members = $1;
698 my ($member, %members);
699 while (($member, $members) =
700 ($members =~ m/^[,\s]*\"([^\"]+)\"(.*)$/)) {
701 $members{lc($member)} = 1;
702 }
703 return sub {
704 for my $x (split /,/, ${$_[0]}) {
705 croak ("SQL set badly formed or invalid member $x "
706 ." (SET" . join(",", keys %members). ")")
707 if (not exists $members{lc($x)});
708 }
709 };
710
711 =pod
712
713 ENUM("possible", "values")
714 checks that the value passed is one of
715 the allowed values.
716
717 =cut
718
719 } elsif ($option->{sql} =~
720 m/^\s*enum\s*\(
721 (\"[^\"]+\" (?:\s*,\s*\"[^\"]+\")* \s* )
722 \)\s*/xi) {
723 my $values = $1;
724 my ($value, %values);
725 while (($value, $values) =
726 ($values =~ m/^[,\s]*\"([^\"]+)\"(.*)$/)) {
727 $values{lc($value)} = 1;
728 }
729 return sub {
730 croak ("invalid enum value ${$_[0]} must be ("
731 . join(",", keys %values). ")")
732 if (not exists $values{lc(${$_[0]})});
733 }
734
735 } else {
736 die ("Please build support for your string SQL type in "
737 ."Class::Tangram (".$option->{sql}.")");
738 }
739 }
740
741 # Here is where I map Tangram::Type types to functions.
742 # Format:
743 # type => {
744 # check => \&check_X
745 # parse => \&parse_type
746 # destroy => \&destroy_X
747 # }
748 #
749 my %defaults =
750 (
751 int => { check => \&check_int },
752 real => { check => \&check_real },
753 string => { parse => \&parse_string },
754 ref => { check => \&check_obj, destroy => \&destroy_ref },
755 array => { check => \&check_array, destroy => \&destroy_array },
756 iarray => { check => \&check_array, destroy => \&destroy_array },
757 flat_array => { check => \&check_flat_array },
758 set => { check => \&check_set, destroy => \&destroy_set },
759 iset => { check => \&check_set, destroy => \&destroy_set },
760 rawdatetime => { check => \&check_rawdatetime },
761 time => { check => \&check_time },
762 timestamp => { check => \&check_timestamp },
763 flat_hash => { check => \&check_flat_hash },
764 hash => { check => \&check_hash, destroy => \&destroy_hash },
765 perl_dump => { check => \&check_nothing }
766 );
767
768 =item import_schema($class)
769
770 Parses a tangram object schema, in "\$${class}::schema" to the
771 internal representation used to check types values by set(). Called
772 automatically on the first get(), set(), or new() for an object of a
773 given class.
774
775 This function updates Tangram schema option hashes, with the following
776 keys:
777
778 check_func - supply/override the check_X function for
779 this attribute.
780
781 destroy_func - supply/override the destroy_X function for
782 this attribute
783
784 See the SYNOPSIS section for an example of supplying a check_func in
785 an object schema.
786
787 =cut
788
789 sub import_schema($) {
790 my ($class) = (@_);
791
792 eval {
793 my ($fields, $bases, $abstract);
794 {
795 no strict 'refs';
796 $fields = ${"${class}::schema"}->{fields};
797 $bases = ${"${class}::schema"}->{bases};
798 $abstract = ${"${class}::schema"}->{abstract};
799 }
800
801 my $check_class = { };
802 my $cleaners_class = { };
803 my $init_defaults_class = { };
804 my $types_class = { };
805
806 # if this is an abstract type, do not allow it to be
807 # instantiated
808 if ($abstract) {
809 $abstract{$class} = 1;
810 }
811
812 # If there are any base classes, import them first so that the
813 # check, cleaners and init_defaults can be inherited
814 if (defined $bases) {
815 (ref $bases eq "ARRAY")
816 or die "bases not an array ref for $class";
817
818 # Note that the order of your bases is significant, that
819 # is if you are using multiple iheritance then the later
820 # classes override the earlier ones.
821 #for my $super ( Class::ISA::super_path($class) ) {
822 for my $super ( @$bases ) {
823 import_schema $super unless (exists $check{$super});
824
825 # copy each of the per-class configuration hashes to
826 # this class as defaults.
827 my ($k, $v);
828 $types_class->{$k} = $v
829 while (($k, $v) = each %{ $types{$super} } );
830 $check_class->{$k} = $v
831 while (($k, $v) = each %{ $check{$super} } );
832 $cleaners_class->{$k} = $v
833 while (($k, $v) = each %{ $cleaners{$super} } );
834 $init_defaults_class->{$k} = $v
835 while (($k, $v) = each %{ $init_defaults{$super} } );
836 }
837 }
838
839 # iterate over each of the *types* of fields (string, int, ref, etc.)
840 while (my ($type, $v) = each %$fields) {
841 if (ref $v eq "ARRAY") {
842 $v = { map { $_, undef } @$v };
843 }
844 my $def = $defaults{$type};
845
846 # iterate each of the *attributes* of a particular type
847 while (my ($attribute, $option) = each %$v) {
848 $types_class->{$attribute} = $type;
849
850 # ----- check_X functions ----
851 if (ref $option eq "HASH" and $option->{check_func}) {
852 # user-supplied check_X function
853 $check_class->{$attribute} =
854 $option->{check_func};
855
856 } else {
857 if (not defined $def) {
858 die "No default check function for type $type";
859 }
860
861 # check for a type-specific option hash parser
862 if ($def->{parse}) {
863 my ($check, $destroy) =
864 $def->{parse}->($attribute, $option);
865
866 $check_class->{$attribute} = $check;
867 $cleaners_class->{$attribute} = $destroy
868 if (defined $destroy);
869
870 } else {
871 # use the default for this type
872 $check_class->{$attribute} = $def->{check};
873 }
874 }
875
876 # ----- destroy_X functions
877 if (ref $option eq "HASH" and $option->{destroy_func}) {
878 # user-supplied destroy_X function
879 $cleaners_class->{$attribute} =
880 $option->{destroy_func};
881 } else {
882 if ($def->{destroy}) {
883 # use the default for this type
884 $cleaners_class->{$attribute} =
885 $def->{destroy};
886 }
887 }
888
889 # ----- init_default functions
890 # create empty Set::Object containers as necessary
891 if ($type =~ m/^i?set$/) {
892 $init_defaults_class->{$attribute} =
893 sub { Set::Object->new() };
894 }
895 if (ref $option eq "HASH" and $option->{init_default}) {
896 $init_defaults_class->{$attribute} =
897 $option->{init_default};
898 }
899 }
900 }
901 $types{$class} = $types_class;
902 $check{$class} = $check_class;
903 $cleaners{$class} = $cleaners_class;
904 $init_defaults{$class} = $init_defaults_class;
905 };
906
907 $@ && die "$@ while trying to import schema for $class";
908 }
909
910
911 =item $instance->quickdump
912
913 Quickly show the blessed hash of an object, without descending into
914 it. Primarily useful when you have a large interconnected graph of
915 objects so don't want to use the B<x> command within the debugger.
916 It also doesn't have the side effect of auto-vivifying members.
917
918 This function returns a string, suitable for print()ing. It does not
919 currently escape unprintable characters.
920
921 =cut
922
923 sub quickdump($) {
924 my ($self) = (@_);
925
926 my $r = "REF ". (ref $self). "\n";
927 for my $k (sort keys %$self) {
928 $r .= (" $k => "
929 . (
930 tied $self->{$k}
931 || ( ref $self->{$k}
932 ? $self->{$k}
933 : "'".$self->{$k}."'" )
934 )
935 . "\n");
936 }
937 return $r;
938 }
939
940
941 =item $instance->DESTROY
942
943 This function ensures that all of your attributes have their
944 destructors called. It calls the destroy_X function for attributes
945 that have it defined, if that attribute exists in the instance that we
946 are destroying. It calls the destroy_X functions as destroy_X($self,
947 $k)
948
949 =cut
950
951 sub DESTROY($) {
952 my ($self) = (@_);
953
954 my $class = ref $self;
955
956 # if no cleaners are known for this class, it hasn't been imported
957 # yet. Don't call import_schema, that would be a bad idea in a
958 # destructor.
959 exists $cleaners{$class} or return;
960
961 # for every attribute that is defined, and has a cleaner function,
962 # call the cleaner function.
963 for my $k (keys %$self) {
964 if (defined $cleaners{$class}->{$k} and exists $self->{$k}) {
965 $cleaners{$class}->{$k}->($self, $k);
966 }
967 }
968 $self->{_DESTROYED} = 1;
969 }
970
971 =item $instance->clear_refs
972
973 This clears all references from this object, ie exactly what DESTROY
974 normally does, but calling an object's destructor method directly is
975 bad form. Also, this function has no qualms with loading the class'
976 schema with import_schema() as needed.
977
978 This is useful for breaking circular references, if you know you are
979 no longer going to be using an object then you can call this method,
980 which in many cases will end up cleaning up most of the objects you
981 want to get rid of.
982
983 However, it still won't do anything about Tangram's internal reference
984 to the object, which must still be explicitly unlinked with the
985 Tangram::Storage->unload method.
986
987 =cut
988
989 sub clear_refs($) {
990 my ($self) = (@_);
991
992 my $class = ref $self;
993
994 exists $cleaners{$class} or import_schema($class);
995
996 # break all ref's, sets, arrays
997 for my $k (keys %$self) {
998 if (defined $cleaners{$class}->{$k} and exists $self->{$k}) {
999 $cleaners{$class}->{$k}->($self, $k);
1000 }
1001 }
1002 $self->{_NOREFS} = 1;
1003 }
1004
1005
1006 =back
1007
1008 =head1 SEE ALSO
1009
1010 L<Tangram::Schema>
1011
1012 B<A guided tour of Tangram, by Sound Object Logic.>
1013
1014 http://www.soundobjectlogic.com/tangram/guided_tour/fs.html
1015
1016 =head1 BUGS/TODO
1017
1018 More datetime types. I originally avoided the DMDateTime type because
1019 Date::Manip is self-admittedly the most bloated module on CPAN, and I
1020 didn't want to be seen encouraging it. Then I found out about
1021 autosplit :-}.
1022
1023 More AUTOLOAD methods, in particular for container types such as
1024 array, hash, etc.
1025
1026 This documentation should be easy enough for a fool to understand.
1027
1028 There should be more functions for breaking loops; in particular, a
1029 standard function called drop_refs($obj), which replaces references to
1030 $obj with the appropriate Tangram::RefOnDemand so that an object can
1031 be unloaded via Tangram::Storage->unload() and actually have a hope of
1032 being reclaimed. Another function that would be handy would be a
1033 deep "mark" operation for mark & sweep garbage collection.
1034
1035 =head1 AUTHOR
1036
1037 Sam Vilain, <sam@vilain.net>
1038
1039 =cut
1040
1041 69;

MailToCvsAdmin">MailToCvsAdmin
ViewVC Help
Powered by ViewVC 1.1.26 RSS 2.0 feed