Class::MethodMaker - Create generic methods for OO Perl
use Class::MethodMaker
[ scalar => [qw/ foo bar baz /],
new => [qw/ new /] ,
];
This module solves the problem of having to continually write accessor methods
for your objects that perform standard tasks.
The argument to 'use' is an
arrayref, as pairs whose "keys" are
the names of types of generic methods generated by MethodMaker and whose
"values" tell method maker what methods to make.
To override any generated methods, it is sufficient to ensure that the
overriding method is defined when Class::MethodMaker is called. Note that the
"use" keyword introduces a "BEGIN" block, so you may need
to define (or at least declare) your overriding method in a "BEGIN"
block.
A simple class made with "Class::MethodMaker" looks like this:
package MyClass;
use Class::MethodMaker
[ scalar => [qw/ name /],
new => [qw/ new /],
];
This creates a class, of which new instances may be created using
"new", each with a single scalar component called "name".
Name may be queried and (re)set using the methods "name",
"name_reset" and "name_isset":
package main;
my $m = MyClass->new;
my $n;
$\ = "\n";
print $m->name_isset ? "true" : "false"; # false
$m->name("foo");
$n = $m->name;
print defined $n ? "->$n<-" : "*undef*"; # ->foo<-
print $m->name_isset ? "true" : "false"; # true
$m->name(undef);
$n = $m->name;
print defined $n ? "->$n<-" : "*undef*"; # *undef*
print $m->name_isset ? "true" : "false"; # true
$m->name_reset;
$n = $m->name;
print defined $n ? "->$n<-" : "*undef*"; # *undef*
print $m->name_isset ? "true" : "false"; # false
The available component types are scalar, array, hash. Certain non-data-type
utilities are also provided: new, for constructors, deep_copy and copy for
object copies, and abstract for creating abstract methods.
Each of the components take common options. These include -static, for per-class
rather than per-instance data, -type, to restrict the data stored to certain
types (e.g., objects of a certain class), -forward to forward (proxy) given
methods onto components, -default/-default_ctor to set default values for
components, -tie_class to tie the storage of a data type to a given class,
-read_cb/-store_cb to call user-defined functions on read/store (without the
overhead/complexity of ties; and allowing callbacks on existing tie classes).
"Class::MethodMaker" installs
components into a class, by means
of methods that interrogate and amend those components. A component, sometimes
referred in other documentation as a
slot is a group of one or more
attributes (variables) that are associated with an instance of a class
(sometimes called an object), or occasionally a class itself (often referred
to as a
static component). A component is intended as a cohesive unit
of data that should only normally be interrogated or set through the methods
provided.
Given an instance of a class where each instance represents a car, examples of
components are the "make" and "model" (each of which would
be a simple scalar, a string), the engine (a simple scalar, an instance of
Engine::Combustion), and the wheels (an array of instances of Wheel). Note
that the wheels form one component, an array. Of course, the implementor might
instead choose to use four components, each being a scalar wheel.
To have the components created, the principle use of Class::MethodMaker is to
specify the type (data-structure) and name of each component to the import
method of Class::MethodMaker
package MyClass;
use Class::MethodMaker
[ scalar => 'name',
new => [qw/ new /],
];
In this example, the import is called implicitly via the "use"
statement. The components are installed in the package in effect where the
import is called. The argument to import is arranged as pairs, where the first
of each pair is the type of the data-structure, the second is the arguments
for that data-structure; in the most simple case, the name of a component to
install using that data-structure. The second of the pair should be an
arrayref if not a simple name.
Data-structures may be repeated in the call:
use Class::MethodMaker
[ scalar => 'name1',
new => [qw/ new /],
scalar => 'name2',
];
It is an error to attempt to install a two or more components with the same name
twice.
Options may be given to data structures to amend the nature and behaviour of the
components created. Some options are common across all data structure (e.g.,
"static") whilst some are specific to their respective data
structures. Option syntax is laid out in detail below. In simple, options are
provided by way of hashrefs from option name to option value. Options and
component names are order-sensitive; options appearing after a component do
not affect that component. Options only apply to the data-structure to which
they are specified.
Boolean options (e.g., static) may be abbreviated
to -option to set, !option to unset, without a hashref.
use Class::MethodMaker
[ scalar => [+{ -type => 'File::stat' }, qw/ -static name /],
new => 'new',
];
There are also non-data-structure methods that may be created by
Class::MethodMaker. "new" is an example of one such value; it
instead causes a standard "new" method to be created for the calling
class. The arguments and options syntax remains the same, but many options
clearly do not apply (e.g., "type" for "new").
Basically, "Class::MethodMaker" takes no notice of class hierarchies.
If you choose to install a component x in a class B that is a subclass of
class A that already has a component x, then the methods addressing x in B
will simply override those in class A in the usual fashion.
"Class::MethodMaker" takes no special action for this situation.
This is a feature.
The arguments to Class::MethodMaker are passed in a single arrayref, as pairs,
with the first of each pair being the name of the data-structure, and the
second being the arguments to that structure.
use Class::MethodMaker
[ scalar => 'name',
new => [qw/ new /],
];
The second of the pair may in the most simple case be a single scalar that is
the name of a component to use.
use Class::MethodMaker
[ scalar => 'bob', ];
For anything more complex, the second argument must itself be an arrayreference.
Simple names within this arrayreference are again taken as component names to
use; in the following example, both "foo" and "bar" scalar
components are created:
use Class::MethodMaker
[ scalar => [qw/ foo bar /], ];
Options to the data-structure, to change the behaviour of the component, or
methods available, etc., are specified by the presence of a hash reference in
line with the component names. Each key of the hashref is the name of an
option; the corresponding value is the option value. Option names are easily
recognized by a leading hyphen ("-") (or leading exclamation mark,
"!"). The options affect only the components named
after the
option itself. In the following example, "foo" is non-static (the
default), whilst bar is a static:
use Class::MethodMaker
[ scalar => ['foo', { -static => 1 }, 'bar'], ];
Naturally, options may be altered by later settings overriding earlier ones. The
example below has exactly the same effect as the one above:
use Class::MethodMaker
[ scalar => [{ -static => 1 }, 'bar', { -static => 0 }, 'foo'], ];
Options that are boolean (on/off) valued, such as "-static", may be
specified external to any hashref as "-optionname" to set them on
and "!optionname" to set them off. The example below has exactly the
same effect as the one above:
use Class::MethodMaker
[ scalar => [ qw/ -static bar !static foo /], ];
Options that take a value, e.g., "-type", must be specified within a
hashref:
use Class::MethodMaker
[ scalar => [ +{ type => 'File::stat' }, 'bob' ], ];
Options affect is limited by the scope of the nearest enclosing arrayref. This
particularly means that for multiple invocations of a data structure type,
options on earlier invocations do not affect later ones. In the following
example, "foo" is non-static (the default), whilst bar is a static:
use Class::MethodMaker
[ scalar => [ qw/ -static bar /],
scalar => [ 'foo' ],
];
This is true even if later invocations do not use an arrayref. The example below
has exactly the same effect as the one above:
use Class::MethodMaker
[ scalar => [ qw/ -static bar /],
scalar => 'foo',
];
Arrayrefs may be employed within a set of arguments for a single data-structure
to likewise limit scope. The example below has exactly the same effect as the
one above:
use Class::MethodMaker
[ scalar => [ [ qw/ -static bar / ], 'foo' ],
];
Methods may be renamed, by providing options that map from one generic name to
another. These are identified by the presence of a '*' in the option name.
The example below installs component "a" as a scalar, but the method
that would normally be installed as "a_get" is instead installed as
"get_a", and likewise "set_a" is installed in place of
"a_set".
use Class::MethodMaker
[ scalar => [ { '*_get' => 'get_*',
'*_set' => 'set_*', },
'a' ],
];
Class::MethodMaker installs a number of methods by default. Some methods,
considered to be useful only to a subset of developers are installed only on
request. Each method is marked in the text to state whether it is installed by
default or only upon request.
To request that a non-default method is installed, one needs to rename it (even
possibly to its normal name). So, to install the *_get method for a scalar
attribute (as *_get), the syntax is:
package MyClass;
use Class::MethodMaker
[ scalar => [{'*_get' => '*_get'}, 'a'] ];
The method may be installed using a non-default name using similar syntax:
package MyClass;
use Class::MethodMaker
[ scalar => [{'*_get' => 'get_*'}, 'a'] ];
The client may choose to not install a default method by renaming it to undef:
use Class::MethodMaker
[ scalar => [{'*' => undef }, 'a'] ];
Note Class::MethodMaker will not install a method in place of an existing
method, so if the intent is to not install a default method because the client
has their own version, an alternative to the above is to define the client
version before calling Class::MethodMaker.
The standard method names are designed with predictability and class
extendibility in mind.
Naming
For any component
x that Class::MethodMaker creates, the method names are
always "x" or "x_*". This enables predictability, for you
do not need to remember which methods are named "x_*" and which *_x,
and also you can name methods that you create by avoiding prefixing them with
"x", and so avoid any clash with Class::MethodMaker-generated
methods (even if Class::MethodMaker is upgraded with shiny new extra methods).
Class::MethodMaker users may rename methods (see "Method Renaming").
For any
data-structure component (scalar, array, hash, etc.)
x
that Class::MethodMaker creates, the method "x"
sets the
value of that component: i.e., overriding any existing value, not amending or
modifying. E.g., for array components, "x" does not push or pull
values but all old values are removed, and new ones placed in their stead:
package MyClass;
use Class::MethodMaker
[ array => 'a',
new => 'new',
];
package main;
my $m = MyClass->new;
$m->a(4,5);
print join(' ', $m->a), "\n"; # 4 5
$m->a(6,7);
print join(' ', $m->a), "\n"; # 6 7
The method returns the
new value of the component:
print join(' ', $m->a(8,9)), "\n"; # 8 9
Note that calling the method with an empty list
does not reset the value
to empty; this is so that normal lookups work on the method (i.e., if
$m->a
emptied the component, then
@a = $m->a
would always give an empty list: not that useful.
Set/Unset
Each data-structure component has the concept of being set/unset as a whole,
independent of individual members being set. Each component starts life unset
(unless a default or default option or tie class has been supplied), and is
becomes set by any assignment. The component is then reset with the *_reset
method. Thus it is possible to distinguish between a component that has been
set to an explicitly empty value, and one that has not been set (or been
reset). This distinction is analogous to the distinction in hashes between a
missing key and a key whose value is undef.
package MyClass;
use Class::MethodMaker
[ new => 'new',
scalar => 'x',
];
package main;
my $m = MyClass->new;
$\ = "\n";
print $m->x_isset ? "true" : "false"; # false; components start this way
my $x = $m->x;
print defined $n ? "->$n<-" : '*undef*'; # *undef*
print $m->x_isset ? "true" : "false"; # false; reading doesn't set
$m->x(undef);
$x = $m->x;
print $m->x_isset ? "true" : "false"; # true;
print defined $n ? "->$n<-" : '*undef*'; # ->foo<-
$m->x("foo");
$x = $m->x;
print $m->x_isset ? "true" : "false"; # true; undef is valid value
print defined $n ? "->$n<-" : '*undef*'; # *undef*
$m->x_reset;
$x = $m->x;
print defined $n ? "->$n<-" : '*undef*'; # *undef*
print $m->x_isset ? "true" : "false"; # false
It is not an error to query the value of an unset component: the value is undef.
Querying (any passive command, or pure function) an unset component does not
cause it to become set; only assigning (any active command, or procedure)
changes the set status of a component.
NOTE THAT lvalues are still experimental (as of perl 5.8.0), and so their
implementation may change r disappear in the future. Note that lvalue use
defeats type-checking. This may be considered a bug, and so may be fixed if
possible at some point in the future.
Other Design Considerations
Further design goals for Class::MethodMaker version 2:
- Consistency of Options
- The options passed to components are now handled in a
single place, to try to be phrased consistently. As many options as
possible are common to all data-structures.
- Flexibility
- It is intended that all common class-construction options
are supported across all data-types, so that e.g., defaults, ties, typing
may be used with your data-structure of choice, and combined.
- Speed
- The methods are intended to be as fast as possible, within
other constraints outlined here.
- "-target_class"
- By default, the target class is determined to be the last
(latest) class in the call stack that is not a Class::MethodMaker::Engine
subtype. This is what is wanted 99% of the time, and typical users need
not worry. However, the target class may be set explicitly in the call to
"use"/"import":
use Class::MethodMaker
[ -target_class => 'X',
scalar => [qw/ a /],
-target_class => 'Y',
scalar => [qw/ b /],
];
Note that the "-target_class" option is order sensitive: it
affects only components requested after it in the call to
"use"/"import". As shown, the same call may handle
specify multiple target classes. Any components requested before the first
"-target_class" are created in the default-determined class, as
outlined above.
Setting the target class in this way does not persist over multiple
calls to "use"/"import". A subsequent call to either
will use the default-determined class as target (unless again overridden
by "-target_class").
The following options are observed by all data structure components (scalar,
array, hash).
- -static
-
package MyClass;
use Class::MethodMaker
[ scalar => [qw/ -static s /], ];
This option causes components to hold class-specific, rather than
instance-specific values. Thus:
package main;
my $m = MyClass->new;
my $n = MyClass->new;
$m->a(4,5);
print join(' ', $m->a), "\n"; # 4 5
print join(' ', $n->a), "\n"; # 4 5
$n->a(6,7);
print join(' ', $n->a), "\n"; # 6 7
print join(' ', $m->a), "\n"; # 6 7
- -type
-
use Class::MethodMaker
[ scalar => [{ -type => 'File::stat' }, 'st' ]];
Takes the name of a class, and checks that all values assigned to the
component are of the appropriate type (uses UNIVERSAL::isa, so subtypes
are permissible).
- -forward
- This option takes as value an arrayref (or a simple
scalar). The values specify a list of methods that when called on an
instance of the target class, are "forwarded on" to the given
component. For example,
package X;
use Class::MethodMaker
[scalar => [{ -type => 'File::stat',
-forward => [qw/ mode size /], },
'st1',
],
])},
any call of "mode" or "size" on an instance of
"X" will simply call the method of the same name on the value
stored in the component "st1", with the same arguments, and
returns the value(s) of this call.
Forwarding only applies to the first named component (since the methodname
is fixed, without the a componentname part). This is because the
components are installed in the order in which they are created, and
Class::MethodMaker never overwrites a pre-existing method. So, in the
following example, "mode" and "size" forward to the
"st1" component, and "read" forwards to the
"st2" component.
package MyClass;
Class::MethodMaker->import([scalar =>
[{ -type => 'File::stat',
-forward => [qw/ mode
size /],
},
qw( st1 ),
{ -type => 'IO::Handle',
-forward => 'read', },
qw( st2 ),
]])},
Forwarding a method to a component of composite data type (e.g., array,
hash) causes the method to be mapped over the values of that component.
The returned value is appropriate to the component type; so a method
forwarded to an array will return a list, like the array that is the
component, but with each value being the instead result of applying the
forwarded method to the corresponding value of the array.
The following code populates the @sizes array with the sizes of
/etc/passwd, /etc/group, in that order.
package main;
my $m = MyClass->new;
$m->st1("/etc/passwd", "/etc/group");
my @sizes = $m->size;
Calling the forwarding method in a scalar context will get the same results,
but as an arrayref:
my $sizes = $m->size; # [ 921, 598 ] for example
Likewise, forwarding to a hash component will return a hash from original
key to result of method on the corresponding component, or an equivalent
hashref in scalar context.
- -default
-
use Class::MethodMaker
[ scalar => [{ -default => 7 }, 'df1' ]];
Takes a simple value; must be either undef or an instance of the appropriate
type if "-type" has also been specified. Whenever a component is
new or reset, its value(s) default to the value given. Hence *_isset will
always return true for that component. For compound data-structures, the
default applies to the each element of the structure, not the compound
itself. So, for array structures, the default applies to each element of
the array, not the array itself.
It is an error to specify the "-default" option and the
"-default_ctor" option simultaneously.
- -default_ctor
-
use Class::MethodMaker
[scalar => [{ -default_ctor => sub {
Y->new(-3);
},
'df2',
{ -type => 'Y',
-default_ctor => 'new' },
'df3',
]
];
Takes a coderef to call to generate the default value. This is called the
first time a value is required, and afterwards whenever reset is called.
The subr is called with one argument, which is the object upon which the
component exists (or the name of the class upon which the component is
created, if the call is made on the class).
If the "-type" option is in effect, then the value may be a simple
value, which shall be considered the name of a method to call on the class
specified by "-type".
It is an error to specify the "-default" option and the
"-default_ctor" option simultaneously.
- -tie_class
-
# @z is an audit trail
my @z;
package W;
use Tie::Scalar;
use base qw( Tie::StdScalar );
sub TIESCALAR { push @z, [ 'TIESCALAR' ]; $_[0]->SUPER::TIESCALAR }
sub FETCH { push @z, [ 'FETCH' ]; $_[0]->SUPER::FETCH }
sub STORE { push @z, [ STORE => $_[1] ]; $_[0]->SUPER::STORE($_[1]) }
sub DESTROY { push @z, [ 'DESTROY' ]; $_[0]->SUPER::DESTROY }
sub UNTIE { push @z, [ UNTIE => $_[1] ]; $_[0]->SUPER::UNTIE($_[1]) }
package X;
Class::MethodMaker->import([scalar =>
[{ -type => 'File::stat',
-tie_class => 'W',
-forward => [qw/ mode
size /],
},
qw( tie1 ),
new => 'new',
]]);
This option takes a simple value as argument, which is taken be the name of
a class that is to be tied to the storage for the component, e.g., for an
array component, a class that implements the API for tied arrays is needed
(see Tie::Array for more information on this). Likewise for scalar
components, hash components, etc. Note that it is the component that is
tied, not the data items.
package main;
my $x = X->new;
# @z is empty
my $stat1 = stat "/etc/passwd";
my $stat2 = stat "/etc/group";
$x->tie1($stat1);
# @z is (['TIESCALAR'], ['STORE', $stat1])
my $y = $x->tie1;
# $y is $stat1
# @z is (['TIESCALAR'], ['STORE', $stat1], ['FETCH'])
$x->tie1($stat2);
# @z is (['TIESCALAR'], ['STORE', $stat1], ['FETCH'], ['STORE', $stat2])
$x->tie1_reset;
# @z is (['TIESCALAR'], ['STORE', $stat1], ['FETCH'], ['STORE', $stat2],\
# ['DESTROY'])
- -tie_args
-
package X;
Class::MethodMaker->import
([scalar => [{ -tie_class => 'V',
-tie_args => [enum => [qw/A B C/],
default => 'B'],
},
qw( tie2 ),
]]);
This option takes an array reference, whose members are passed as arguments
to any tie invoked on the component (by virtue "-tie_class"). If
"-tie_class" is not in force, this is ignored.
As a convenience measure, a single argument may be passed directly, rather
than embedding in an array ref --- unless that arg is an array ref
itself...
- -read_cb
-
The implementation of this option is incomplete
package MyClass;
use Class::MethodMaker
[ scalar => [{ -read_cb => sub { ($_[1]||0) + 1 } }, 'rcb1' ]
new => 'new';
];
This option takes as argument a coderef, which is called whenever a value is
read. It is called with two arguments: the instance upon which the method
was called, and the value stored in the component. The return value of the
given coderef is the value which is passed to the caller of the method as
the component value. Thus, the above example adds one to whatever the
stored value is. Note that the value is returned to the callee, but not
stored in the object
package main;
my $m = MyClass->new;
$m->rcb1(4);
my $n = $x->rcb1; # 5
my $n = $x->rcb1; # 5
- -store_cb
-
The implementation of this option is incomplete
package MyClass;
use Class::MethodMaker
[ scalar => [{ -store_cb => sub { $_[1] + 1 } }, 'scb1' ]
new => 'new';
];
This option takes as argument a coderef, which is called whenever a value is
stored. It is called with four arguments: the instance upon which the
method was called, the value to store in the component, the name of the
component, and the previous value of the component (if any; if the given
element of the component was previously unset, only three arguments are
passed).
The return value of the given coderef is the value which is actually stored
in the component. Thus, the above example stores 1 greater than the value
passed in.
package main;
my $m = MyClass->new;
$m->scb1(4);
my $n = $x->scb1; # 5
Generally, store callbacks are cheaper than read callbacks, because values
are read more often than they are stored. But that is a generalization.
YMMV.
Some new facilities may be marked as EXPERIMENTAL in the documentation. These
facilities are being trialled, and whilst it is hoped that they will become
mainstream code, no promises are made. They may change or disappear at any
time. Caveat Emptor. The maintainer would be delighted to hear any feedback
particularly regarding such facilities, be it good or bad, so long as it is
constructive.
Some old facilities may be marked as COMPATIBILITY in the documentation. These
facilities are being maintained purely for compatibility with old versions of
this module, but will ultimately disappear. They are normally replaced by
alternatives that are considered preferable. Please avoid using them, and
consider amending any existing code that does use them not to. If you believe
that their removal will cast an unacceptable pall over your life, please
contact the maintainer.
Class::MethodMaker::Engine, Class::MethodMaker::scalar,
Class::MethodMaker::array, Class::MethodMaker::hash,
Class::MethodMaker::V1Compat