File Coverage

blib/lib/Class/C3/Componentised/ApplyHooks.pm
Criterion Covered Total %
statement 62 62 100.0
branch 13 14 92.8
condition n/a
subroutine 7 7 100.0
pod 2 2 100.0
total 84 85 98.8


line stmt bran cond sub pod time code
1             package Class::C3::Componentised::ApplyHooks;
2              
3 2     2   157919 use strict;
  2         17  
  2         45  
4 2     2   8 use warnings;
  2         3  
  2         587  
5              
6             our %Before;
7             our %After;
8              
9             sub BEFORE_APPLY (&) {
10 6     6 1 75   push @{$Before{scalar caller}}, $_[0];
  6         21  
11 6         77   $Class::C3::Componentised::APPLICATOR_FOR{scalar caller} = __PACKAGE__;
12             }
13             sub AFTER_APPLY (&) {
14 4     4 1 40   push @{$After {scalar caller}}, $_[0];
  4         8  
15 4         64   $Class::C3::Componentised::APPLICATOR_FOR{scalar caller} = __PACKAGE__;
16             }
17              
18             sub _apply_component_to_class {
19 4     4   9   my ($me, $comp, $target, $apply) = @_;
20 4         6   my @heritage = @{mro::get_linear_isa($comp)};
  4         11  
21              
22               my @before = map {
23 4         6      my $to_run = $Before{$_};
  7         18  
24 7 100       21      ($to_run?[$_,$to_run]:())
25               } @heritage;
26              
27 4         6   for my $todo (@before) {
28 6         17      my ($parent, $fn) = @$todo;
29 6         9      for my $f (reverse @$fn) {
30 11         39         $target->$f($parent)
31                  }
32               }
33              
34 4         31   $apply->();
35              
36               my @after = map {
37 4         14      my $to_run = $After{$_};
  7         10  
38 7 100       18      ($to_run?[$_,$to_run]:())
39               } @heritage;
40              
41 4         10   for my $todo (reverse @after) {
42 5         15      my ($parent, $fn) = @$todo;
43 5         8      for my $f (@$fn) {
44 9         29         $target->$f($parent)
45                  }
46               }
47             }
48              
49             {
50 2     2   12    no strict 'refs';
  2         4  
  2         492  
51                sub import {
52 5     5   618       my ($from, @args) = @_;
53 5         12       my $to = caller;
54              
55 5         70       my $default = 1;
56 5         8       my $i = 0;
57 5         6       my $skip = 0;
58 5         6       my @import;
59 5         10       for my $arg (@args) {
60 8 100       18          if ($skip) {
61 3         3             $skip--;
62 3         4             $i++;
63                         next
64 3         5          }
65              
66 5 100       18          if ($arg eq '-before_apply') {
    100          
    50          
67 2         3             $default = 0;
68 2         2             $skip = 1;
69 2         9             push @{$Before{$to}}, $args[$i + 1];
  2         5  
70 2         4             $Class::C3::Componentised::APPLICATOR_FOR{$to} = $from;
71                      } elsif ($arg eq '-after_apply') {
72 1         6             $default = 0;
73 1         3             $skip = 1;
74 1         1             push @{$After{$to}}, $args[$i + 1];
  1         4  
75 1         2             $Class::C3::Componentised::APPLICATOR_FOR{$to} = $from;
76                      } elsif ($arg =~ /^BEFORE_APPLY|AFTER_APPLY$/) {
77 2         2             $default = 0;
78 2         4             push @import, $arg
79                      }
80 5         7          $i++;
81                   }
82 5 100       14       @import = qw(BEFORE_APPLY AFTER_APPLY)
83                      if $default;
84              
85 5         19       *{"$to\::$_"} = \&{"$from\::$_"} for @import
  8         312  
  8         23  
86                }
87             }
88              
89             1;
90              
91             =head1 NAME
92            
93             Class::C3::Componentised::ApplyHooks - Run methods before or after components are injected
94            
95             =head1 SYNOPSIS
96            
97             package MyComponent;
98            
99             our %statistics;
100            
101             use Class::C3::Componentised::ApplyHooks
102             -before_apply => sub {
103             my ($class, $component) = @_;
104            
105             push @{$statistics{$class}}, '-before_apply';
106             },
107             -after_apply => sub {
108             my ($class, $component) = @_;
109            
110             push @{$statistics{$class}}, '-after_apply';
111             }, qw(BEFORE_APPLY AFTER_APPLY);
112            
113             BEFORE_APPLY { push @{$statistics{$class}}, 'BEFORE_APPLY' };
114             AFTER_APPLY { push @{$statistics{$class}}, 'AFTER_APPLY' };
115             AFTER_APPLY { use Devel::Dwarn; Dwarn %statistics };
116            
117             1;
118            
119             =head1 DESCRIPTION
120            
121             This package allows a given component to run methods on the class that is being
122             injected into before or after the component is injected. Note from the
123             L</SYNOPSIS> that all C<Load Actions> may be run more than once.
124            
125             =head1 IMPORT ACTION
126            
127             Both import actions simply run a list of coderefs that will be passed the class
128             that is being acted upon and the component that is being added to the class.
129            
130             =head1 IMPORT OPTIONS
131            
132             =head2 -before_apply
133            
134             Adds a before apply action for the current component without importing
135             any subroutines into your namespace.
136            
137             =head2 -after_apply
138            
139             Adds an after apply action for the current component without importing
140             any subroutines into your namespace.
141            
142             =head1 EXPORTED SUBROUTINES
143            
144             =head2 BEFORE_APPLY
145            
146             BEFORE_APPLY { warn "about to apply $_[1] to class $_[0]" };
147            
148             Adds a before apply action for the current component.
149            
150             =head2 AFTER_APPLY
151            
152             AFTER_APPLY { warn "just applied $_[1] to class $_[0]" };
153            
154             Adds an after apply action for the current component.
155            
156             =cut
157