NAME
Class::Meta::Declare - Deprecated in favor of Class::Meta::Express
VERSION
Version 0.04
SYNOPSIS
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"
DESCRIPTION
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.
COMPARISON TO CLASS::META
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:
Shorter code
Compile-time failures for many mistyped attribute values
Less duplication of information (for example, see
add_method
)Helper classes for included
Class::Meta
types are autoloadedSensible defaults for many entries
CLASS METHODS
new
Class::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
.
create
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.
INSTANCE METHODS
cm
my $cm = $declare->cm;
Returns the Class::Meta
object used to build the the class.
installed_types
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.
package
my $package = $declare->package;
Returns the package for which the Class::Meta
code was declared.
CONSTRUCTOR OPTIONS
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.
meta
Note that all keys for meta
are optional.
key
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".
package
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', ]
accessors
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.use
By default, we assume that
Class::Meta
is the build class. If you have subclassedClass::Meta
(or done something really bizarre like creating an alternative with an identical interface), you may specify that class with theuse
key:meta => [ use => "Class::Meta::Subclass", ]
Note that
Class::Meta::Declare
is an alternate interface, not a subclass ofClass::Meta
.
meta
defaults
accessors
$ACC_PERL
key
Defaults to value of
package
key.package
Calling package.
use
Class::Meta
constructors
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
.create
If
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.
attributes
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:
name
Set to the value of the "key" for the attribute:
rank => { # name will be set to 'rank' default => 'private', }
type
$TYPE_SCALAR
.required
False.
once
False.
label
None.
desc
None.
view
$VIEW_PUBLIC
.authz
$AUTHZ_RDWR
.create
Value corresponding to value in
authz
slot.context
$CTXT_OBJECT
.default
None (Ironic, eh?)
override
False.
Custom Accessors
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)
.
Custom Types
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',
}
]
);
methods
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:
name
Set to the value of the "key" for the method:
rank => { # name will be set to 'rank' code => \&some_method, }
label
None.
desc
None.
view
$VIEW_PUBLIC
.context
$CTXT_OBJECT
.caller
None.
args
None.
returns
None.
EXPORT
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 for details on where to use these.
:acc
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
$ACC_PERL
$ACC_AFFORDANCE
$ACC_SEMI_AFFORDANCE
:authz
Sets the authorization for each attribute, determining whether people can read or write to a given accessor. Defaults to Class::Meta::RDWR
.
See authz.
$AUTHZ_READ
$AUTHZ_WRITE
$AUTHZ_RDWR
$AUTHZ_NONE
:create
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.
$CREATE_GET
$CREATE_SET
$CREATE_GETSET
$CREATE_NONE
:ctxt
For each attribute, you may specify if it is a class or instance attribute.
See the "context" section under add_attribute.
$CTXT_CLASS
$CTXT_OBJECT
:type
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.
$TYPE_SCALAR
$TYPE_SCALARREF
$TYPE_ARRAY
$TYPE_ARRAYREF
$TYPE_HASH
$TYPE_HASHREF
$TYPE_CODE
$TYPE_CODEREF
$TYPE_CLOSURE
$TYPE_STRING
$TYPE_BOOLEAN
$TYPE_BOOL
$TYPE_WHOLE
$TYPE_INTEGER
$TYPE_DECIMAL
$TYPE_REAL
$TYPE_FLOAT
:view
Sets the "visibility" of a constructor, attribute, or method.
See the "view" section under add_constructor.
$VIEW_PUBLIC
$VIEW_PRIVATE
$VIEW_TRUSTED
$VIEW_PROTECTED
AUTHOR
Curtis "Ovid" Poe, <ovid@cpan.org>
BUGS
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.
ACKNOWLEDGEMENTS
Thanks to Kineticode, Inc, http://www.kineticode.com for sponsoring this work.
SEE ALSO
DEPENDENCIES
COPYRIGHT & LICENSE
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.