NAME
Chj::Class::Array - array based perl objects (deprecated)
SYNOPSIS
package My::BaseClass;
use strict;
use Chj::Class::Array -fields => -public => qw(Name Firstname),
-protected => qw(Age),
-private => qw(Secret);
# Method example
sub age {
my $self = shift;
if (@_) {
my $val = shift;
if ($val >= 18) {
$self->[Age] = $val;
} else {
carp "not old enough";
$self->[Secret] = $val;
}
} else {
$self->[Age]
}
}
----
package My::DerivedClass;
use strict;
use My::BaseClass -extend => -public => qw(Nickname),
-private => qw(Fiancee);
# The best way to generate an object, if you want to
# initialize your objects but let parent classes
# initialize them too:
sub new {
my $class = shift;
my $self = $class->SUPER::new;
# Chj::Class::Array::new will catch the above if
# no one else does
# do initialization stuff of your own here
$self
}
sub DESTROY {
my $self = shift;
# do some cleanup here
$self->SUPER::DESTROY;
# can be called without worries,
# Chj::Class::Array provides an empty default DESTROY method.
}
----
# package main:
use strict;
use My::DerivedClass;
my $v = new My::DerivedClass;
$v->[Name] = "Bla";
$v->age(19);
$v->[Age] = 20; # gives a compile time error since `Age'
# does not exist here
DESCRIPTION
So you don't like objects based on hashes, since all you can do to prevent mistakes while accessing object data is to create accessor methods which are slow and inconvenient (and you don't want to use depreciated pseudohashes either) - what's left? Some say, use constants and array based objects. Of course it's a mess since the constants and the objects aren't coupled, and what about inheritance? Chj::Class::Array tries to help you with that.
Array based classes give the possibility to access data fields of your objects directly without the need of slow (and inconvenient) wrapper methods but still with some protection against typos and overstepping borders of privacy.
USAGE
Usage is somewhat similar to importing from non-object oriented modules. `use Chj::Class::Array', as well as `use ' of any Chj::Class::Array derived class, takes a number of arguments. These declare which parent class you intend to use, and which object fields you want to create. See below for an explanation of all options. Option arguments begin with a minus `-'
Arguments listed before the first option are interpreted as symbol names to be imported into your namespace directly (apart from the field names). This is handy to import constants and `enum's. (Note that unlike the usual Exporter, the one from Chj::Class::Array doesn't look at the our @EXPORT* variables yet. Drop me a note if you would like to have that.)
- -fields => list
-
This option is needed to set up an initial Chj::Class::Array based class (i.e. not extend an existing class). The following arguments are the names of the object fields in this class. (For compatibility reasons with older versions of this module, `-members' is an alias for -fields.)
- -extend => list
-
This is used to inherit from an existing class. The following names are created in addition to the member names inherited from the parent class.
- -public | -protected | -private => list
-
These options may be used anywhere after the -fields and -extend options to define the scope of the subsequent names. They can be used multiple times. Public means, the member will be accessible from anywhere your class is `use'd. Protected means, the member will only be accessible from the class itself as well as from derived classes (but not from other places your class is used). Private means, the member will only be accessible inside the class which has defined it. (For compatibility reasons with older versions there is also a `-shared' option which is the same as protected.)
Note that you could always access all array elements by numeric index, and you could also fully qualify the member name constant in question. The member name is merely not put `before your nose'.
The default is protected.
- -onlyfields list
-
Optional. List the fields you want to use after this option. If not given, all (public) member names are imported. Use this if you have name conflicts (see also the IMPORTANT section). (`-onlymembers' is an alias for -onlyfields.)
- -nowarn
-
If you have a name conflict, and you don't like being warned all the time, you can use this option (instead of explicitely listing all non-conflicting names with -onlyfields).
- -class => 'Some::Baseclass'
-
In order to make it possible to define classes independant from module files (i.e. package Some::Baseclass is not located in a file .../Some/Baseclass.pm) you can inherit from such classes by using the -class option. Instead of `use Some::Baseclass ...' you would type `use Chj::Class::Array -class => "Some::Baseclass", ...'.
- -namehash => 'hashname'
-
By default, there is no way to convert field name strings into the correspondent array index except to use eval ".." to interpret the string as a constant. If you need fast string-to-index lookups, use this option to get a hash with the given name into your namespace that has the field name strings as keys and the array index as value.
Use this only if needed since it takes some time to create the hash.
Note that the hash only has the fields that are accessible to you.
You could use the reverse of the hash to get the field names for an index, i.e. for debugging.
There's also a
class_array_namehash
class method with which you can create the hash (or get the cached copy of it) at a later time:class->class_array_namehash( [ aliasname [, some more args ]] )
This returns a reference to the hash. Depending on whether you are in a class inheriting from 'class' or not, or whether you *are* the 'class' or not, you will get a hash containing protected (and your private) fields or not. If 'aliasname' is given, the hash is imported into your namespace with that name.
To get a list of indices for a list of field names, there is also a method:
class->class_array_indices( list of fieldnames )
This will croak if a field doesn't exist or is not visible to you.
IMPORTANT
1.) Be aware that object member names are defined as constants (like in the `constant' module/pragma) that are independant from the actual object. So there are two sources of mistakes:
Use of member names that are also used as subroutine names, perl builtins, or as member names of another array based class you use in the same package (namespace). When a particular name is already used in your namespace and you call `use' on a Chj::Class::Array based class, Chj::Class::Array will detect this, warn you and either die (if it's about a member name you're about to newly create), or both not import the member name into your namespace and also *remove* the existant entry, so you can't accidentally use the wrong one. You can still access the field by fully qualifying it's constant name, i.e. $v->[My::SomeClass::Name] (note that this way you could also access private fields). Use the -onlyfields or -nowarn options if you don't like the warning messages.
Using the member constants from another class than the one the object belongs to. I.e. if you have two classes, `My::House' and `My::Person', perl and Chj::Class::Array won't warn you if you accidentally type $me->[Pissoire] instead of $me->[Memoire].
2.) The `use Chj::Class::Array' or `use My::ArraybasedClass' lines should always be the *LAST* ones importing something from another module. Only this way name conflicts can be detected by Chj::Class::Array. But there is one important exception to this rule: use of other Chj::Class::Array based modules should be even *later*. This is to resolve circularities: if there are two array bases modules named A and B, and both use each other, they will have to create their field names before they can be imported by the other one. To rephrase: always put your "use" lines in this order: 1. other, not Chj::Class::Array based modules, 2. our own definition, 3. other Chj::Class::Array based modules.
3.) Remember that Chj::Class::Array relies on the module import mechanism and thus on it's `import' method. So either don't define subroutines called `import' in your modules, or call SUPER::import from there after having stripped the arguments meant for your own import functionality, and specify -caller => scalar caller() as additional arguments.
4.) (Of course) remember to never `use enum' or `use constant' to define your field names. (`use enum' is fine however for creating *values* you store in your object's fields.)
5.) Don't forget to `use strict' or perl won't check your field names!
TIPS
To avoid name conflicts, always use member names starting with an uppercase letter (and the remaining part in mixed case, so to distinguish from other constants), and use lowercase names for your methods / subs. Define fields private, if you don't need them to be accessible outside your class.
BUGS
Scalars can't be checked for conflicts/existence. This is due to a strange inconsistency in perl (5.00503 as well as 5.6.1). This will probably not have much impact. (Note that constants are not SCALARs but CODE entries in the symbol table.)
CAVEATS
Chj::Class::Array only supports single inheritance. I think there's no way to implement multiple inheritance with arrays / member name constants. Another reason not to use multiple inheritance with arrays is that users can't both inherit from hash and array based classes, so any class aiming to be compatible to other classes to allow multiple inheritance should use the standard hash based approach.
There is now a -pmixin => class Note that you can still force multiple inheritance by loading further subclasses yourself ('use Classname ()' or 'require Classname') and push()ing the additional classnames onto @ISA. (But for Chj::Class::Array, subclasses of such a class will look as they would only inherit from the one class that Chj::Class::Array has been told of.)
NOTE
There is also another helper module for array classes (on CPAN), Class::ArrayObjects by Robin Berjon. I didn't know about his module at the time I wrote Chj::Class::Array. You may want to have a look at it, too.
FAQ
(Well it's not yet one but I put this in here before it becomes one:)
- Q: Why does perl complain with 'Bareword "Foo" not allowed' when I have defined Foo as -public in class X and I have a 'use X;' in my class Y?
-
A: Could it be there is a line 'use Y;' in your X module and you have placed it before defining X's fields? (See also "IMPORTANT" section.)
AUTHOR
Christian Jaeger <copying@christianjaeger.ch>.
SEE ALSO
constant, enum, Class::Class, Class::ArrayObjects
NOTE
This is alpha software! Read the status section in the package README or on the website.