Curtis "Ovid" Poe > Class-Meta-Declare-0.04 > Class::Meta::Declare

Download:
Class-Meta-Declare-0.04.tar.gz

Dependencies

Annotate this POD

CPAN RT

New  1
Open  0
View/Report Bugs
Module Version: 0.04   Source  

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:

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.

meta defaults

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:

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:

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:

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

Class::Meta::AccessorBuilder::Affordance

Class::Meta::AccessorBuilder::SemiAffordance

: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.

: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.

:ctxt

For each attribute, you may specify if it is a class or instance attribute.

See the "context" section under add_attribute.

: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.

:view

Sets the "visibility" of a constructor, attribute, or method.

See the "view" section under add_constructor.

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 ^

Class::Meta

DEPENDENCIES ^

Class::BuildMethods

Class::Meta

Exporter::Tidy

Readonly

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.

syntax highlighting: