NAME

Aspect-Oriented Perl Cookbook - recipes for common situations

DESCRIPTION

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

Tracing the call flow

Problem

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

Solution

Suppose you want to see the call flow for subroutines within the 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-- }

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.

See Also

The cookbook/callflow.pl and cookbook/callflow_attr.pl example programs.

Change Tracking

Problem

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

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
}

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

See Also

The cookbook/changed.pl example program.

Bounds-checking Pre-condition

Problem

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

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];
}

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 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 set_* subroutines, but only those for which bounds are defined.

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

See Also

The cookbook/bounds.pl example program.

AUTHOR

Marcel Grunauer, <marcel@codewerk.com>

COPYRIGHT

Copyright 2001 Marcel Grunauer. All rights reserved.

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

SEE ALSO

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