=head1 NAME

Class::MakeMethods::Standard - Guide to subclasses


=head1 SYNOPSIS

  package MyObject;
  use Class::MakeMethods::Composite::Hash (
    new => 'new',
    scalar => [ 'foo', 'bar' ],
    array => 'my_list',
    hash => 'my_index',
  );


=head1 DESCRIPTION

This document describes the various subclasses of Class::MakeMethods
included under the Standard::* namespace, and the method types each
one provides.

The Standard subclasses provide a parameterized set of method-generation
implementations.

Subroutines are generated as closures bound to a hash containing
the method name and (optionally) additional parameters.


=head2 Calling Conventions

When you C<use> this package, the method names you provide
as arguments cause subroutines to be generated and installed in
your module.

See L<Class::MakeMethods::Standard/"Calling Conventions"> for more information.

=head2 Declaration Syntax

To declare methods, pass in pairs of a method-type name followed
by one or more method names. 

Valid method-type names for this package are listed in L<"METHOD
GENERATOR TYPES">.

See L<Class::MakeMethods::Standard/"Declaration Syntax"> and L<Class::MakeMethods::Standard/"Parameter Syntax"> for more information.

=cut

package Class::MakeMethods::Composite;

use strict;
use Class::MakeMethods::Standard '-isasubclass';
use Carp;

########################################################################

=head2 About Composite Methods

The methods generated by Class::MakeMethods::Composite are assembled
from groups of "fragment" subroutines, each of which provides some
aspect of the method's behavior.

You can add pre- and post- operations to any composite method.

  package MyObject;
  use Class::MakeMethods::Composite::Hash (
    new => 'new',
    scalar => [ 
      'foo' => { 
	'pre_rules' => [ 
	  sub {
	    # Don't automatically convert list to array-ref
	    croak "Too many arguments" if ( scalar @_ > 2 );
	  }
	],
	'post_rules' => [ 
	  sub {
	    # Don't let anyone see my credit card number!
	    ${(pop)->{result}} =~ s/\d{13,16}/****/g;
	  }
	],
      }
    ],
  );

=cut

use vars qw( $Method );

sub _current_method {
  $Method;
}

sub _build_composite {
  my $class = shift;
  my $fragments = shift;
  map { 
    my $method = $_;
    my @fragments = @{ $fragments->{''} };
    foreach my $flagname ( grep $method->{$_}, qw/ permit modifier / ) {
      my $value = $method->{$flagname};
      my $fragment = $fragments->{$value}
		or croak "Unsupported $flagname flag '$value'";
      push @fragments, @$fragment;
    }
    _bind_composite( $method, @fragments );
  } $class->get_declarations(@_)
}

sub _assemble_fragments {
  my $method = shift;
  my @fragments = @_;
  while ( scalar @fragments ) {
    my ($rule, $sub) = splice( @fragments, 0, 2 );
    if ( $rule =~ s/\A\+// ) {
      unshift @{$method->{"${rule}_rules"}}, $sub  
    } elsif ( $rule =~ s/\+\Z// ) {
      push @{$method->{"${rule}_rules"}}, $sub  
    } elsif ( $rule =~ /\A\w+\Z/ ) {
      @{$method->{"${rule}_rules"}} = $sub;
    } else { 	
      croak "Unsupported rule type '$rule'"
    }
  }
}

sub _bind_composite {
  my $method = shift;
  _assemble_fragments( $method, @_ );
  if ( my $subs = $method->{"init_rules"} ) {
    foreach my $sub ( @$subs ) {
      &$sub( $method );
    }
  }
  $method->{name} => sub {
    local $Method = $method;
    local $Method->{args} = [ @_ ];
    local $Method->{result};    
    local $Method->{scratch};
    # Strange but true: you can local a hash-value in hash that's not 
    # a package variable. Confirmed in in 5.004, 5.005, 5.6.0.

    my $wantarray = wantarray;

    if ( my $subs = $Method->{"pre_rules"} ) {
      foreach my $sub ( @$subs ) {
	&$sub( @{$Method->{args}}, $Method );
      }
    }
    my $subs = $Method->{"do_rules"} 
	or Carp::confess("No operations provided for $Method->{name}");
    if ( ! defined $wantarray ) {
      foreach my $sub ( @$subs ) {
	last if $Method->{result};
	&$sub( @{$Method->{args}}, $Method );	
      }
    } elsif ( ! $wantarray ) {
      foreach my $sub ( @$subs ) {
	last if $Method->{result};
	my $value = &$sub( @{$Method->{args}}, $Method );
	if ( defined $value ) { 
	  $Method->{result} = \$value;
	}
      }
    } else {
      foreach my $sub ( @$subs ) {
	last if $Method->{result};
	my @value = &$sub( @{$Method->{args}}, $Method );
	if ( scalar @value ) { 
	  $Method->{result} = \@value;
	}
      }
    }
    if ( my $subs = $Method->{"post_rules"} ) {
      foreach my $sub ( @$subs ) {
	&$sub( @{$Method->{args}}, $Method );
      }
    }
    return ( 
      ( ref($Method->{result}) eq 'ARRAY'  ) ? @{$Method->{result}} : 
      ( ref($Method->{result}) eq 'REF'    ) ? ${$Method->{result}} : 
      ( ref($Method->{result}) eq 'SCALAR' ) ? ${$Method->{result}} : 
      ()
    );
  }
}

########################################################################

=head1 SUBCLASS CATALOG

=head2 Composite::Hash (Instances)

Methods for objects based on blessed hashes.

=over 4

=item *

new: create and copy instances

=item *

scalar: get and set scalar values in each instance

=item *

array: get and set values stored in an array refered to in each
instance

=item *

hash: get and set values in a hash refered to in each instance

=item *

object: access an object refered to by each instance

=back

=head2 Composite::Array (Instances)

Methods for manipulating positional values in arrays.

=over 4

=item *

new: create and copy instances

=item *

scalar: get and set scalar values in each instance

=item *

array: get and set values stored in an array refered to in each
instance

=item *

hash: get and set values in a hash refered to in each instance

=item *

object: access an object refered to by each instance

=back

=head2 Composite::Global (Global)

Methods for manipulating global data.

=over 4

=item *

scalar: get and set global scalar

=item *

array: get and set values stored in a global array

=item *

hash: get and set values in a global hash

=item *

object: global access to an object ref

=back


=head2 Composite::Inheritable (Any)

Methods for manipulating data which may be overridden per class or instance. Uses external data storage, so it works with objects of any underlying data type. 

=over 4

=item *

scalar: get and set scalar values for each instance or class

=back


=head2 Composite::Universal (Any)

=over 4

=item *

patch: add pre and post operations to an existing subroutine

=item *

hook: create a subroutine intended to have operations added to it

=back

=cut

########################################################################

=head1 SEE ALSO

See L<Class::MakeMethods> for an overview of the method-generation
framework this is based on.

See L<Class::MakeMethods::Guide> for a getting-started guide,
annotated examples of usage, and a listing of the method generation
classes included in this distribution.

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

=cut

1;
