Matthew Simon Cavalletto

NAME

Class::MakeMethods::Guide - Getting Started Guide

SYNOPSIS

  package MyObject;
  use Class::MakeMethods::Basic::Hash (
    'new'       => 'new',
    'scalar'    => 'foo',
    'scalar'    => 'bar',
  );

DESCRIPTION

Object-oriented Perl code is widespread -- you've probably seen code like the below a million times:

  my $obj = MyStruct->new( foo=>"Foozle", bar=>"Bozzle" );
  if ( $obj->foo() =~ /foo/i ) {
    $obj->bar("Barbados!");
  }
  print $obj->summary();

(If this doesn't look familiar, take a moment to read perltoot and you'll soon learn more than's good for you.)

Typically, this involves creating numerous subroutines that follow a handful of common patterns, like constructor methods and accessor methods. These methods are generally quite simple, requiring only a couple of lines of Perl, but in sufficient bulk, they can cut down on the maintainability of large classes.

Here's a possible implementation for the class whose interface is shown above:

  package MyStruct;
  
  sub new {
    my $callee = shift;
    my $self = bless { @_ }, (ref $callee || $callee);
    return $self;
  }

  sub foo {
    my $self = shift;
    if ( scalar @_ ) {
      $self->{'foo'} = shift();
    } else {
      $self->{'foo'}
    }
  }

  sub bar {
    my $self = shift;
    if ( scalar @_ ) {
      $self->{'bar'} = shift();
    } else {
      $self->{'bar'}
    }
  }

  sub summary {
    my $self = shift;
    join(', ', map { "\u$_: " . $self->$_() } qw( foo bar ) )
  }

Note in particular that the foo and bar methods are almost identical; this is precisely the type of redundancy Class::MakeMethods addresses.

Class::MakeMethods allows you to simply declare those methods to be of a predefined type, and it generates and installs the necessary methods in your package at compile-time.

Here's the equivalent declaration for that same basic class:

  package MyStruct;
  use Class::MakeMethods::Basic::Hash (
    'new'       => 'new',
    'scalar'    => 'foo',
    'scalar'    => 'bar',
  );
  
  sub summary {
    my $self = shift;
    join(', ', map { "\u$_: " . $self->$_() } qw( foo bar ) )
  }

This is the basic purpose of Class::MakeMethods: The "boring" pieces of code have been replaced by succinct declarations, placing the focus on the "unique" or "custom" pieces.

The remaining complexity described in this document basically boils down to figuring out which arguments to pass to generate the specific methods you want.

CLASS HIERARCHY

Because there are so many common types of methods one might wish to generate, the Class::MakeMethods framework provides an extensible system based on subclasses.

When your class requests a method, the base class performs some standard argument parsing, delegates the construction of the actual method to the appropriate subclass, and then installs whatever method the subclass returns.

Role of the Superclass

The Class::MakeMethods package defines a superclass for method-generating modules, and provides a calling convention and subroutine installation mechanism that will be shared by all subclasses.

The superclass also lets you generate several different types of methods in a single call, and will automatically load named subclasses the first time they're used.

Role of the Subclasses

The type of method that gets created is controlled by the specific subclass and generator-method you request. For example, Class::MakeMethods::Basic::Hash has a generator-method scalar(), which is responsible for generating simple scalar-accessor methods for blessed-hash objects.

Because each subclass defines its own set of method types and customization options, a key step is to find your way to the appropriate subclasses, which is the subject of the next section.

Choosing A Subclass

Generally you will want to begin with the Basic::Hash subclass, to create constructor and accessor methods for working with blessed-hash objects (or you might choose the Basic::Array subclass instead). The Basic::Static subclass provides methods for class data shared by all objects in a class.

For a wider range of method types, and a greater degree of customization options, consider the Standard::* or Composite::* subclasses.

Finally, see Class::MakeMethods::Template, which provide an open-ended structure for objects that assemble Perl code on the fly into cachable closure-generating subroutines; if the method you need isn't included, you can extend existing methods by re-defining just the snippet of code that's different.

Other subclasses may be available separately, or you can define your own for future use. (See "EXTENDING" in Class::MakeMethods.)

Naming Convention

When you use Class::MakeMethods, you can refer to the scalar() method provided by the Class::MakeMethods::Basic::Hash class as "Basic::Hash:scalar", and this document will also use that notation.

The names used by the included subclasses and method types reflect three axes of variation, "Group::Subclass:MethodType":

Group

Each group shares a similar style of technical implementation and level of complexity. For example, the Basic::* packages are all simple, while the Composite::* packages all support pre- and post-conditions.

(For a listing of the four main groups of included subclasses, see "SUBCLASS GROUPS", below.)

Subclass

Each subclass generates methods for a similar level of scoping or underlying object type. For example, the *::Hash packages all make methods for objects based on blessed hashes, while the *::Global packages make methods that access class-wide data that will be shared between all objects in a class.

Method Type

Each method type produces a similar type of constructor or accessor. For examples, the *:new methods are all constructors, while the ::scalar methods are all accessors that allow you to get and set a single scalar value.

Bearing that in mind, you should be able to guess the intent of many of the method types based on their names alone; when you see "Standard::Array:list" you can read it as "a type of method to access a list of data stored in an array object, with a standard implementation style" and know that it's going to call Class::MakeMethods::Standard::Array->list().

Subclass Groups

There are several groups of subclasses included in this distribution.

Basic

The Class::MakeMethods::Basic::* subclasses provide stripped down method generators with no configurable options. Subroutines are bound as closures to the name of each method.

Basic::Hash and Basic::Array provide simple object constructors and accessors. Basic::Static provides basic global-data accessors. A listing of available method types is provided in "SUBCLASS CATALOG" in Class::MakeMethods::Basic.

Standard

The Class::MakeMethods::Standard::* subclasses provide a more flexible set of method generators; each Standard method declaration can optionally include a hash of associated parameters, which allows you to tweak some of the characteristics of the methods. Subroutines are bound as closures to a hash of each method's name and parameters.

Standard::Hash and Standard::Array provide object constructor and accessors. The Standard::Global provides for static data shared by all instances and subclasses, while the data for Standard::Inheritable methods trace the inheritance tree to find values, and can be overriden for any subclass or instance. A listing of available method types is provided in "SUBCLASS CATALOG" in Class::MakeMethods::Standard.

Composite

The Class::MakeMethods::Composite::* subclasses provide an extensible set of method generators that support pre- and post-conditions. Subroutines are bound as closures to a hash of each method's name and optional additional data, and to one or more subroutine references which make up the composite behavior of the method.

Composite::Hash and Composite::Array provide object constructor and accessors. The Composite::Global provides for static data shared by all instances and subclasses, while the data for Composite::Inheritable methods can be overriden for any subclass or instance. A listing of available method types is provided in "SUBCLASS CATALOG" in Class::MakeMethods::Composite.

Template

Class::MakeMethods::Template extends MakeMethods with a text templating system that can assemble Perl code fragments into a desired subroutine. The code for generated methods is eval'd once for each type, and then repeatedly bound as closures to method-specific data for better performance.

Templates for dozens of types of constructor, accessor, and mutator methods are included, ranging from from the mundane (constructors and value accessors for hash and array slots) to the esoteric (inheritable class data and "flyweight" accessors with external indexes). A listing is provided in "SUBCLASS CATALOG" in Class::MakeMethods::Template.

Emulation Adaptors

In several cases, Class::MakeMethods provides functionality closely equivalent to that of an existing module, and it is simple to map the existing module's interface to that of Class::MakeMethods.

Emulators are included for Class::MethodMaker, Class::Accessor::Fast, Class::Data::Inheritable, Class::Singleton, and Class::Struct, each of which passes the original module's test suite, usually requiring only a single-line change.

USAGE

See "USAGE" in Class::MakeMethods for a guide to the calling conventions that apply to all subclasses.

About Precedence

Rather than passing the method declaration arguments when you use one of these packages, you may instead pass them to a subsequent call to the class method make.

The difference between use and make is primarily one of precedence; the use keyword acts as a BEGIN block, and is thus evaluated before make would be. In particular, a use at the top of a file will be executed before any subroutine declarations later in the file have been seen, whereas a make at the same point in the file will not.

By default, Class::MakeMethods will not install generated methods over any pre-existing methods in the target class. To override this you can pass -ForceInstall => 1 as initial arguments to use or make.

If methods with the same name already exist, earlier calls to use or make() win over later ones, but within each call, later declarations superceed earlier ones.

Here are some examples of the results of these precedence rules:

  # 1
  use Class::MakeMethods::Basic::Hash (
    'scalar'=>['baz'] # baz() not seen yet, so we generate, install
  );
  sub baz { 1 } # Subsequent declaration overwrites it, with warning
  
  # 2
  sub foo { 1 }
  use Class::MakeMethods::Basic::Hash (
    'scalar'=>['foo'] # foo() is already declared, so has no effect
  );
  
  # 3
  sub bar { 1 }
  use Class::MakeMethods::Basic::Hash ( 
      -ForceInstall => 1, # Set flag for following methods...
    'scalar' => ['bar']   # ... now overwrites pre-existing bar()
  );
  
  # 4
  Class::MakeMethods::Basic::Hash->make(
    'scalar'=>['blip'] # blip() is already declared, so has no effect
  );
  sub blip { 1 } # Although lower than make(), this "happens" first
  
  # 5
  sub ping { 1 } 
  Class::MakeMethods::Basic::Hash->make(
      -ForceInstall => 1, # Set flag for following methods...
    'scalar' => ['ping']  # ... now overwrites pre-existing ping()
  );

EXAMPLES

The following examples indicate some of the capabilities of Class::MakeMethods.

Access Control Example

The following defines a secret_password method, which will croak if it is called from outside of the declaring package.

  use Class::MakeMethods::Composite::Hash
    'scalar' => [ 'secret_password' => { permit => 'pp' } ];

Similar functionality is also available as:

  use Class::MakeMethods::Template::Hash
    'scalar' => [ '--private', 'secret_password' ];

(See Class::MakeMethods::Template::Universal for information about the private modifier.)

String and Numeric Accessors

You can store arbitrary values using the scalar accessors declared above, but the Template subclasses also provide other more specialized accessors that can facilitate the use of specific types of data.

For example, we could declare the following class to hold information about available Perl packages:

  package MyVersionInfo;
  use Class::MakeMethods::Template::Hash (
    'new'     => 'new',
    'string'  => 'package',
    'number'  => 'version',
  );

  sub summary {
    my $self = shift;
    return $self->package() . " is at version " . $self->version()
  }

The basic usage of this class is similar to the one we defined above:

  package main;
  use MyVersionInfo;
  
  my $obj = MyVersionInfo->new( package=>"Class::MakeMethods");
  $obj->version( 2.0 );
  print $obj->summary();

However, these accessors provide a bit of diagnostic type checking; an attempt to call $obj->version("foo") will cause your program to croak.

Reference Accessor and Helper Methods

For references to arrays and hashes, the Template subclasses provide accessors with extra "helper methods" to facilitate method-based interaction.

Here's a class whose instances each store a string and an array reference, along with a method to search the directories:

  package MySearchPath;
  use Class::MakeMethods::Template::Hash (
    'new'     => 'new',
    'string'  => 'name',
    'array'   => 'directories',
  );
  
  sub search {
    my $self = shift; 
    my $target = shift;
    foreach my $dir ( $self->directories ) {
      my $candidate = $dir . '/' . $target;
      return $candidate if ( -e $candidate );
    }
    return;
  }

Note that the directories accessor returns the contents of the array when called in a list context, making it easier to loop over.

And here's a sample usage:

  package main;
  use MySearchPath;
  
  my $obj = MySearchPath->new( name=>"libs", directories=>['/usr/lib'] );
  $obj->push_directories( '/usr/local/lib' );
  print "Searching in " . $obj->count_directories() . "directories.\n";
  foreach ( 'libtiff', 'libjpeg' ) {
    print "Checking $_: " . ( $obj->search("$_.so") || 'not found' ) . "\n";
  }

Note the use of the push_* and count_* "helper" accessor methods, which are defined by default for all 'array' declarations.

Consult Class::MakeMethods::Template::Generic for more information about the available types of reference accessors, and the various methods they define.

Object Accessors

There's also a specialized accessor for object references:

  package MyStruct;
  use Class::MakeMethods::Template::Hash (
    'new'    => 'new',
    'object' => [ 'widget' => {class=>'MyWidgetClass', delegate=>"twiddle"} ],
  );

(Note that the class and delegate values specified above are method parameters, which provide additional information about the widget declaration; see "Standard Declaration Syntax" for more information.)

The above declaration creates methods equivalent to the following:

  package MyStruct;

  sub widget {
    my $self = shift;
    if ( scalar @_ ) { 
      if (ref $_[0] and UNIVERSAL::isa($_[0], 'MyWidgetClass')) { 
        $self->{widget} = shift;
      } else {
        $self->{widget} = MyWidgetClass->new(@_);
      }
    } else {
      return $self->{widget};
    }
  }
  
  sub clear_widget {
    my $self = shift;
    $self->{widget} = undef;
  }
  
  sub twiddle { 
    my $self = shift;
    my $obj = $self->widget() 
      or Carp::croak("Can't forward twiddle because widget is empty");
    $obj->twiddle(@_) 
  } 

Tree Structure Example

In this example we will create a pair of classes with references to other objects.

The first class is a single-value data object implemented as a reference to a scalar.

  package MyTreeData;
  use Class::MakeMethods::Template::Scalar (
    'new'     => 'new',
    'string'  => 'value',
  );

The second class defines a node in a tree, with a constructor, an accessor for a data object from the class above, and accessors for a list of child nodes.

  package MyTreeNode;
  use Class::MakeMethods::Template::Hash (
    'new'     => 'new',
    'object -class MyTreeData'  => 'data',
    'array_of_objects -class MyTreeNode' => 'children',
  );
  
  sub depth_first_data {
    my $self = shift;
    return $self->data, map { $_->depth_first_data() } $self->children;
  }

Here's a sample of how the above classes could be used in a program.

  package main;
  use MyTreeData;
  use MyTreeNode;

  my $node = MyTreeNode->new( 
      data => { value=>'data1' }, 
      children => [ { value=>'data3' } ] 
  );
  $node->push_children( MyTreeNode->new( data => { value=>'data2' } ) );
  
  foreach my $data ( $node->depth_first_data ) {
    print $data->value();
  }

Mixing Object and Global Methods

Here's a package declaration using two of the included subclasses, Standard::Hash, for creating and accessing hash-based objects, and Basic::Static, for simple global-value accessors:

  package MyQueueItem;
  
  use Class::MakeMethods::Standard::Hash (
    new => { name => 'new', defaults=>{ foo => 'Foozle' } },
    scalar => [ 'foo', 'bar' ],
    hash => 'history'
  );
  
  use Class::MakeMethods::Basic::Static (
    scalar => 'Debug',
    array  => 'InQueue',
  );
  
  sub AddQueueItem {
    my $class = shift;
    my $instance = shift;
    $instance->history('AddQueueItem' => time());
    $class->InQueue([0, 0], $instance);    
  }
  
  sub GetQueueItem {
    my $class = shift;
    $class->InQueue([0, 1], []) or $class->new
  }

Changing Method Names

The Template subclasses allow you to control the names assigned to the methods you generate by selecting from several naming interfaces.

For example, the accessors declared above use a default, Perl-ish style interface, in which a single method can be called without an argument to retrieve the value, or with an argument to set it. However, you can also select a more Java-like syntax, with separate get* and set* methods, by including the '--java' template specification:

  package MyStruct;
  use Class::MakeMethods::Template::Hash (
    'new'     => 'new',
    'scalar'  => '--java Foo',
  );

(Note that the declaration of Foo could also have been written as 'scalar --java' => 'Foo' or 'scalar' => ['--java', 'Foo'], or 'scalar' => [ 'foo' = { 'interface'=>'java' } ], all of which are interpreted identically; see the Class::MakeMethods section on "Argument Normalization" for details.)

Usage of this accessor would then be as follows:

  package main;
  use MyStruct;
  
  my $obj = MyStruct->new( setFoo => "Foozle" );
  print $obj->getFoo();
  $obj->setFoo("Bozzle");

Lazy-Init Interface

Templapte scalar accessors declared with the "init_and_get" interface can be used for "memoization" or lazy-evaluation for object attributes. If the current accessor value is undefined, they will first call a user-provided init_* method and save its value.

  package MyWidget;
  use Class::MakeMethods::Template::Hash (
    'new --with_values' => [ 'new' ],
    'scalar --init_and_get' => [ 'foo', 'count', 'result' ],
  );
  
  sub init_foo { 
    return 'foofle';
  }
  
  sub init_count {
    return '3';
  }
  
  sub init_result {
    my $self = shift;
    return $self->foo x $self->count;
  }
  ...
  
  my $widget = MyWidget->new();
  print $widget->result; # output: fooflefooflefoofle
  
  # if values are predefined, the init methods are not used
  my $other_widget = MyWidget->new( foo => 'bar', count => 2 );
  print $widget->result; # output: barbar  

(See Class::MakeMethods::Generic for more information about init_and_get. This interface is also supported by all of Generic's subclasses, so you can add lazy-init methods for global data, class data, array objects, etc. Unfortunately, to date it is only supported for scalar-value accessors...)

String Concatenation Interface

The following defines a get_concat method i, and specifies a string to use when joining additional values when this method is called.

  use Class::MakeMethods::Template::Hash
    'string' => [ '--get_concat', 'i', { join => ' - ' } ];

(See Class::MakeMethods::Template::Generic for information about the string get_concat interface.)

SEE ALSO

Getting-Started Resources

Ron Savage has posted a pair of annotated examples, linked to below. Each demonstrates building a class with MakeMethods, and each includes scads of comments that walk you through the logic and demonstrate how the various methods work together.

  http://savage.net.au/Perl-tutorials.html
  http://savage.net.au/Perl-tutorials/tut-33.tgz
  http://savage.net.au/Perl-tutorials/tut-34.tgz

Package Documentation

See Class::MakeMethods for an overview of this framework and the general calling conventions for use and make.

See Class::MakeMethods::Basic, Class::MakeMethods::Standard, Class::MakeMethods::Composite, and Class::MakeMethods::Template for information about each family of subclasses.

See Class::MakeMethods::ReadMe for distribution, installation, version and support information.