File Coverage

blib/lib/Aspect/Point.pm
Criterion Covered Total %
statement 42 56 75.0
branch 11 22 50.0
condition n/a
subroutine 12 16 75.0
pod 4 6 66.6
total 69 100 69.0


line stmt bran cond sub pod time code
1             package Aspect::Point;
2              
3             =pod
4              
5             =head1 NAME
6              
7             Aspect::Point - The Join Point context
8              
9             =head1 SYNOPSIS
10            
11             # An anonymous function suitable for use as advice code
12             # across all advice types (as it uses no limited access methods)
13             my $advice_code = sub {
14             print $_->type; # The advice type ('before')
15             print $_->pointcut; # The matching pointcut ($pointcut)
16             print $_->enclosing; # Access cflow pointcut advice context
17             print $_->sub_name; # The full package_name::sub_name
18             print $_->package_name; # The package name ('Person')
19             print $_->short_name; # The sub name (a get or set method)
20             print $_->self; # 1st parameter to the matching sub
21             print ($_->args)[1]; # 2nd parameter to the matching sub
22             $_->original->(x => 3); # Call matched sub independently
23             $->return_value(4) # Set the return value
24             };
25              
26             =head1 DESCRIPTION
27              
28             Advice code is called when the advice pointcut is matched. In this code,
29             there is often a need to access information about the join point context
30             of the advice. Information like:
31              
32             What is the actual sub name matched?
33              
34             What are the parameters in this call that we matched?
35              
36             Sometimes you want to change the context for the matched sub, such as
37             appending a parameter or even stopping the matched sub from being called
38             at all.
39              
40             You do all these things through the C, which is an object
41             that isa L. It is the only parameter provided to the advice
42             code. It provides all the information required about the match context,
43             and allows you to change the behavior of the matched sub.
44              
45             Note: Modifying parameters through the context in the code of an I
46             advice, will have no effect, since the matched sub has already been called.
47              
48             In a future release this will be fixed so that the context for each advice
49             type only responds to the methods relevant to that context, with the rest
50             throwing an exception.
51              
52             =head2 Cflows
53              
54             If the pointcut of an advice is composed of at least one C the
55             advice code may require not only the context of the advice, but the join
56             point context of the cflows as well.
57              
58             This is required if you want to find out, for example, what the name of the
59             sub that matched a cflow. In the synopsis example above, which method from
60             C started the chain of calls that eventually reached the get/set
61             on C?
62              
63             You can access cflow context in the synopsis above, by calling:
64              
65             $point->enclosing;
66              
67             You get it from the main advice join point by calling a method named after
68             the context key used in the cflow spec (which is "enclosing" if a custom name
69             was not provided, in line with AspectJ terminology). In the synopsis pointcut
70             definition, the cflow part was equivalent to:
71              
72             cflow enclosing => qr/^Company::/
73             ^^^^^^^^^
74              
75             An L will be created for the cflow, and you can access it
76             using the C method.
77              
78             =head1 EXAMPLES
79              
80             Print parameters to matched sub:
81              
82             before {
83             print join ',', $_->args;
84             } $pointcut;
85              
86             Append a parameter:
87              
88             before {
89             $_->args( $_->args, 'extra parameter' );
90             } $pointcut;
91              
92             Don't proceed to matched sub, return 4 instead:
93              
94             before {
95             shift->return_value(4);
96             } $pointcut;
97              
98             Call matched sub again and again until it returns something defined:
99              
100             after {
101             my $point = shift;
102             my $return = $point->return_value;
103             while ( not defined $return ) {
104             $return = $point->original($point->params);
105             }
106             $point->return_value($return);
107             } $pointcut;
108              
109             Print the name of the C object that started the chain of calls
110             that eventually reached the get/set on C:
111              
112             before {
113             print shift->enclosing->self->name;
114             } $pointcut;
115              
116             =head1 METHODS
117              
118             =cut
119              
120 21     21   79 use strict;
  21         29  
  21         1057  
121 21     21   100 use warnings;
  21         24  
  21         482  
122 21     21   97 use Carp ();
  21         34  
  21         349  
123 21     21   77 use Sub::Uplevel ();
  21         32  
  21         293  
124 21     21   72 use Aspect::Point::Static ();
  21         26  
  21         5143  
125              
126             our $VERSION = '0.97_06';
127              
128              
129              
130              
131              
132             ######################################################################
133             # Constructor and Built-In Accessors
134              
135             # sub new {
136             # my $class = shift;
137             # bless { @_ }, $class;
138             # }
139              
140             =pod
141              
142             =head2 pointcut
143              
144             my $pointcut = $_->pointcut;
145              
146             The C method provides access to the original join point specification
147             (as a tree of L objects) that the current join point matched
148             against.
149              
150             Please note that the pointcut returned is the full and complete pointcut tree,
151             due to the heavy optimisation used on the actual pointcut code when it is run
152             there is no way at the time of advice execution to indicate which specific
153             conditions in the pointcut tree matched and which did not.
154              
155             Returns an object which is a sub-class of L.
156              
157             =cut
158              
159             sub pointcut {
160             $_[0]->{pointcut};
161             }
162              
163             =pod
164              
165             =head2 sub_name
166              
167             # Prints "Full::Function::name"
168             before {
169             print $_->sub_name . "\n";
170             } call 'Full::Function::name';
171              
172             The C method returns a string with the full resolved function name
173             at the join point the advice code is running at.
174              
175             =cut
176              
177             sub sub_name {
178             $_[0]->{sub_name};
179             }
180              
181             =pod
182              
183             =head2 package_name
184              
185             # Prints "Just::Package"
186             before {
187             print $_->package_name . "\n";
188             } call 'Just::Package::name';
189              
190             The C parameter is a convenience wrapper around the C
191             method. Where C will return the fully resolved function name, the
192             C method will return just the namespace of the package of the
193             join point.
194              
195             =cut
196              
197             sub package_name {
198 0     0 1 0 my $name = $_[0]->{sub_name};
199 0 0       0 return '' unless $name =~ /::/;
200 0         0 $name =~ s/::[^:]+$//;
201 0         0 return $name;
202             }
203              
204             =pod
205              
206             =head2 short_name
207              
208             # Prints "name"
209             before {
210             print $_->short_name . "\n";
211             } call 'Just::Package::name';
212              
213             The C parameter is a convenience wrapper around the C
214             method. Where C will return the fully resolved function name, the
215             C method will return just the name of the function.
216              
217             =cut
218              
219             sub short_name {
220 0     0 1 0 my $name = $_[0]->{sub_name};
221 0 0       0 return $name unless $name =~ /::/;
222 0         0 $name =~ /::([^:]+)$/;
223 0         0 return $1;
224             }
225              
226             # Back compatibility
227             BEGIN {
228 21     21   9546 *short_sub_name = *short_name;
229             }
230              
231             =pod
232              
233             # Add a parameter to the function call
234             $_->args( $_->args, 'more' );
235              
236             The C method allows you to get or set the list of parameters to a
237             function. It is the method equivalent of manipulating the C<@_> array.
238              
239             It uses a slightly unusual calling convention based on list context, but does
240             so in a way that allows your advice code to read very naturally.
241              
242             To summarise the situation, the three uses of the C method are listed
243             below, along with their C<@_> equivalents.
244              
245             # Get the parameters as a list
246             my @list = $_->args; # my $list = @_;
247            
248             # Get the number of parameters
249             my $count = $_->args; # my $count = @_;
250            
251             # Set the parameters
252             $_->args( 1, 2, 3 ); # @_ = ( 1, 2, 3 );
253              
254             As you can see from the above example, when C is called in list context
255             it returns the list of parameters. When it is called in scalar context, it
256             returns the number of parameters. And when it is called in void context, it
257             sets the parameters to the passed values.
258              
259             Although this is somewhat unconventional, it does allow the most common existing
260             uses of the older C method to be changed directly to the new C
261             method (such as the first example above).
262              
263             And unlike the original, you can legally call C in such a way as to set
264             the function parameters to be an empty list (which you could not do with the
265             older C method).
266              
267             # Set the function parameters to a null list
268             $_->args();
269              
270             =cut
271              
272             sub args {
273 69 100   69 0 270 if ( defined CORE::wantarray ) {
274 57         51 return @{$_[0]->{args}};
  57         614  
275             } else {
276 12         36 @{$_[0]->{args}} = @_[1..$#_];
  12         184  
277             }
278             }
279              
280             =pod
281              
282             =head2 self
283              
284             after_returning {
285             $_->self->save;
286             } My::Foo::set;
287              
288             The C method is a convenience provided for when you are writing advice
289             that will be working with object-oriented Perl code. It returns the first the
290             first parameter to the method (which should be object), which you can then call
291             methods on.
292              
293             The result is advice code that is much more natural to read, as you can see in
294             the above example where we implement an auto-save feature on the class
295             C, writing the contents to disk every time a value is set without
296             error.
297              
298             At present the C method is implemented fairly naively, if used outside
299             of object-oriented code it will still return something (including C in
300             the case where there were no parameters to the join point function).
301              
302             =cut
303              
304             sub self {
305 17     17 1 86 $_[0]->{args}->[0];
306             }
307              
308             =pod
309              
310             =head2 wantarray
311              
312             # Return differently depending on the calling context
313             if ( $_->wantarray ) {
314             $_->return_value(5);
315             } else {
316             $_->return_value(1, 2, 3, 4, 5);
317             }
318              
319             The C method returns the L context of the
320             call to the function for the current join point.
321              
322             As with the core Perl C function, returns true if the function is
323             being called in list context, false if the function is being called in scalar
324             context, or C if the function is being called in void context.
325              
326             B
327              
328             Prior to L 0.98 the wantarray context of the call to the join point
329             was available not only via the C method, but the advice code itself
330             was called in matching wantarray context to the function call, allowing you to
331             use plain C in the advice code as well.
332              
333             As all the other information about the join point was available through methods,
334             having this one piece of metadata available different was becoming an oddity.
335              
336             The C context of the join point is now B available by the
337             C method.
338              
339             =cut
340              
341             sub wantarray {
342             $_[0]->{wantarray};
343             }
344              
345             =pod
346              
347             =head2 return_value
348              
349             # Add an extra value to the returned list
350             $_->return_value( $_->return_value, 'thing' );
351              
352             The C method is used to get or set the return value for the
353             join point function, in a similar way to the normal Perl C keyword.
354              
355             As with the C method, the C method is sensitive to the
356             context in which it is called.
357              
358             When called in list context, the C method returns the join point
359             return value as a list. If the join point is called in scalar context, this will
360             be a single-element list containing the scalar return value. If the join point
361             is called in void context, this will be a null list.
362              
363             When called in scalar context, the C method returns the join
364             point return value as a scalar. If the join point is called in list context,
365             this will be the number of vales in the return list. If the join point is called
366             in void context, this will be C
367              
368             When called in void context, the C method sets the return value
369             for the join point using semantics identical to the C keyword.
370              
371             Because of this change in behavior based on the context in which C
372             is called, you should generally always set C in it's own statement
373             to prevent accidentally calling it in non-void context.
374              
375             # Return null (equivalent to "return;")
376             $_->return_value;
377              
378             In advice types that can be triggered by an exception, or need to determine
379             whether to continue to the join point function, setting a return value via
380             C is seen as implicitly indicating that any exception should be
381             suppressed, or that we do B want to continue to the join point function.
382              
383             When you call the C method this does NOT trigger an immediate
384             C equivalent in the advice code, the lines after C will
385             continue to be executed as normal (to provide an opportunity for cleanup
386             operations to be done and so on).
387              
388             If you use C inside an if/else structure you will still need to
389             do an explicit C if you wish to break out of the advice code.
390              
391             Thus, if you wish to break out of the advice code as well as return with an
392             alternative value, you should do the following.
393              
394             return $_->return_value('value');
395              
396             This usage of C appears to be contrary to the above instruction
397             that setting the return value should always be done on a standalone line to
398             guarentee void context.
399              
400             However, in Perl the context of the current function is inherited by a function
401             called with return in the manner shown above. Thus the usage of C
402             in this way alone is guarenteed to also set the return value rather than fetch
403             it.
404              
405             =cut
406              
407             sub return_value {
408 78     78 1 821 my $self = shift;
409              
410             # Handle usage in getter form
411 78 100       195 if ( defined CORE::wantarray() ) {
412             # Let the inherent magic of Perl do the work between the
413             # list and scalar context calls to return_value
414 10 50       44 if ( $self->{wantarray} ) {
    50          
415 0         0 return @{$self->{return_value}};
  0         0  
416             } elsif ( defined $self->{wantarray} ) {
417 10         73 return $self->{return_value};
418             } else {
419 0         0 return;
420             }
421             }
422              
423             # Having provided a return value, suppress any exceptions
424             # and don't proceed if applicable.
425 68         152 $self->{exception} = '';
426 68         136 $self->{proceed} = 0;
427 68 100       253 if ( $self->{wantarray} ) {
    50          
428 4         7 @{$self->{return_value}} = @_;
  4         80  
429             } elsif ( defined $self->{wantarray} ) {
430 64         1125 $self->{return_value} = pop;
431             }
432             }
433              
434             # Accelerate the recommended cflow key
435             sub enclosing {
436             $_[0]->{enclosing};
437             }
438              
439             sub AUTOLOAD {
440 19     19   1019 my $self = shift;
441 19         27 my $key = our $AUTOLOAD;
442 19         87 $key =~ s/^.*:://;
443 19 100       227 Carp::croak "Key does not exist: [$key]" unless exists $self->{$key};
444 11         66 return $self->{$key};
445             }
446              
447             # Improves performance by not having to send DESTROY calls
448             # through AUTOLOAD, and not having to check for DESTROY in AUTOLOAD.
449       0     sub DESTROY () { }
450              
451              
452              
453              
454              
455             #######################################################################
456             # Back Compatibility
457              
458             sub params_ref {
459             $_[0]->{args};
460             }
461              
462             sub params {
463 0 0   0 0   $_[0]->{args} = [ @_[1..$#_] ] if @_ > 1;
464             return CORE::wantarray
465 0           ? @{$_[0]->{args}}
466 0 0         : $_[0]->{args};
467             }
468              
469              
470              
471              
472              
473             ######################################################################
474             # Optional XS Acceleration
475              
476             BEGIN {
477 21     21   45 local $@;
478 21     21   1700 eval <<'END_PERL';
  21         106  
  21         516  
  21         207  
479             use Class::XSAccessor 1.08 {
480             replace => 1,
481             getters => {
482             'pointcut' => 'pointcut',
483             'sub_name' => 'sub_name',
484             'wantarray' => 'wantarray',
485             'params_ref' => 'args',
486             'enclosing' => 'enclosing',
487             },
488             };
489             END_PERL
490             }
491              
492             1;
493              
494             =pod
495              
496             =head1 AUTHORS
497              
498             Adam Kennedy Eadamk@cpan.orgE
499              
500             Marcel GrEnauer Emarcel@cpan.orgE
501              
502             Ran Eilam Eeilara@cpan.orgE
503              
504             =head1 COPYRIGHT
505              
506             Copyright 2001 by Marcel GrEnauer
507              
508             Some parts copyright 2009 - 2011 Adam Kennedy.
509              
510             This library is free software; you can redistribute it and/or modify
511             it under the same terms as Perl itself.
512              
513             =cut