File Coverage

blib/lib/Aspect/Point.pm
Criterion Covered Total %
statement 53 66 80.3
branch 18 28 64.2
condition n/a
subroutine 12 16 75.0
pod 6 8 75.0
total 89 118 75.4


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 26     26   137 use strict;
  26         49  
  26         885  
121 26     26   132 use Carp ();
  26         46  
  26         355  
122 26     26   130 use Sub::Uplevel ();
  26         43  
  26         352  
123 26     26   134 use Aspect::Point::Static ();
  26         35  
  26         25111  
124              
125             our $VERSION = '1.04';
126              
127              
128              
129              
130              
131             ######################################################################
132             # Aspect::Point Methods
133              
134             # sub new {
135             # my $class = shift;
136             # bless { @_ }, $class;
137             # }
138              
139             =pod
140              
141             =head2 type
142              
143             The C method is a convenience provided in the situation something has a
144             L method and wants to know the advice declarator it is made for.
145              
146             Returns C<"before"> in L advice, C<"after"> in
147             L advice, or C<"around"> in
148             L advice.
149              
150             =cut
151              
152             sub type {
153             $_[0]->{type};
154             }
155              
156             =pod
157              
158             =head2 pointcut
159              
160             my $pointcut = $_->pointcut;
161              
162             The C method provides access to the original join point specification
163             (as a tree of L objects) that the current join point matched
164             against.
165              
166             Please note that the pointcut returned is the full and complete pointcut tree,
167             due to the heavy optimisation used on the actual pointcut code when it is run
168             there is no way at the time of advice execution to indicate which specific
169             conditions in the pointcut tree matched and which did not.
170              
171             Returns an object which is a sub-class of L.
172              
173             =cut
174              
175             sub pointcut {
176             $_[0]->{pointcut};
177             }
178              
179             =pod
180              
181             =head2 original
182              
183             $_->original->( 1, 2, 3 );
184              
185             In a pointcut, the C method returns a C reference to the
186             original function before it was hooked by the L weaving process.
187              
188             Calls made to the function are unprotected, parameters and calling context will
189             not be replicated into the function, return params and exception will not be
190             caught.
191              
192             =cut
193              
194             sub original {
195             $_[0]->{original};
196             }
197              
198             =pod
199              
200             =head2 sub_name
201              
202             # Prints "Full::Function::name"
203             before {
204             print $_->sub_name . "\n";
205             } call 'Full::Function::name';
206              
207             The C method returns a string with the full resolved function name
208             at the join point the advice code is running at.
209              
210             =cut
211              
212             sub sub_name {
213             $_[0]->{sub_name};
214             }
215              
216             =pod
217              
218             =head2 package_name
219              
220             # Prints "Just::Package"
221             before {
222             print $_->package_name . "\n";
223             } call 'Just::Package::name';
224              
225             The C parameter is a convenience wrapper around the C
226             method. Where C will return the fully resolved function name, the
227             C method will return just the namespace of the package of the
228             join point.
229              
230             =cut
231              
232             sub package_name {
233 0     0 1 0 my $name = $_[0]->{sub_name};
234 0 0       0 return '' unless $name =~ /::/;
235 0         0 $name =~ s/::[^:]+$//;
236 0         0 return $name;
237             }
238              
239             =pod
240              
241             =head2 short_name
242              
243             # Prints "name"
244             before {
245             print $_->short_name . "\n";
246             } call 'Just::Package::name';
247              
248             The C parameter is a convenience wrapper around the C
249             method. Where C will return the fully resolved function name, the
250             C method will return just the name of the function.
251              
252             =cut
253              
254             sub short_name {
255 0     0 1 0 my $name = $_[0]->{sub_name};
256 0 0       0 return $name unless $name =~ /::/;
257 0         0 $name =~ /::([^:]+)$/;
258 0         0 return $1;
259             }
260              
261             =pod
262              
263             =head2 args
264              
265             # Add a parameter to the function call
266             $_->args( $_->args, 'more' );
267              
268             The C method allows you to get or set the list of parameters to a
269             function. It is the method equivalent of manipulating the C<@_> array.
270              
271             It uses a slightly unusual calling convention based on list context, but does
272             so in a way that allows your advice code to read very naturally.
273              
274             To summarise the situation, the three uses of the C method are listed
275             below, along with their C<@_> equivalents.
276              
277             # Get the parameters as a list
278             my @list = $_->args; # my $list = @_;
279            
280             # Get the number of parameters
281             my $count = $_->args; # my $count = @_;
282            
283             # Set the parameters
284             $_->args( 1, 2, 3 ); # @_ = ( 1, 2, 3 );
285              
286             As you can see from the above example, when C is called in list context
287             it returns the list of parameters. When it is called in scalar context, it
288             returns the number of parameters. And when it is called in void context, it
289             sets the parameters to the passed values.
290              
291             Although this is somewhat unconventional, it does allow the most common existing
292             uses of the older C method to be changed directly to the new C
293             method (such as the first example above).
294              
295             And unlike the original, you can legally call C in such a way as to set
296             the function parameters to be an empty list (which you could not do with the
297             older C method).
298              
299             # Set the function parameters to a null list
300             $_->args();
301              
302             =cut
303              
304             sub args {
305 94 100   94 1 419 if ( defined CORE::wantarray ) {
306 76         86 return @{$_[0]->{args}};
  76         1276  
307             } else {
308 18         60 @{$_[0]->{args}} = @_[1..$#_];
  18         375  
309             }
310             }
311              
312             =pod
313              
314             =head2 self
315              
316             after {
317             $_->self->save;
318             } My::Foo::set;
319              
320             The C method is a convenience provided for when you are writing advice
321             that will be working with object-oriented Perl code. It returns the first
322             parameter to the method (which should be object), which you can then call
323             methods on.
324              
325             The result is advice code that is much more natural to read, as you can see in
326             the above example where we implement an auto-save feature on the class
327             C, writing the contents to disk every time a value is set without
328             error.
329              
330             At present the C method is implemented fairly naively, if used outside
331             of object-oriented code it will still return something (including C in
332             the case where there were no parameters to the join point function).
333              
334             =cut
335              
336             sub self {
337 20     20 1 1066 $_[0]->{args}->[0];
338             }
339              
340             =pod
341              
342             =head2 wantarray
343              
344             # Return differently depending on the calling context
345             if ( $_->wantarray ) {
346             $_->return_value(5);
347             } else {
348             $_->return_value(1, 2, 3, 4, 5);
349             }
350              
351             The C method returns the L context of the
352             call to the function for the current join point.
353              
354             As with the core Perl C function, returns true if the function is
355             being called in list context, false if the function is being called in scalar
356             context, or C if the function is being called in void context.
357              
358             B
359              
360             Prior to L 0.98 the wantarray context of the call to the join point
361             was available not only via the C method, but the advice code itself
362             was called in matching wantarray context to the function call, allowing you to
363             use plain C in the advice code as well.
364              
365             As all the other information about the join point was available through methods,
366             having this one piece of metadata available different was becoming an oddity.
367              
368             The C context of the join point is now B available by the
369             C method.
370              
371             =cut
372              
373             sub wantarray {
374             $_[0]->{wantarray};
375             }
376              
377             =pod
378              
379             =head2 exception
380              
381             unless ( $_->exception ) {
382             $_->exception('Kaboom');
383             }
384              
385             The C method is used to get the current die message or exception
386             object, or to set the die message or exception object.
387              
388             =cut
389              
390             sub exception {
391 33 50   33 1 211 unless ( $_[0]->{type} eq 'after' ) {
392 0         0 Carp::croak("Cannot call exception in $_[0]->{exception} advice");
393             }
394 33 100       190 return $_[0]->{exception} if defined CORE::wantarray();
395 29         735 $_[0]->{exception} = $_[1];
396             }
397              
398             =pod
399              
400             =head2 return_value
401              
402             # Add an extra value to the returned list
403             $_->return_value( $_->return_value, 'thing' );
404              
405             The C method is used to get or set the return value for the
406             join point function, in a similar way to the normal Perl C keyword.
407              
408             As with the C method, the C method is sensitive to the
409             context in which it is called.
410              
411             When called in list context, the C method returns the join point
412             return value as a list. If the join point is called in scalar context, this will
413             be a single-element list containing the scalar return value. If the join point
414             is called in void context, this will be a null list.
415              
416             When called in scalar context, the C method returns the join
417             point return value as a scalar. If the join point is called in list context,
418             this will be the number of vales in the return list. If the join point is called
419             in void context, this will be C
420              
421             When called in void context, the C method sets the return value
422             for the join point using semantics identical to the C keyword.
423              
424             Because of this change in behavior based on the context in which C
425             is called, you should generally always set C in it's own statement
426             to prevent accidentally calling it in non-void context.
427              
428             # Return null (equivalent to "return;")
429             $_->return_value;
430              
431             In advice types that can be triggered by an exception, or need to determine
432             whether to continue to the join point function, setting a return value via
433             C is seen as implicitly indicating that any exception should be
434             suppressed, or that we do B want to continue to the join point function.
435              
436             When you call the C method this does NOT trigger an immediate
437             C equivalent in the advice code, the lines after C will
438             continue to be executed as normal (to provide an opportunity for cleanup
439             operations to be done and so on).
440              
441             If you use C inside an if/else structure you will still need to
442             do an explicit C if you wish to break out of the advice code.
443              
444             Thus, if you wish to break out of the advice code as well as return with an
445             alternative value, you should do the following.
446              
447             return $_->return_value('value');
448              
449             This usage of C appears to be contrary to the above instruction
450             that setting the return value should always be done on a standalone line to
451             guarentee void context.
452              
453             However, in Perl the context of the current function is inherited by a function
454             called with return in the manner shown above. Thus the usage of C
455             in this way alone is guarenteed to also set the return value rather than fetch
456             it.
457              
458             =cut
459              
460             sub return_value {
461 100     100 1 1733 my $self = shift;
462 100         222 my $want = $self->{wantarray};
463              
464             # Handle usage in getter form
465 100 100       303 if ( defined CORE::wantarray() ) {
466             # Let the inherent magic of Perl do the work between the
467             # list and scalar context calls to return_value
468 10 0       32 return @{$self->{return_value} || []} if $want;
  0 50       0  
469 10 50       101 return $self->{return_value} if defined $want;
470 0         0 return;
471             }
472              
473             # We've been provided a return value
474 90         187 $self->{exception} = '';
475 90 100       1662 $self->{return_value} = $want ? [ @_ ] : pop;
476             }
477              
478             sub proceed {
479 45     45 0 1632 my $self = shift;
480              
481 45 100       171 unless ( $self->{type} eq 'around' ) {
482 8         179 Carp::croak("Cannot call proceed in $self->{type} advice");
483             }
484              
485 37         46 local $_ = ${$self->{topic}};
  37         94  
486              
487 37 100       142 if ( $self->{wantarray} ) {
    100          
488 3         11 $self->return_value(
489             Sub::Uplevel::uplevel(
490             2,
491             $self->{original},
492 3         9 @{$self->{args}},
493             )
494             );
495              
496             } elsif ( defined $self->{wantarray} ) {
497 26         121 $self->return_value(
498             scalar Sub::Uplevel::uplevel(
499             2,
500             $self->{original},
501 26         53 @{$self->{args}},
502             )
503             );
504              
505             } else {
506 8         31 Sub::Uplevel::uplevel(
507             2,
508             $self->{original},
509 8         24 @{$self->{args}},
510             );
511             }
512              
513 37         1207 ${$self->{topic}} = $_;
  37         91  
514              
515 37         889 return;
516             }
517              
518             sub enclosing {
519             $_[0]->{enclosing};
520             }
521              
522             sub topic {
523 0     0 0 0 Carp::croak("The join point method topic in reserved");
524             }
525              
526             sub AUTOLOAD {
527 12     12   1274 my $self = shift;
528 12         27 my $key = our $AUTOLOAD;
529 12         74 $key =~ s/^.*:://;
530 12 50       55 Carp::croak "Key does not exist: [$key]" unless exists $self->{$key};
531 12         101 return $self->{$key};
532             }
533              
534             # Improves performance by not having to send DESTROY calls
535             # through AUTOLOAD, and not having to check for DESTROY in AUTOLOAD.
536 0     0     sub DESTROY () { }
537              
538              
539              
540              
541              
542             ######################################################################
543             # Optional XS Acceleration
544              
545             BEGIN {
546 26     26   70 local $@;
547 26     26   3428 eval <<'END_PERL';
  26         174  
  26         787  
  26         334  
548             use Class::XSAccessor 1.08 {
549             replace => 1,
550             getters => {
551             'type' => 'type',
552             'pointcut' => 'pointcut',
553             'original' => 'original',
554             'sub_name' => 'sub_name',
555             'wantarray' => 'wantarray',
556             'enclosing' => 'enclosing',
557             },
558             };
559             END_PERL
560             }
561              
562             1;
563              
564             =pod
565              
566             =head1 AUTHORS
567              
568             Adam Kennedy Eadamk@cpan.orgE
569              
570             Marcel GrEnauer Emarcel@cpan.orgE
571              
572             Ran Eilam Eeilara@cpan.orgE
573              
574             =head1 COPYRIGHT
575              
576             Copyright 2001 by Marcel GrEnauer
577              
578             Some parts copyright 2009 - 2013 Adam Kennedy.
579              
580             This library is free software; you can redistribute it and/or modify
581             it under the same terms as Perl itself.
582              
583             =cut