| Class-Meta-Declare documentation | view source | Contained in the Class-Meta-Declare distribution. |
Class::Meta::Declare - Deprecated in favor of Class::Meta::Express
Version 0.04
This was a first attempt at making a saner interface for Class::Meta. It is nicer, but Class::Meta::Express is nicer still. Go use that one.
package MyApp::Thingy;
use Class::Meta::Declare ':all';
use Data::UUID;
Class::Meta::Declare->new(
meta => [
key => 'thingy',
accessors => $ACC_SEMI_AFFORDANCE,
],
attributes => [
pi => {
context => $CTXT_CLASS,
authz => $AUTHZ_READ,
default => 3.1415927,
},
id => {
authz => $AUTHZ_READ,
type => $TYPE_STRING,
default => sub { Data::UUID->new->create_str },
},
name => {
required => 1,
type => $TYPE_STRING,
default => 'No Name Supplied',
},
age => { type => $TYPE_INTEGER, },
],
methods => [
some_method => {
view => $VIEW_PUBLIC,
code => sub {
my $self = shift;
return [ reverse @_ ];
},
}
]
);
my $object = MyApp::Thingy->new;
print MyApp::Thingy->pi; # prints 3.1415927
print $object->name; # prints "No Name Supplied';
$object->set_name("bob");
print $object->name; # prints "bob"
This class provides an alternate interface for Class::Meta.
Class::Meta is a useful module which allows one to create Perl classes
which support introspection (also known as reflection). Typically Perl
classes, when created, don't supply a lot of metadata. Imported helper
functions show up when you call $object->can($method). Private,
protected and trusted methods are not readily supported. Fetching a list of
attributes or methods is a haphazard affair. Class::Meta overcomes these
shortcomings by building the classes for you and allowing you to fetch a class
object:
my $class_object = $object->my_class;
foreach my $attribute ( $class_object->attributes ) {
print $attribute->name, "\n";
}
foreach my $method ( $class_object->methods ) {
print $method->name, "\n";
}
If you've set up your class correctly, these properties are now easy to discover.
Unfortunately, many find the Class::Meta interface to be a bit clumsy. As
an alternative, Class::Meta::Declare allows you to declare your entire
class in a single argument list to the constructor and have the class built
for you automatically. Further, reasonable defaults are provided for just
about everything.
IMPORTANT: You want this class or Class::Meta if you need an
introspection API for your classes. If you do not need introspection or
dynamic class generation, these modules are overkill.
Consider the Class::Meta::Declare example from the SYNOPSIS:
package MyApp::Thingy;
use Class::Meta::Declare ':all';
use Data::UUID;
Class::Meta::Declare->new(
meta => [
key => 'thingy',
accessors => $ACC_SEMI_AFFORDANCE,
],
attributes => [
pi => {
context => $CTXT_CLASS,
authz => $AUTHZ_READ,
default => 3.1415927,
},
id => {
authz => $AUTHZ_READ,
type => $TYPE_INTEGER,
default => sub { Data::UUID->new->create_str },
},
name => {
required => 1,
type => $TYPE_STRING,
default => 'No Name Supplied',
},
age => { type => $TYPE_INTEGER, },
],
methods => [
some_method => {
view => $VIEW_PUBLIC,
code => sub {
my $self = shift;
return [ reverse @_ ];
},
}
]
);
Here's the equivalent Class::Meta code:
package MyApp::Thingy;
use Class::Meta;
use Class::Meta::Types::String 'semi-affordance';
use Class::Meta::Types::Numeric 'semi-affordance';
use Data::UUID;
my $cm = Class::Meta->new( key => 'thingy' );
$cm->add_constructor(
name => 'new',
create => 1,
);
$cm->add_attribute(
name => 'pi',
context => Class::Meta::CLASS,
authz => Class::Meta::READ,
type => 'whole',
default => 3.1415927,
);
$cm->add_attribute(
name => 'id',
authz => Class::Meta::READ,
type => 'integer',
default => sub { Data::UUID->new->create_str },
);
$cm->add_attribute(
name => 'name',
required => 1,
type => 'string',
default => 'No Name Supplied',
);
$cm->add_attribute(
name => 'age',
type => 'integer',
);
sub some_method {
my $self = shift;
return [ reverse @_ ];
}
$cm->add_method(
name => 'some_method',
view => Class::Meta::PUBLIC,
);
$cm->build;
As you can see, the Class::Meta code is longer. The larger and more
complicated the class, the longer it gets. Class::Meta::Declare offers the
following advantages:
add_method)Class::Meta types are autoloadedClass::Meta::Declare->new(%options);
The new method allows you to build an entire class, with reflective
capabilities, just like Class::Meta. However, the syntax is shorter,
hopefully clearer, and it builds everything in one go.
See CONSTRUCTOR OPTIONS for details on %options.
my $declare = Class::Meta::Declare->create(\%options); my $class_meta = $declare->cm; # more Class::Meta stuff $class_meta->build;
This constructor is exactly the same as new except it does not call
Class::Meta's build method. Use this constructor if you have more stuff
you need to do prior to build being called.
my $cm = $declare->cm;
Returns the Class::Meta object used to build the the class.
my @types = $declare->installed_types;
if ($declare->installed_types('Class::Meta::Type::Numeric')) { ... }
Returns a list of data types used. If passed a data type, returns a boolean value indicating whether or not that type was used.
my $package = $declare->package;
Returns the package for which the Class::Meta code was declared.
The constructor takes an even-sized list of name/value declarations. Each name
should be one of meta, constructors, attributes or methods. Each
declaration should be an array reference of with key/value pairs in it (in
other words, it's like a hashref but because it's in an array reference, we
preserve the element order). Each key is optional, but supplying no keys
pretty much means you have an empty class (though you will get a default
constructor).
The following lists the key/value options for each declaration.
Note that all keys for meta are optional.
This specifies the "class key" underwhich you may fetch a new instance of a class object:
my $class_object = Class::Meta->for_key($key);
See Class::Meta's "for_key".
Building a class assumes the class is to be built in the current package. You may override this with a package parameter.
meta => [ key => 'foo', package => 'Foo', ]
This key specifies the getter/setter style which will be built for attributes. Perl-style getter/setters look like this:
my $name = $object->name;
$object->name('Bob');
You may also specify "semi-affordance" style accessors with
$ACC_SEMI_AFFORDANCE:
my $name = $object->name;
$object->set_name('Bob');
You may also specify "affordance" style accessors with
$ACC_AFFORDANCE:
my $name = $object->get_name;
$object->set_name('Bob');
This meta declaration thus might look like this:
meta => [ accessors => $ACC_SEMI_AFFORDANCE ]
Note that the accessors parameter has no value on data types not supplied by
Class::Meta unless they have been written to recognize them.
By default, we assume that Class::Meta is the build class. If you have
subclassed Class::Meta (or done something really bizarre like creating an
alternative with an identical interface), you may specify that class with the
use key:
meta => [ use => "Class::Meta::Subclass", ]
Note that Class::Meta::Declare is an alternate interface, not a subclass of
Class::Meta.
meta defaultsaccessors$ACC_PERL
keyDefaults to value of package key.
packageCalling package.
useClass::Meta
By default, a new constructor is created for you. If you pass a
constructors declaration, the default constructor will not be built and all
constructor creation will be up to you.
Each constructor must have a key which specifies the name of the constructor
and point to a hashref containing additional information about the
constructor. An empty hashref will simply create a constructor with the given
name, so the default constructor which is provided by Class::Meta::Declare
in the absense of a constructors declaration is simply:
constructors => [
new => {}
]
The values of the hashref should match the values identified in the
Class::Meta "add_constructor" documentation. name is not required (and
will be ignored if supplied) as name is taken from the hashref key. view
should be on of the values listed in the EXPORT ":view" section of this
documentation.
The actual body of the constructor, if supplied, should be provided with the
code key.
So to create factory constructor, one might do this (the following example assumes that the two factory classes listed are subclasses of the current class):
package MyClass;
use Class::Meta::Declare;
Class::Meta::Declare->new(
constructors => [
new => {}, # we can have multiple constructors
factory => {
view => $VIEW_PUBLIC, # optional as this is the default
code => sub {
my ($class, $target) = @_;
$class = $target eq 'foo'
? 'Subclass::Foo'
: 'Subclass::Bar';
return bless {}, $class;
}
}
]
And later you'll be able to do this:
my $object = MyClass->new;
print ref $object; # MyClass
$object = MyClass->factory('foo');
print ref $object; # Subclass::Foo
constructors defaults:view$VIEW_PUBLIC.
createIf code is provided, false, otherwise true.
Note that if you supply a create slot, its value will be ignored in favor
of the "default" create value.
Each attribute must have a key which specifies the name of the attribute and point to a hashref containing additional information about the attribute. An empty hashref will create a simple scalar attribute with the given name, so a basic getter/setter with no validation is simply:
attributes => [
some_attribute => {}
]
The values of the hashref should match the values identified in the
Class::Meta "add_attribute" documentation. name is not required (and
will be ignored if supplied) as name is taken from the hashref key. view
should be on of the values listed in the EXPORT ":view" section of this
documentation.
The type should be one of the datatypes specified in the EXPORT ":type"
section. Note that unlike Class::Meta, you do not have to load the type
class. Class::Meta::Declare will infer the type class from the type you
provide and handle this for you.
The authz and create values should be one of their corresponding values
in the EXPORT section of this document.
The context key indicates whether this is a class or instance attribute.
It's value should be either $CTXT_CLASS or $CTXT_OBJECT.
attributes defaults:nameSet to the value of the "key" for the attribute:
rank => { # name will be set to 'rank'
default => 'private',
}
type$TYPE_SCALAR.
requiredFalse.
onceFalse.
labelNone.
descNone.
view$VIEW_PUBLIC.
authz$AUTHZ_RDWR.
createValue corresponding to value in authz slot.
context$CTXT_OBJECT.
defaultNone (Ironic, eh?)
overrideFalse.
If you wish to provide custom attribute accessors, the actual body of the
accessor should be provided with the code key. If this is done, the
create value will automatically be set to $CREATE_NONE. This tells
Class::Meta to not create attribute accessor for you, but to use the code
you have supplied.
There are two ways to create custom attribute code depending on the
accessor style you have chosen. If you are using regular "perl style"
accessors (the default), then code should point to a code reference or an
anonymous sub:
password => { # insecure code for demonstration purposes only
code => sub {
my $self = shift;
return $self->{password} unless @_;
my $password = shift;
if (length $password < 5) {
croak "Password too short";
}
$self->{password} = $password;
return $self;
}
}
However, if you are using $ACC_SEMI_AFFORDANCE or $ACC_AFFORDANCE style
accessors, then you'll have separate get and set methods. code
should then point to a hash reference with get and set as the keys and
their values pointing to their corresponding methods.
meta => [
accessors => $ACC_SEMI_AFFORDANCE,
],
attributes => [
password => { # insecure code for demonstration purposes only
code => {
get => sub { shift->{password} },
set => sub {
my ($self, $password) = @_;
if (length $password < 5) {
croak "Password too short";
}
$self->{password} = $password;
return $self;
}
}
}
]
For the code above, you may then access the attribute via
$object->password and $object->set_password($password).
You may find the built-in list of types insufficient for your needs. For
example, you may wish to create an accessor which only accepts types of class
Customer. In this case, Customer should be a Class::Meta or
Class::Meta::Declare class and should be loaded prior to new being
called. type should then point to the Customer key.
Class::Meta::Declare->new(
meta => [
key => 'customer',
package => 'Customer',
],
@customer_attributes,
@customer_methods
);
And later:
Class::Meta::Declare->new(
meta => [
key => 'some_key',
package => 'Some::Package',
],
attributes => [
cust => {
type => 'customer',
}
]
);
Each method must have a key which specifies the name of the method and point
to a hashref containing additional information about the method. Each hashref
should contain, at minimum, a code key which points to a subref of anonymous
subroutine which defines the method:
methods => [
reverse_name => sub {
my $self = shift;
return scalar reverse $self->name;
}
]
The values of the hashref should match the values identified in the
Class::Meta "add_method" documentation. name is not required (and will
be ignored if supplied) as name is taken from the hashref key. view should
be on of the values listed in the EXPORT ":view" section of this
documentation.
The context key indicates whether this is a class or instance method. It's
value should be either $CTXT_CLASS or $CTXT_OBJECT. The default is
$CTXT_OBJECT.
The actual body of the method, if supplied, should be provided with the
code key. If it's not supplied, it is assumed the the method will still
be available at runtime. This is if the method is declared elsewhere or will
be provided via AUTOLOAD or similar functionality.
methods defaults:nameSet to the value of the "key" for the method:
rank => { # name will be set to 'rank'
code => \&some_method,
}
labelNone.
descNone.
view$VIEW_PUBLIC.
context$CTXT_OBJECT.
callerNone.
argsNone.
returnsNone.
Class::Meta::Declare exports a number of constants on demand. These
constants are used to provide a simpler interface for Class::Meta use.
See CONSTRUCTOR OPTIONS|"CONSTRUCTOR OPTIONS" for details on where to use these.
Foreach each class, you can specify the type of attribute accessors created. Defaults to "perl-style" accessors.
See the "Accessors" section for Class::Meta.
See also:
Class::Meta::AccessorBuilder::Affordance
Class::Meta::AccessorBuilder::SemiAffordance
Sets the authorization for each attribute, determining whether people can read
or write to a given accessor. Defaults to Class::Meta::RDWR.
See authz.
Indicates what type of accessor or accessors are to be created for the
attribute. Generally sets a sensible default based upon the authz setting.
See the "create" section under add_attribute.
For each attribute, you may specify if it is a class or instance attribute.
See the "context" section under add_attribute.
Sets the data type for each attribute. Setting an attribute to an illegal data
type is a fatal error. This list of data types covers all that are supplied
with Class::Meta. If you use others, you'll have to specify them
explicitly.
See Data Types.
Sets the "visibility" of a constructor, attribute, or method.
See the "view" section under add_constructor.
Curtis "Ovid" Poe, <ovid@cpan.org>
Please report any bugs or feature requests to
bug-class-meta-declare@rt.cpan.org, or through the web interface at
http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Class-Meta-Declare.
I will be notified, and then you'll automatically be notified of progress on
your bug as I make changes.
Thanks to Kineticode, Inc, http://www.kineticode.com for sponsoring this work.
Copyright 2005 Curtis "Ovid" Poe, all rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
| Class-Meta-Declare documentation | view source | Contained in the Class-Meta-Declare distribution. |