File Coverage

lib/Aspect/Library/Wormhole.pm
Criterion Covered Total %
statement 19 19 100.0
branch n/a
condition n/a
subroutine 7 7 100.0
pod 0 1 0.0
total 26 27 96.3


line stmt bran cond sub pod time code
1             package Aspect::Library::Wormhole;
2              
3 1     1   1838 use strict;
  1         3  
  1         45  
4 1     1   6 use warnings;
  1         2  
  1         39  
5 1     1   7 use Carp;
  1         2  
  1         91  
6 1     1   8 use Aspect;
  1         2  
  1         85  
7              
8 1     1   6 use base 'Aspect::Modular';
  1         2  
  1         257  
9              
10             sub get_advice {
11 1     1 0 4 my ($self, $source, $target) = @_;
12 1     1   2 before { my $c = shift; $c->append_param($c->source->self) }
  1         10  
13 1         11 call $target & cflow source => $source;
14             }
15              
16             1;
17              
18             =head1 NAME
19              
20             Aspect::Library::Wormhole - A wormhole between call frames
21              
22             =head1 SYNOPSIS
23              
24             package A;
25             sub new { bless {}, shift }
26             sub a { B->new->b }
27              
28             package B;
29             sub new { bless {}, shift }
30             sub b { C->new->c }
31              
32             package C;
33             sub new { bless {}, shift }
34             sub c { ref pop }
35              
36             package main;
37              
38             print ref A->new->a; # without aspect, prints C
39              
40             use Aspect::Library::Wormhole;
41             aspect Wormhole => 'A::a', 'C::c';
42             print ref A->new->a; # with aspect, prints A
43              
44             =head1 SUPER
45              
46             L
47              
48             =head1 DESCRIPTION
49              
50             A reusable aspect for passing objects down a call flow, without adding
51             extra arguments to the frames between the source and the target. It is a
52             tool for acquiring implicit context.
53              
54             Suppose C calls C calls C... until C.
55              
56             All is well, until one day you get a requirement with a crosscutting
57             implication- C requires one extra argument. It requires an
58             instance of the class C. The very same instance on which the method
59             C was called, high up the call chain of C.
60              
61             Without this aspect you can either add a global C<$Current_A> (very
62             problematic), or make C send C its C<$self>, make
63             C pass it on to C, and so on until C. You are
64             forced to add many arguments to many methods.
65              
66             Show me a developer who has never encountered this situation: you need
67             to add an argument to a long call flow, just because someone at the
68             bottom needs it, yet only someone on the top has it. The monkey code
69             required I in the call flow, I
70             that I requires, is suffering from B- Extraneous
71             Embedded Knowledge (L).
72              
73             The code for the frames between the two ends of the wormhole, knows more
74             about the world than it should. This extraneous knowledge is embedded in
75             each method on the call flow, and there is no easy way to remove it.
76              
77             This aspect removes the EEK by allowing you to setup a wormhole between
78             the source and target frames in the call flow. The only effect the
79             wormhole has on the call flow, is that the target gets called with one
80             extra argument: the calling source object. Thus the target acquires
81             implicit context.
82              
83             So this wormhole:
84              
85             aspect Wormhole => 'A::a', 'Z::z';
86              
87             Means: before the method C is called, I C exists in
88             the call flow, I append one argument to the argument list of
89             C. The argument appended is the calling C object.
90              
91             No method in the call flow is required to pass the source object, but
92             C will still receive it.
93              
94             +--------+ +--------+
95             | source | +--------+ +--------+ | target |
96             +--------+--> | B::b() |--> | C::c() |--> ...--> +--------+
97             | A::a() | +--------+ +--------+ | Z::z() |
98             +--------+ +--------+
99             . ,
100             | /|\
101             | / | \
102             | |
103             +------------- The Bajoran Wormhole -------------+
104              
105             =head1 USING
106              
107             The aspect constructor takes two pointcut specs, a source and a target.
108             The spec can be a string (full sub name), a regex (sub will match if
109             rexep matches), or a coderef (called with sub name, will match if returns
110             true).
111              
112             For example, this will append a calling C to any call to a sub
113             defined on C, if it is in the call flow of C:
114              
115             aspect Wormhole => 'Printer::Print', qr/^Page::/;
116              
117             =head1 SEE ALSO
118              
119             See the L pods for a guide to the Aspect module.
120              
121             You can find an example comparing the OO and AOP solutions in the
122             C directory of the distribution.
123              
124             =cut
125