The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
NAME
    Aspect - Aspect-Oriented Programming (AOP) for Perl

SYNOPSIS
      package Person;
      
  sub create      { ... }
      
  sub set_name    { ... }
      
  sub get_address { ... }
      
  package main;
      
  use Aspect;
      
  # Using reusable aspects
      aspect Singleton => 'Person::create';        # let there be only one Person
      aspect Profiler  => call qr/^Person::set_/;  # profile calls to setters
      
  # Append extra argument when Person::get_address is called:
      # the instance of the calling Company object, iff get_address
      # is in the call flow of Company::get_employee_addresses.
      # aspect will live as long as $wormhole reference is in scope
      $aspect = aspect Wormhole => 'Company::make_report', 'Person::get_address';
      
  # Writing your own advice
      $pointcut = call qr/^Person::[gs]et_/; # defines a collection of events
      
  # Advice will live as long as $before is in scope
      $before = before {
          print "g/set will soon be called";
      } $pointcut;
      
  # Advice will live lexical, because it is created in void context 
      after {
          print "g/set has just been called";
      } $pointcut;
      
  before {
          print "get will soon be called, if in call flow of Tester::run_tests";
      } call qr/^Person::get_/
      & cflow tester => 'Tester::run_tests';
      
  # Conditionally hijack a method if some condition is true
      around {
          my $context = shift;
          if ( $context->self->customer_name eq 'Adam Kennedy' ) {
              $context->return_value('One meeeelion dollars');
          } else {
              # Take a dollar off everyone else
              $context->run_original;
              $context->return_value( $context->return_value - 1 );
          }
      } call 'Bank::Account::balance';

DESCRIPTION
    Aspect-Oriented Programming (AOP) is a programming method developed by
    Xerox PARC and others. The basic idea is that in complex class systems
    there are certain aspects or behaviors that cannot normally be expressed
    in a coherent, concise and precise way. One example of such aspects are
    design patterns, which combine various kinds of classes to produce a
    common type of behavior. Another is logging. See <http://www.aosd.net>
    for more info.

    The Perl "Aspect" module closely follows the terminology of the AspectJ
    project (<http://eclipse.org/aspectj>). However due to the dynamic
    nature of the Perl language, several "AspectJ" features are useless for
    us: exception softening, mixin support, out-of-class method
    declarations, and others.

    The Perl "Aspect" module is focused on subroutine matching and wrapping.
    It allows you to select collections of subroutines using a flexible
    pointcut language, and modify their behavior in any way you want.

  Terminology
    Join Point
        An event that occurs during the running of a program. Currently only
        calls to subroutines are recognized as join points.

    Pointcut
        An expression that selects a collection of join points. For example:
        all calls to the class "Person", that are in the call flow of some
        "Company", but *not* in the call flow of "Company::make_report".
        "Aspect" supports "call()", and "cflow()" pointcuts, and logical
        operators ("&", "|", "!") for constructing more complex pointcuts.
        See the Aspect::Pointcut documentation.

    Advice
        A pointcut, with code that will run when it matches. The code can be
        run before or after the matched sub is run.

    Advice Code
        The code that is run before or after a pointcut is matched. It can
        modify the way that the matched sub is run, and the value it
        returns.

    Weave
        The installation of advice code on subs that match a pointcut.
        Weaving happens when you create the advice. Unweaving happens when
        the advice goes out of scope.

    The Aspect
        An object that installs advice. A way to package advice and other
        Perl code, so that it is reusable.

  Features
    *   Create and remove pointcuts, advice, and aspects.

    *   Flexible pointcut language: select subs to match using string
        equality, regexp, or "CODE" ref. Match currently running sub, or a
        sub in the call flow. Build pointcuts composed of a logical
        expression of other pointcuts, using conjunction, disjunction, and
        negation.

    *   In advice code, you can: modify parameter list for matched sub,
        modify return value, decide if to proceed to matched sub, access
        "CODE" ref for matched sub, and access the context of any call flow
        pointcuts that were matched, if they exist.

    *   Add/remove advice and entire aspects during run-time. Scope of
        advice and aspect objects, is the scope of their effect.

    *   A reusable aspect library. The Wormhole, aspect, for example. A base
        class makes it easy to create your own reusable aspects. The Memoize
        aspect is an example of how to interface with AOP-like modules from
        CPAN.

  Why create this module?
    Perl is a highly dynamic language, where everything this module does can
    be done without too much difficulty. All this module does, is make it
    even easier, and bring these features under one consistent interface. I
    have found it useful in my work in several places:

    *   Saves me from typing an entire line of code for almost every
        "Test::Class" test method, because I use the TestClass aspect.

    *   I use the Wormhole aspect, so that my methods can acquire implicit
        context, and so I don't need to pass too many parameters all over
        the place. Sure I could do it with "caller()" and "Hook::LexWrap",
        but this is much easier.

    *   Using custom advice to modify class behavior: register objects when
        constructors are called, save object state on changes to it, etc.
        All this, while cleanly separating these concerns from the effected
        class. They exist as an independent aspect, so the class remains
        unpolluted.

    The "Aspect" module is different from "Hook::Lexwrap" (which it uses for
    the actual wrapping) in two respects:

    *   Select join points using flexible pointcut language instead of the
        sub name. For example: select all calls to "Account" objects that
        are in the call flow of "Company::make_report".

    *   More options when writing the advice code. You can, for example, run
        the original sub, or append parameters to it.

  Using Aspect.pm
    This package is a facade on top of the Perl AOP framework. It allows you
    to create pointcuts, advice, and aspects in a simple declarative
    fastion.

    You will be mostly working with this package ("Aspect"), and the advice
    context package.

    When you "use Aspect;" you will import a family of around a dozen
    functions. These are all factories that allow you to create pointcuts,
    advice, and aspects.

  Pointcuts
    Pointcuts select join points, so that an advice can run code when they
    happen. The most common pointcut you will probably use is "call()". For
    example:

      $p = call 'Person::get_address';

    This selects the calling of "Person::get_address()" as defined in the
    symbol table during weave-time. The string is a pointcut spec, and can
    be expressed in three ways:

    "string"
        Select only the sub whose name is equal to the spec string.

    "regexp"
        Select only the subs whose name matches the regexp. The following
        will match all the subs defined on the "Person" class, but not on
        the "Person::Address" class.

          $p = call qr/^Person::\w+$/;

    "CODE" ref
        Select only subs, where the supplied code, when run with the sub
        name as only parameter, returns true. The following will match all
        calls to subs whose name isa key in the hash %subs_to_match:

          $p = call sub { exists $subs_to_match{shift()} }

    Pointcuts can be combined to form logical expressions, because they
    overload "&", "|", and "!", with factories that create composite
    pointcut objects. Be careful not to use the non-overloadable "&&", and
    "||" operators, because you will get no error message.

    Select all calls to "Person", which are not calls to the constructor:

      $p = call qr/^Person::\w+$/ & ! call 'Person::create';

    The second pointcut you can use, is "cflow()". It selects only the subs
    that are in call flow of its spec. Here we select all calls to "Person",
    only if they are in the call flow of some method in "Company":

      $p = call qr/^Person::\w+$/ & cflow company => qr/^Company::\w+$/;

    The "cflow()" pointcut takes two parameters: a context key, and a
    pointcut spec. The context key is used in advice code to access the
    context (params, sub name, etc.) of the sub found in the call flow. In
    the example above, the key can be used to access the name of the
    specific sub on "Company" that was found in the call flow of the
    "Person" method.The second parameter is a pointcut spec, that should
    match the sub required from the call flow.

    See the Aspect::Pointcut docs for more info.

  Advice
    An advice definition is just some code that will run on a match of some
    pointcut. The "advice" can run around the entire call to allow lexical
    variables to capture custom information on the way into the function
    that will be needed when it exists, or it can be more specific and only
    run before the sub, after the sub runs and returns, after the sub throws
    an exception, or after the sub runs regardless of the result.

    Using a more specific advice type will allow the optimiser to generate
    smaller and faster hooks into your code.

    You create advice using "around", "before", "after_returning",
    "after_throwing" or "after()".

    These take a "CODE" ref, and a pointcut, and install the code on the
    subs that match the pointcut. For example:

      after {
          print "Person::get_address has returned!\n";
      } call 'Person::get_address';

    The advice code is run with one parameter: the advice context. You use
    it to learn how the matched sub was run, modify parameters, return
    value, and if it is run at all.

    When the advice is created in void context, it remains enabled until the
    interpreter dies, or the symbol table reloaded.

    However, advice code can also be applied to matching pointcuts (i.e. the
    advice is enabled) for only a specific scope by declare it in scalar
    context and storing the returned guard object.

    This allows you to neatly control enabling and disabling of advice:

      SCOPE: {
         my $advice = before { print "called!\n" } $pointcut;
         # Do something while the device is enabled
      }
      
  # The advice is now disabled

  Aspects
    Aspects are just plain old Perl objects, that install advice, and do
    other AOP-like things, like install methods on other classes, or mess
    around with the inheritance hierarchy of other classes. A good base
    class for them is Aspect::Modular, but you can use any Perl object as
    long as the class inherits from Aspect::Library.

    If the aspect class exists immediately below the namespace
    "Aspect::Library", then it can be easily created with the following
    shortcut.

      aspect Singleton => 'Company::create';

    This will create an Aspect::Library::Singleton object. This reusable
    aspect is included in the "Aspect" distribution, and forces singleton
    behavior on some constructor, in this case, "Company::create()".

    Such aspects share a similar behaviour to advice. If enabled in void
    context they will be installed permanently, but if called in scalar
    context they will return a guard object that allows the aspect to be
    enabled only until the end of the current scope.

  Internals
    Due to the dynamic nature of Perl, there is no need for processing of
    source or byte code, as required in the Java and .NET worlds.

    The implementation is very simple: when you create advice, its pointcut
    is matched using "match_define()" to find every sub defined in the
    symbol table that might match against the pointcut (potentially subject
    to further runtime conditions).

    Those that match, will get a special wrapper installed. The wrapper only
    executes if, during run-time, a compiled context test for the pointcut
    returns true.

    The wrapper code creates an advice context, and gives it to the advice
    code.

    Some pointcuts like "call()" are static, so the compiled run-time
    function always returns true, and "match_define()" returns true if the
    sub name matches the pointcut spec.

    Some pointcuts like "cflow()" are dynamic, so "match_define()" always
    returns true, but the compiled run-time function returns true only if
    some condition within the point is true.

    To make this process faster, when the advice is installed, the pointcut
    will not use itself directly for the compiled run-time function but will
    additionally generate a "curried" (optimised) version of itself.

    This curried version uses the fact that the run-time check will only be
    called if it matches the "call()" pointcut pattern, and so no "call()"
    pointcuts needed to be tested at run-time unless they are in deep and
    complex nested coolean logic. It also handles collapsing any boolean
    logic impacted by the safe removal of the "call()" pointcuts.

    If you use only "call()" pointcuts (alone or in boolean combinations)
    the currying results in a null test (the pointcut is optimised away
    entirely) and so the need to make a run-time point test will be removed
    altogether from the generated advice hooks, reducing call overheads
    significantly.

    If your pointcut does not have any static conditions (i.e. "call") then
    the wrapper code will need to be installed into every function on the
    symbol table. This is highly discouraged and liable to result in hooks
    on unusual functions and unwanted side effects.

FUNCTIONS
    TO BE COMPLETED

LIMITATIONS
  Inheritance Support
    Support for inheritance is lacking. Consider the following two classes:

      package Automobile;
      ...
      sub compute_mileage { ... }
      
  package Van;
      use base 'Automobile';

    And the following two advice:

      before { print "Automobile!\n" } call 'Automobile::compute_mileage';
      before { print "Van!\n"        } call 'Van::compute_mileage';

    Some join points one would expect to be matched by the call pointcuts
    above, do not:

      $automobile = Automobile->new;
      $van = Van->new;
      $automobile->compute_mileage; # Automobile!
      $van->compute_mileage;        # Automobile!, should also print Van!

    "Van!" will never be printed. This happens because "Aspect" installs
    advice code on symbol table entries. "Van::compute_mileage" does not
    have one, so nothing happens. Until this is solved, you have to do the
    thinking about inheritance yourself.

  Performance
    You may find it very easy to shoot yourself in the foot with this
    module. Consider this advice:

      # Do not do this!
      before {
          print shift->sub_name;
      } cflow company => 'MyApp::Company::make_report';

    The advice code will be installed on every sub loaded. The advice code
    will only run when in the specified call flow, which is the correct
    behavior, but it will be *installed* on every sub in the system. This
    can be slow. It happens because the "cflow()" pointcut matches *all*
    subs during weave-time. It matches the correct sub during run-time. The
    solution is to narrow the pointcut:

      # Much better
      before {
          print shift->sub_name;
      } call qr/^MyApp::/
      & cflow company => 'MyApp::Company::make_report';

TO DO
    There are a number of things that could be added, if people have an
    interest in contributing to the project.

Documentation
    * cookbook

    * tutorial

    * example of refactoring a useful CPAN module using aspects

  Pointcuts
    * new pointcuts: execution, cflowbelow, within, advice, calledby. Sure
    you can implement them today with Perl treachery, but it is too much
    work.

    * need a way to match subs with an attribute, attributes::get() will not
    work for some reason

    * isa() support for method pointcuts as Gaal Yahas suggested: match
    methods on class hierarchies without callbacks

    * Perl join points: phasic- BEGIN/INIT/CHECK/END

    * The previous items indicate a need for a real join point specification
    language

  Weaving
    * look into byte code manipulation with B:: modules- could be faster, no
    need to mess with caller, and could add many more pointcut types. All we
    need to do for sub pointcuts is add 2 gotos to selected subs.

    * a debug flag to print out subs that were matched on match_define

    * warnings when over 1000 methods wrapped

    * support more pulling (vs. pushing) of aspects into packages:
    attributes, package specific join points

    * add whatever constructs required for mocking packages, objects,
    builtins

    * allow finer control of advice execution order

  Reusable Aspects
    * need better example for wormhole- something less tedius

    * use Scalar-Footnote for adding aspect state to objects, e.g. in
    Listenable. Problem is it is still in developer release state

    * Listenable: when listeners go out of scope, they should be removed
    from listenables, so you don't have to remember to remove them manually

    * Listenable: should overload some operator on listenables so that it is
    easier to add/remove listeners, e.g.: $button += (click => sub { print
    'click!' });

    * design aspects: DBC, threading, more GOF patterns

    * middleware aspects: security, load balancing, timeout/retry,
    distribution

    * Perl aspects: add use strict/warning/Carp to all matched packages.
    Actually, Spiffy, Toolkit, and Toolset do this already very nicely.

    * interface with existing Perl modules for logging, tracing, param
    checking, generally all things that are AOPish on CPAN. One should be
    able to use it all through one consistent interface. If I have a good
    set of pointcuts, I should be able to do all kinds of cross- cutting
    things with them.

    * UnderscoreContext aspect: subs that match will, if called with no
    parameters, get $_, and if in void context, return value will set $_.
    Allows you to use your subs like builtins, that fall back on $_. So if
    we have a sub:

         sub replace_foo { my $in = shift; $in =~ s/foo/bar; $in }

      Then both calls would be equivalent:

         $_ = replace_foo($_);
         replace_foo;

    * a generic FriendParamAppender aspect, that adds to a param list for
    affected methods, any object the method requires. Heuristics are applied
    to find the friend: maybe it is available in the call flow? Perhaps
    someone in the call flow has an accessor that can get it? Maybe a
    lexical in some sub in the call flow has it? The point is to cover all
    cases where we pass objects around, so that we don't have to. A
    generalization of the wormhole aspect.

SUPPORT
    Please report any bugs or feature requests through the web interface at
    <http://rt.cpan.org/Public/Dist/Display.html?Name=Aspect>.

INSTALLATION
    See perlmodinstall for information and options on installing Perl
    modules.

AVAILABILITY
    The latest version of this module is available from the Comprehensive
    Perl Archive Network (CPAN). Visit <http://www.perl.com/CPAN/> to find a
    CPAN site near you. Or see <http://search.cpan.org/perldoc?Aspsect.pm>.

AUTHORS
    Adam Kennedy <adamk@cpan.org>

    Marcel GrĂ¼nauer <marcel@cpan.org>

    Ran Eilam <eilara@cpan.org>

SEE ALSO
    You can find AOP examples in the "examples/" directory of the
    distribution.

    Aspect::Library::Memoize

    Aspect::Library::Profiler

    Aspect::Library::Trace

COPYRIGHT
    Copyright 2001 by Marcel GrĂ¼nauer

    Some parts copyright 2009 - 2010 Adam Kennedy.

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