NAME
Class::Tangram - create constructors, accessor and update methods for objects from a Tangram-compatible object specification.
SYNOPSIS
package MyObject;
use base qw(Class::Tangram);
our $fields => { int => [ qw(foo bar) ],
string => [ qw(baz quux) ] };
package main;
my $object = MyObject->new(foo => 2, baz => "hello");
print $object->baz(); # prints "hello"
$object->set_quux("Something");
$object->set_foo("Something"); # dies - not an integer
DESCRIPTION
Class::Tangram is a tool for defining objects attributes. Simply define your object's fields/attributes using the same syntax introduced in _A Guided Tour of Tangram_, and you get objects that work As You'd Expect(tm).
Class::Tangram has no dependancy upon Tangram, and vice versa. Neither requires anything special of your objects, nor do they insert any special fields into your objects. This is a very important feature with innumerable benefits, and few (if any) other object persistence tools have this feature.
So, fluff aside, let's run through how you use Class::Tangram to make objects.
First, you decide upon the attributes your object is going to have. You might do this using UML, or you might pick an existing database table and declare each column to be an attribute (you can leave out "id"; that one is implicit).
Your object should use Class::Tangram as a base class;
use base qw(Class::Tangram)
or for older versions of perl:
use Class::Tangram;
use vars qw(@ISA);
@ISA = qw(Class::Tangram)
You should then define a $fields
variable in the scope of the package, that is a hash from attribute types (see Tangram::Type) to either an array of attribute names, or another hash from attribute names to options hashes (or undef
). The layout of this structure coincides with the fields
portion of a tangram schema (see Tangram::Schema). Note: the term `schema' is used frequently to refer to the $fields
structure.
For example,
package MyObject;
use base qw(Class::Tangram);
our $fields = {
int => {
juiciness => undef,
segments => {
# this code reference is called when this
# attribute is set, to check the value is
# OK
check_func => sub {
die "too many segments"
if (${(shift)} > 30);
},
# the default for this attribute.
init_default => 7,
},
},
ref => {
grower => undef,
},
# 'required' attributes - insist that these fields are
# set, both with constructor and set()/set_X methods
string => {
# true: 'type' must have non-empty value (for
# strings) or be logically true (for other types)
type => { required => 1 },
# false: 'tag' must be defined but may be empty
tag => { required => '' },
},
# fields allowed by Class::Tangram but not ever
# stored by Tangram - no type checking by default
transient => [ qw(_tangible) ],
};
It is of critical importance to your sanity that you understand how anonymous hashes and anonymous arrays work in Perl. Some additional features are used above that have not yet been introduced, but you should be able to look at the above data structure and see that it satisfies the conditions stated in the paragraph before it. If it is hazy, I recommend reading perlref or perlreftut.
When the schema for the object is first imported (see "Schema import"), Class::Tangram defines accessor functions for each of the attributes defined in the schema. These accessor functions are then available as $object->function
on created objects. By virtue of inheritance, various other methods are available.
From Class::Tangram 1.12 onwards, no use of perl's AUTOLOAD
functionality is used.
METHODS
The following methods are available for all Class::Tangram objects
Constructor
A Constructor is a method that returns a new instance of an object.
- Class->new (attribute1 => value, attribute2 => value)
-
Sets up a new object of type
Class
, with attributes set to the values supplied.Can also be used as an object method (normal use is as a "class method"), in which case it returns a copy of the object, without any deep copying.
Accessing & Setting Attributes
- $instance->set(attribute => $value, ...)
-
Sets the attributes of the given instance to the given values. croaks if there is a problem with the values.
This function simply calls
$instance->set_attribute($value)
for each of theattribute => $value
pairs passed to it. - $instance->get("attribute")
-
Gets the value of
$attribute
. This simply calls$instance->get_attribute
. If multiple attributes are listed, then a list of the attribute values is returned in order. Note that you get back the results of the scalar contextget_attribute
call in this case. - $instance->attribute($value)
-
If
$value
is not given, then this is equivalent to$instance->get_attribute
If
$value
is given, then this is equivalent to$instance->set_attribute($value)
. This usage issues a warning if warnings are on; you should change your code to use the set_attribute syntax for better readability. OO veterans will tell you that for maintainability object method names should always be a verb. - $instance->get_attribute
- $instance->set_attribute($value)
-
The normative way of getting and setting attributes. If you wish to override the behaviour of an object when getting or setting an attribute, override these functions. They will be called when you use
$instance->attribute
,$instance->get()
, constructors, etc. - $instance->attribute_includes(@objects)
- $instance->attribute_insert(@objects)
- $instance->attribute_size
- $instance->attribute_clear
- $instance->attribute_remove(@objects)
-
This suite of functions applies to attributes that are sets (
iset
orset
). It could in theory also apply generally to all collections - ie also arrays (iarray
orarray
), and hashes (hash
,ihash
). This will be implemented subject to user demand.
Note: The above functions can be overridden, but they may not be called with the $self->SUPER::
superclass chaining method. This is because they are not defined within the scope of Class::Tangram, only your package.
ATTRIBUTE TYPE CHECKING
Class::Tangram provides type checking of attributes when attributes are set - either using the default set_attribute
functions, or created via the new
constructor.
The checking has default behaviour for each type of attribute (see "Default Type Checking"), and can be extended arbitrarily via a per-attribute check_func
, described below. Critical attributes can be marked as such with the required
flag.
The specification of this type checking is placed in the class schema, in the per-attribute options hash. This is a Class::Tangram extension to the Tangram schema structure.
- check_func
-
A function that is called with a reference to the new value in
$_[0]
. It should calldie()
if the value is bad. Note that this check_func will never be passed an undefined value; this is covered by the "required" option, below.In the example schema (above), the attribute
segments
has acheck_func
that prevents setting the value to anything greater than 30. Note that it does not prevent you from setting the value to something that is not an integer; if you define acheck_func
, it replaces the default. - required
-
If this option is set to a true value, then the attribute must be set to a true value to pass type checking. For string attributes, this means that the string must be defined and non-empty (so "0" is true). For other attribute types, the normal Perl definition of logical truth is used.
If the required option is defined but logically false, (ie "" or 0), then the attribute must also be defined, but may be set to a logically false value.
If the required option is undefined, then the attribute may be set to an undefined value.
For integration with tangram, the
new()
function has a special hack; if it is being invoked from within Tangram, then the required test is skipped.
Other per-attribute options
Any of the following options may be inserted into the per-attribute options hash:
- init_default
-
This value specifies the default value of the attribute when it is created with
new()
. It is a scalar value, it is copied to the fresh object. If it is a code reference, that code reference is called and its return value inserted into the attribute. If it is an ARRAY or HASH reference, then that array or hash is COPIED into the attribute. - destroy_func
-
If anything special needs to happen to this attribute before the object is destroyed (or when someone calls
$object->clear_refs()
), then define this. It is called as$sub->($object, "attribute")
.
Default Type Checking
# The following list is eval'ed from this documentation
# when Class::Tangram loads, and used as default attribute
# options for the specified types. So, eg, the default
# "init_default" for "set" types is a subroutine that
# returns a new Set::Object container.
# "parse" is special - it is passed the options hash given
# by the user and should return (\&check_func,
# \&destroy_func). This is how the magical string type
# checking is performed - see the entry for parse_string(),
# below.
int => { check_func => \&check_int },
real => { check_func => \&check_real },
string => { parse => \&parse_string },
ref => { check_func => \&check_obj,
destroy_func => \&destroy_ref },
array => { check_func => \&check_array,
destroy_func => \&destroy_array },
iarray => { check_func => \&check_array,
destroy_func => \&destroy_array },
flat_array => { check_func => \&check_flat_array },
set => { check_func => \&check_set,
destroy_func => \&destroy_set,
init_default => sub { Set::Object->new() } },
iset => { check_func => \&check_set,
destroy_func => \&destroy_set,
init_default => sub { Set::Object->new() } },
dmdatetime => { check_func => \&check_dmdatetime },
rawdatetime => { check_func => \&check_rawdatetime },
rawdate => { check_func => \&check_rawdate },
rawtime => { check_func => \&check_rawtime },
flat_hash => { check_func => \&check_flat_hash },
transient => { check_func => \&check_nothing },
hash => { check_func => \&check_hash,
destroy_func => \&destroy_hash,
get_func => \&get_hash },
perl_dump => { check_func => \&check_nothing }
- check_X (\$value)
-
This series of internal functions are built-in
check_func
functions defined for all of the standard Tangram attribute types.- check_string
-
checks that the supplied value is less than 255 characters long.
- check_int
-
checks that the value is a (possibly signed) integer
- check_real
-
checks that the value is a real number, by stringifying it and matching it against (
m/^-?\d*(\.\d*)?(e-?\d*)?$/
). Inefficient? Yes. Patches welcome. - check_obj
-
checks that the supplied variable is a reference to a blessed object
- check_flat_array
-
checks that $value is a ref ARRAY and that all elements are unblessed scalars. Does NOT currently check that all values are of the correct type (int vs real vs string, etc)
- check_array
-
checks that $value is a ref ARRAY, and that each element in the array is a reference to a blessed object.
- check_set
-
checks that $value->isa("Set::Object")
- check_rawdate
-
checks that $value is of the form YYYY-MM-DD, or YYYYMMDD, or YYMMDD.
- check_rawtime
-
checks that $value is of the form HH:MM(:SS)?
- check_rawdatetime
-
checks that $value is of the form YYYY-MM-DD HH:MM(:SS)? (the time and/or the date can be missing), or a string of numbers between 6 and 14 numbers long.
- check_dmdatetime
-
checks that $value is of the form YYYYMMDDHH:MM:SS, or those allowed for rawdatetime.
- check_flat_hash
-
checks that $value is a ref HASH and all values are scalars. Does NOT currently check that all values are of the correct type (int vs real vs string, etc)
- check_hash
-
checks that $value is a ref HASH, that every key in the hash is a scalar, and that every value is a blessed object.
- check_nothing
-
checks whether Australians like sport
- destroy_X ($instance, $attr)
-
Similar story with the check_X series of functions, these are called during object destruction on every attribute that has a reference that might need breaking. Note: these functions all assume that attributes belonging to an object that is being destroyed may be destroyed also. In other words, do not allow distinct objects to share Set::Object containers or hash references in their attributes, otherwise when one gets destroyed the others will lose their data.
Available functions:
- destroy_array
-
empties an array
- destroy_set
-
Calls Set::Object::clear to clear the set
- destroy_hash
-
empties a hash
- destroy_ref
-
destroys a reference.
- parse_X ($attribute, { schema option })
-
Parses the schema option field, and returns one or two closures that act as a check_X and a destroy_X function for the attribute.
This is currently a very ugly hack, parsing the SQL type definition of an object. But it was bloody handy in my case for hacking this in quickly. This is probably unmanagably unportable across databases; but send me bug reports on it anyway, and I'll try and make the parsers work for as many databases as possible.
This perhaps should be replaced by primitives that go the other way, building the SQL type definition from a more abstract definition of the type.
Available functions:
- parse_string
-
parses SQL types of:
- CHAR(N), VARCHAR(N)
-
closure checks length of string is less than N characters
- TINYBLOB, BLOB, LONGBLOB
-
checks max. length of string to be 255, 65535 or 16777215 chars respectively. Also works with "TEXT" instead of "BLOB"
- SET("members", "of", "set")
-
checks that the value passed is valid as a SQL set type, and that all of the passed values are allowed to be a member of that set.
- ENUM("possible", "values")
-
checks that the value passed is one of the allowed values.
Quick Object Dumping and Destruction
- $instance->quickdump
-
Quickly show the blessed hash of an object, without descending into it. Primarily useful when you have a large interconnected graph of objects so don't want to use the x command within the debugger. It also doesn't have the side effect of auto-vivifying members.
This function returns a string, suitable for print()ing. It does not currently escape unprintable characters.
- $instance->DESTROY
-
This function ensures that all of your attributes have their destructors called. It calls the destroy_X function for attributes that have it defined, if that attribute exists in the instance that we are destroying. It calls the destroy_X functions as destroy_X($self, $k)
- $instance->clear_refs
-
This clears all references from this object, ie exactly what DESTROY normally does, but calling an object's destructor method directly is bad form. Also, this function has no qualms with loading the class' schema with import_schema() as needed.
This is useful for breaking circular references, if you know you are no longer going to be using an object then you can call this method, which in many cases will end up cleaning up most of the objects you want to get rid of.
However, it still won't do anything about Tangram's internal reference to the object, which must still be explicitly unlinked with the Tangram::Storage->unload method.
FUNCTIONS
The following functions are not intended to be called as object methods.
Schema Import
our $fields = { int => [ qw(foo bar) ],
string => [ qw(baz quux) ] };
# Version 1.115 and below compatibility:
our $schema = {
fields => { int => [ qw(foo bar) ],
string => [ qw(baz quux) ] }
};
- Class::Tangram::import_schema($class)
-
Parses a tangram object field list, in
${"${class}::fields"}
(or${"${class}::schema"}->{fields}
to the internal type information hashes. It will also define all of the attribute accessor and update methods in the$class
package.Note that calling this function twice for the same class is not tested and may produce arbitrary results. Patches welcome.
Run-time type information
It is possible to access the data structures that Class::Tangram uses internally to verify attributes, create objects and so on.
This should be considered a HIGHLY EXPERIMENTAL interface to INTERNALS of Class::Tangram.
Class::Tangram keeps seven internal hashes:
%types
-
$types{$class}->{$attribute}
is the tangram type of each attribute, ie "ref", "iset", etc. See Tangram::Type. %attribute_options
-
$attribute_options{$class}->{$attribute}
is the options hash for a given attribute. %required_attributes
-
$required_attributes{$class}->{$attribute}
is the 'required' option setting for a given attribute. %check
-
$check{$class}->{$attribute}
is a function that will be passed a reference to the value to be checked and either throw an exception (die) or return true. %cleaners
-
$attribute_options{$class}->{$attribute}
is a reference to a destructor function for that attribute. It is called as an object method on the object being destroyed, and should ensure that any circular references that this object is involved in get cleared. %abstract
-
$abstract->{$class}
is set if the class is abstract %init_defaults
-
$init_defaults{$class}->{$attribute}
represents what an attribute is set to automatically if it is not specified when an object is created. If this is a scalar value, the attribute is set to the value. If it is a function, then that function is called (as a method) and should return the value to be placed into that attribute. If it is a hash ref or an array ref, then that structure is COPIED in to the new object. If you don't want that, you can do something like this:[...] flat_hash => { attribute => { init_default => sub { { key => "value" } }, }, }, [...]
Now, every new object will share the same hash for that attribute.
There are currently four functions that allow you to access parts of this information.
- Class::Tangram::attribute_options($class)
-
Returns a hash ref to a data structure from attribute names to the option hash for that attribute.
- Class::Tangram::attribute_types($class)
-
Returns a hash ref from attribute names to the tangram type for that attribute.
- Class::Tangram::required_attributes($class)
-
Returns a hash ref from attribute names to the 'required' option setting for that attribute. May also be called as a method, as in
$instance->required_attributes
. - Class::Tangram::init_defaults($class)
-
Returns a hash ref from attribute names to the default intial values for that attribute. May also be called as a method, as in
$instance->init_defaults
. - Class::Tangram::known_classes
-
This function returns a list of all the classes that have had their object schema imported by Class::Tangram.
- Class::Tangram::is_abstract($class)
-
This function returns true if the supplied class is abstract.
- Class->set_init_default(attribute => $value);
-
Sets the default value on an attribute for newly created "Class" objects, as if it had been declared with init_default. Can be called as a class or an instance method.
SEE ALSO
A guided tour of Tangram, by Sound Object Logic.
http://www.soundobjectlogic.com/tangram/guided_tour/fs.html
DEPENDENCIES
The following modules are required to be installed to use Class::Tangram:
Set::Object => 1.02
Pod::Constants => 0.11
Test::Simple => 0.18
Date::Manip => 5.21
Test::Simple and Date::Manip are only required to run the test suite.
If you find Class::Tangram passes the test suite with earlier versions of the above modules, please send me an e-mail.
MODULE RELEASE
This is Class::Tangram version 1.12.
BUGS/TODO
There should be more functions for breaking loops; in particular, a standard function called drop_refs($obj)
, which replaces references to $obj with the appropriate Tangram::RefOnDemand
object so that an object can be unloaded via Tangram::Storage-
unload()> and actually have a hope of being reclaimed. Another function that would be handy would be a deep "mark" operation for manual mark & sweep garbage collection.
Need to think about writing some functions using Inline
for speed. One of these days...
Allow init_default
values to be set in a default import function?
ie
use MyClassTangramObject -defaults => { foo => "bar" };
AUTHOR
Sam Vilain, <sam@vilain.net>
CREDITS
# Some modifications
# Copyright �© 2001 Micro Sharp Technologies, Inc., Vancouver, WA, USA
# Author: Karl M. Hegbloom <karlheg@microsharp.com>
# Perl Artistic Licence.
Many thanks to Charles Owens and David Wheeler for their feedback, ideas, patches and bug testing.
1 POD Error
The following errors were encountered while parsing the POD:
- Around line 1580:
Non-ASCII character seen before =encoding in '�©'. Assuming CP1252