|
Class::Translucent - A base class for translucency |
Class::Translucent - A base class for translucency
package My::Class;
BEGIN {
use Class::Translucent ({
name => 'sparrow',
item2 => 'else',
attr => { one => 'two', two => 'three', three => 'one' },
events => [ 'buy', 'grow', 'sell', 'eat', 'sleep' ],
});
use base qw{Class::Translucent};
}
sub new {
my $self = shift;
return $self->SUPER::new( @_ );
}
package main;
my $o = new My::Class;
print $o->name; # Prints 'sparrow'
$o->name( 'robin' ); # Set the per-object value
print $o->name; # Prints 'robin'
print My::Class->name; # Prints 'sparrow'
Nothing by default.
the Carp manpage, the Data::Dumper manpage
Actually, Data::Dumper is only required for debugging, so this module will eventually only need Carp.
This is an abstract base class that provides functionality for translucent attributes in its derivatives. A translucent attribute is an attribute which has a class-wide default. A class's attributes are set in a template, from which all class and instance method calls initially get/set their data. However, once an object has stored a value, it loses its translucency, and thereafter returns its own distinct value. For more information about translucency, see Tom Christiansen's excellent OO Tutorial for Class Data in Perl, which can be found at <http://language.perl.com/misc/perltootc.html>.
In order for your class to usefully inherit from Class::Translucent, it needs to tell Class::Translucent about itself via a template. This template should be a hash or hash reference containing keys for all the translucent attributes of your class, along with default values for each one.
There are several methods for defining this template. If you have class data that needs to be accessed before any instances of your class are created, you can pass the template as the argument to the 'use' statement, like so:
use Class::Translucent ({ attribute => 'defaultValue' });
You can also define a package global named the same thing as the last part of
your package (eg., if you class is called HTML::Graphics::Vector, the hash
should be %HTML::Graphics::Vector::Vector). When Class::Translucent's
constructor is called as a superclass constructor from your class (or one of its
parent classes) and it doesn't already have a template registered for your class,
it will look for such a hash, and if it is found, use it as the class's template.
In any case, as soon as the template is defined, Class::Translucent auto-generates translucent accessor methods for the attributes you've specified in the template, skipping any that may already be defined.
The constructor returns an empty hashref blessed into the calling class.
TODO: More docs
Michael Granger <ged@FaerieMUD.org> based on ideas from Tom's OO Tutorial for Class Data in Perl (the perltootc manpage) by Tom Christiansen.
Copyright (c) 1999, 2000, The FaerieMUD Consortium. All rights reserved.
This module is free software. You may use, modify, and/or redistribute this software under the terms of the Perl Artistic License. (See http://language.perl.com/misc/Artistic.html)
Tie::SecureHash.
set() are ambiguous for complex datatypes. For
example, push adds an element to an array -- so if an array attribute is
translucent, should pushAttribute() called as an object method push the given
value onto a new empty array, or should it make a copy of the class data first,
and push the new element onto it?
THe copy-on-write behaviour is the current behaviour, but will need some rigorous testing to make sure it conforms to Perl's do-what-I-mean.
use
Class::Translucent, the import() function must do the method
generation. This requires that the class template be mangled into the argument
to use, which is perhaps ugly and unintuitive to some. I can't see any way
around it, though, as it has to occur in a BEGIN block in order to guarantee
that the generated methods exist before the constructor is called. Otherwise,
setting class-wide data must wait until the first instance is created.
You can also cause the warnings to disappear by prototyping the methods you wish
to override before the use Class::Translucent call, but that's unintuitive.
%Classes hash in the closure so a
class's template can be reloaded. Perhaps some mechanism for clobbering existing
accessors would also be desirable.
Another related problem -- do Perl internals care if a method gets clobbered? Does Perl do caching of method lookups, and, if so, what will happen if the cached method is undefined during the life of the program? Randal Schwartz suggests modifying the @ISA, which will cause cached methods to be discarded, but I haven't yet tested this.
_') are considered 'protected', and the accessor methods
generated for it may only be called from within the defining
class or one of its derivatives. Attributes which are prefixed
with two or more underscores are considered 'private', and may
only be accessed from within the defining class itself. All
other attributes are considered public, and may be accessed
from any package.
The code contained in these templates contain special tokens '%%
ATTRIBUTE %%', and '%% CLASS %%', which will be replaced,
respectively, with the attribute name and the name of the
class for which they are being generated.
The code in these templates can contain the same tokens as the
%AccessCheck global, and they are subject to the same
substitution at method-generation.
Note that the attribute passed to later chunks of the method will always be a reference to the needed attribute, even if the attribute is already a reference. This is to make it easier later on to modify the attribute without knowing exactly where the thing being modified lives.
$attribute, and the rest of the argument list will
be untouched.
The word 'attribute' in the key is replaced in the generated method with the name of the attribute. For example, if you had a key named 'bargleAttribute', an attribute called 'name' would result in a generated method called 'bargleName'. Leading underscores in an attribute name are always translated to the beginning of the method name, so if the attribute above was instead called '__name', then the generated method would be '__bargleName'. Attributes with leading capitalization result in leading capitalization in the generated method name as well. Eg., an attribute called 'Name' would result in a method named 'BargleName', and an attribute called '_Name' would generate a method called '_BargleName'.
The methods in the 'default' key/value pair are given to every datatype, and can be overidden by the more specific datatype key/value pair. This can be used to establish some default behaviour for an accessor, and then override it for specific datatypes.
use statement, or
can be called explicitly. Note that overriding one of the
methods provided by this function may result in a
'subroutine redefined' warning, as they won't yet exist
when import() is called, typically. This is probably
harmless.
import() method,
but still use Class::Translucent's method generation. Idea borrowed from
Exporter's export_to_level().
My::Derived::Class will be called
%My::Derived::Class::Class. This function
|
Class::Translucent - A base class for translucency |