File Coverage

lib/Aspect/Advice.pm
Criterion Covered Total %
statement 45 47 95.7
branch 8 10 80.0
condition 3 3 100.0
subroutine 14 14 100.0
pod 0 8 0.0
total 70 82 85.3


line stmt bran cond sub pod time code
1             package Aspect::Advice;
2              
3 1     1   6 use strict;
  1         2  
  1         42  
4 1     1   7 use warnings;
  1         2  
  1         41  
5 1     1   7 use Carp;
  1         2  
  1         90  
6 1     1   8 use Aspect::AdviceContext;
  1         1  
  1         39  
7 1     1   7 use Aspect::Weaver;
  1         1  
  1         792  
8              
9             sub new {
10 9     9 0 16 my ($class, $type, $code, $pointcut) = @_;
11 9         63 my $self = bless {
12             weaver => Aspect::Weaver->new, # a weaver that will install advice code
13             hooks => undef, # list of Hook::LexWrap hooks
14             type => $type, # before or after
15             code => $code, # the advice code
16             pointcut => $pointcut, # the advice pointcut
17             }, $class;
18 9         35 $self->install;
19 9         16037 return $self;
20             }
21              
22             # private ---------------------------------------------------------------------
23              
24             sub install {
25 9     9 0 18 my $self = shift;
26 9         30 my $weaver = $self->weaver;
27 9         26 my $type = $self->type;
28 9         31 my $pointcut = $self->pointcut;
29 9         28 my $code = $self->code;
30             # find all pointcuts that are staticaly matched
31             # wrap the method with advice code and install the wrapper
32 9         31 for my $sub_name ($weaver->get_sub_names) {
33 21906 100       42167 next unless $pointcut->match_define($sub_name);
34 9         80 my $wrapped_code = $self->wrap_code($type, $code, $pointcut, $sub_name);
35 9         66 $self->add_hooks
36             ($weaver->install($type, $sub_name, $wrapped_code));
37             }
38             }
39              
40             # return wrapper sub to be installed instead of original
41             # wrapper sub creates context then calls advice code
42             # it runs only if the pointcut answers true to match_run()
43             sub wrap_code {
44 9     9 0 31 my ($self, $type, $code, $pointcut, $sub_name) = @_;
45              
46             return sub {
47             # hacked Hook::LexWrap calls hooks with 3 params
48 14     14   23 my ($params, $original, $return_value) = @_;
49 14         22 my $runtime_context = {};
50 14 100       179 return unless $pointcut->match_run($sub_name, $runtime_context);
51              
52             # create context for advice code
53 13         90 my $advice_context = Aspect::AdviceContext->new(
54             sub_name => $sub_name,
55             type => $type,
56             pointcut => $pointcut,
57             params => $params,
58             return_value => $return_value,
59             original => $original,
60             %$runtime_context,
61             );
62            
63             # execute advice code with its context
64 13 50       40 if (wantarray)
    50          
65 0         0 { () = &$code($advice_context) }
66             elsif (defined wantarray)
67 13         38 { my $dummy = &$code($advice_context) }
68             else
69 0         0 { &$code($advice_context) }
70              
71             # if proceeding to original, modify params, else modify return value
72 13 100 100     100 if ($type eq 'before' && $advice_context->proceed)
73 4         12 { @$params = $advice_context->params }
74             else
75 9         23 { $_[-1] = $advice_context->return_value }
76 9         107 };
77             }
78              
79 9     9 0 17 sub add_hooks { push @{shift->{hooks}}, shift }
  9         136  
80              
81 9     9 0 27 sub weaver { shift->{weaver} }
82 9     9 0 24 sub type { shift->{type} }
83 9     9 0 17 sub code { shift->{code} }
84 9     9 0 20 sub pointcut { shift->{pointcut} }
85              
86             1;
87              
88             =head1 NAME
89              
90             Aspect::Advice - change how Perl code is run at a pointcut
91              
92             =head1 SYNOPSIS
93              
94             # creating using public interface: trace calls to Account subs
95             use Aspect;
96             before { print 'called: '. shift->sub_name } call qw/^Account::/;
97              
98             # creating using internal interface
99             use Aspect::Advice;
100             $advice = Aspect::Advice->new(before =>
101             { print 'called: '. shift->sub_name },
102             call qw/^Account::/
103             );
104              
105             =head1 DESCRIPTION
106              
107             An advice is composed of a pointcut and some code that will run at the
108             pointcut. The code is run C or C the pointcut, depending
109             on advice C.
110              
111             You do not normally create advice using the constructor. By Cing
112             L, you get 2 subs imported: C and C,
113             that do what you need. They also store the advice if called in void
114             context, so you do not need to keep in scope. The advice code will be
115             removed when the advice object is destroyed.
116              
117             The advice code is given one parameter: an L. You
118             use this object to change the parameter list for the matched sub, modify
119             return value, find out information about the matched sub, and more.
120              
121             This class has no public methods that do anything, but there are
122             accessors C, C, C, and C, if you
123             need them.
124              
125             =head1 SEE ALSO
126              
127             See the L pod for a guide to the Aspect module.
128              
129             =cut