=head1 NAME

Aspect-Oriented Perl Cookbook - recipes for common situations

=head1 DESCRIPTION

This cookbook contains recipes for using aspect-oriented techniques
to solve common problems.

=head1 Tracing the call flow

=head2 Problem

You want to see how subroutines call each other while your program
is running.

=head2 Solution

Suppose you want to see the call flow for subroutines within the
C<Foo> package:

  use Aspect qw(advice calls returns);

  my $adv1 = advice(calls(qr/^Foo::/), sub {
    $::indent++;
    print ' ' x ($::indent - 1), $::thisjp->signature(@_), "\n"
  })->enable;

  my $adv2 = advice(returns(qr/^Foo::/), sub { $::indent-- })->enable;

or, using the attribute interface to creating advice:

  use Aspect::Attribute;

  sub adv1 : Before(qr/^Foo::/) {
    $::indent++;
    print ' ' x ($::indent - 1), $::thisjp->signature(@_), "\n"
  }

  sub adv2 : After(qr/^Foo::/) { $::indent-- }

=head2 Discussion

Create a pointcut designating the call join points of the subroutines
you're interested in; then another designating the return join
points of those subroutines. Remember the call level by increasing
a variable in the call advice, then decreasing it in the return
advice. Output the desired information in the call advice.

=head2 See Also

The C<cookbook/callflow.pl> and C<cookbook/callflow_attr.pl> example
programs.



=head1 Change Tracking

=head2 Problem

You need to be able to tell if properties of an object have changed.

=head2 Solution

  package Foo;
  use Aspect::Attribute;

  sub changed1 : After(qr/^Foo::set[XYZ]/) { $_[0]->{changed} = 1 }

  sub test_and_clear {
    my $self = shift;
    my $c = $self->{changed};
    $self->{changed} = 0;
    $c || 0
  }

=head2 Discussion

Without aspects, you would have to maintain the flag manually in
each method that sets a value whose change status you wish to track.
Each such method would have to make a call to a C<set_changed>
method. This behavior of setting the changed flag at each relevant
operation is the cross-cutting concern we wish to encapsulate with
aspects.

Using Aspect-oriented Perl in this case has several advantages:

The behavior is stated explicitly. The programmer does not have to
check all routines to see which ones set the changed flag and deduce
a pattern from those calls.

The behavior is easy to turn on and off. If you don't need the
functionality, change tracking in this case, anymore, you just
remove the aspect. No other code has to be changed. This is because
the functionality is explicitly captured in an aspect.

The behavior is more consistent. If you add more C<set*> methods,
the advice code of the change tracking aspect will still be applied.
Without aspects you would have to remember to set the flag yourself.

=head2 See Also

The C<cookbook/changed.pl> example program.



=head1 Bounds-checking Pre-condition

=head2 Problem

You want to impose bounds-checking on values passed to C<set_x>
and <set_y>.

=head2 Solution

  my %bounds = (
    x => { min =>  1, max =>  9 },
    y => { min => 11, max => 19 }
  );

  sub bounds1 : Before(qr/^main::set_/) {
    (my $prop = $::thisjp->sub) =~ s/^main::set_//;
    return unless exists $bounds{$prop};
    return if $_[0] >= $bounds{$prop}{min} && $_[0] <= $bounds{$prop}{max};
    croak sprintf "%s is out of bounds: min = %s, max = %s, given = %s",
      $prop, $bounds{$prop}{min}, $bounds{$prop}{max}, $_[0];
}

=head2 Discussion

Design-by-contract is a software engineering technique in which
each module of a software system specifies explicitly what input
(or data or arguments) it requires, and what output (or information
or results) it guarantees to produce in response. See the
C<Class::Contract> module for a framework implementing Design-by-contract
OOP.

Aspect-oriented Perl makes it possible to implement pre- and
post-condition testing in a modular way.

This advice deals with the bounds-checking aspect of pre-condition
testing for C<set_*> subroutines, but only those for which bounds
are defined.

Testing pre-conditions and post-conditions are especially useful
during development.

=head2 See Also

The C<cookbook/bounds.pl> example program.


=head1 AUTHOR

Marcel GrE<uuml>nauer <marcel.gruenauer@chello.at>

=head1 COPYRIGHT

Copyright 2001 Marcel GrE<uuml>nauer. All rights reserved.

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=head1 SEE ALSO

Aspect::Intro(3pm), Aspect(3pm).

=cut
