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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (hide 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 joko 1.2 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