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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

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

Legend:
Removed from v.1.1  
changed lines
  Added in v.1.2

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