File Coverage

blib/lib/MooX/CaptainHook.pm
Criterion Covered Total %
statement 76 91 83.5
branch 17 30 56.6
condition 7 20 35.0
subroutine 20 22 90.9
pod 3 3 100.0
total 123 166 74.1


line stmt bran cond sub pod time code
1             package MooX::CaptainHook;
2              
3 9     9   181402 use 5.008;
  9         29  
  9         372  
4 9     9   44 use strict;
  9         15  
  9         318  
5 9     9   93 use warnings;
  9         14  
  9         293  
6              
7 9     9   4632 use Exporter::Shiny qw/ on_application on_inflation is_role /;
  9         36408  
  9         62  
8              
9             BEGIN {
10 9     9   642 no warnings 'once';
  9         16  
  9         465  
11 9     9   19 $MooX::CaptainHook::AUTHORITY = 'cpan:TOBYINK';
12 9         2217 $MooX::CaptainHook::VERSION = '0.011';
13             }
14              
15             our %on_application;
16             our %on_inflation;
17              
18             sub is_role
19             {
20 20     20 1 35 my $package = shift;
21 20         103 require Role::Tiny;
22 20 100       70 return !!1 if exists $Role::Tiny::INFO{$package};
23 14 50       86 return !!0 if exists $Moo::MAKERS{$package};
24 0 0 0     0 if ($INC{'Class/MOP.pm'} and my $classof = 'Class::MOP'->can('class_of')) {
25 0         0 my $meta = $classof->($package);
26 0 0 0     0 return !!1 if $meta && $meta->isa('Moose::Meta::Role');
27             }
28 0         0 return !!0;
29             }
30              
31             {
32             my %already;
33             sub _fire
34             {
35 8     8   19 my (undef, $callbacks, $key, $args) = @_;
36 8 50 33     65 return if defined $key && $already{$key}++;
37 8 100       19 return unless $callbacks;
38 7         14 for my $cb (@$callbacks)
39             {
40 7         36 $cb->($args) for $args->[0]; # local $_ aliasing
41             }
42             }
43             }
44              
45 9         13 use constant ON_APPLICATION => do {
46             package MooX::CaptainHook::OnApplication;
47             BEGIN {
48 9     9   55 no warnings 'once';
  9         27  
  9         421  
49 9     9   22 $MooX::CaptainHook::OnApplication::AUTHORITY = 'cpan:TOBYINK';
50 9         207 $MooX::CaptainHook::OnApplication::VERSION = '0.011';
51             }
52 9     9   545 use Moo::Role;
  9         6058  
  9         71  
53             after apply_roles_to_package => sub
54             {
55 7         44662 my ($toolage, $package, @roles) = @_;
56            
57 7         16 for my $role (@roles)
58             {
59 8         64 'MooX::CaptainHook'->_fire(
60             $on_application{$role},
61             "OnApplication: $package $role",
62             [ $package, $role ],
63             );
64            
65             # This stuff is for internals...
66 8 50 50     45 push @{ $on_application{$package} ||= [] }, @{ $on_application{$role} || [] }
  1 100       12  
  1         3  
67             if MooX::CaptainHook::is_role($package);
68 8 100 100     12 push @{ $on_inflation{$package} ||= [] }, @{ $on_inflation{$role} || [] };
  8         37  
  8         186  
69             }
70 9         66 };
71 9         21704 __PACKAGE__;
72 9     9   4659 };
  9         16  
73              
74             # This sub makes sure that when a role which has an on_application hook
75             # gets inflated to a full Moose role (as will happen if the role is
76             # consumed by a Moose class!) then the generated metarole object will
77             # have a trait that still triggers the on_application hook.
78             #
79             # There are probably numerous edge cases not catered for, but my simple
80             # tests seem to work.
81             #
82             sub _inflated
83             {
84 0     0   0 my $args = shift;
85 0         0 my $meta = $args->[0];
86 0 0       0 return unless $meta->isa('Moose::Meta::Role');
87 0         0 require Moose::Util::MetaRole;
88 0         0 $args->[0] = $meta = Moose::Util::MetaRole::apply_metaroles(
89             for => $meta->name,
90             role_metaroles => {
91             role => eval q{
92             package MooX::CaptainHook::OnApplication::Moose;
93             BEGIN {
94             no warnings 'once';
95             $MooX::CaptainHook::OnApplication::Moose::AUTHORITY = 'cpan:TOBYINK';
96             $MooX::CaptainHook::OnApplication::Moose::VERSION = '0.011';
97             }
98             use Moose::Role;
99             after apply => sub {
100             my $role = $_[0]->name;
101             my $package = $_[1]->name;
102            
103             'MooX::CaptainHook'->_fire(
104             $on_application{$role},
105             "OnApplication: $package $role",
106             [ $package, $role ],
107             );
108            
109             # This stuff is for internals...
110             if (MooX::CaptainHook::is_role($_[1]->name)) {
111             push @{ $on_application{$package} ||= [] }, @{ $on_application{$role} || [] };
112             Moose::Util::MetaRole::apply_metaroles(
113             for => $package,
114             role_metaroles => {
115             role => [__PACKAGE__],
116             },
117             );
118             }
119             };
120             [__PACKAGE__];
121             },
122             },
123             );
124             }
125              
126             sub on_application (&;$)
127             {
128 7     7 1 73 my ($code, $role) = @_;
129 7 100       34 $role = caller unless defined $role;
130 7   50     10 push @{$on_application{$role}||=[]}, $code;
  7         72  
131            
132 7 100       42 'Moo::Role'->apply_single_role_to_package('Moo::Role', ON_APPLICATION)
133             unless Role::Tiny::does_role('Moo::Role', ON_APPLICATION);
134            
135 7         13086 return;
136             }
137              
138 9         13 use constant ON_INFLATION => do {
139             package MooX::CaptainHook::OnInflation;
140             BEGIN {
141 9     9   65 no warnings 'once';
  9         17  
  9         392  
142 9     9   18 $MooX::CaptainHook::OnInflation::AUTHORITY = 'cpan:TOBYINK';
143 9         187 $MooX::CaptainHook::OnInflation::VERSION = '0.011';
144             }
145 9     9   42 use Moo::Role;
  9         12  
  9         41  
146             around inject_real_metaclass_for => sub
147             {
148 0         0 my ($orig, $pkg) = @_;
149 0         0 my $args = [ scalar $orig->($pkg) ];
150 0 0       0 'MooX::CaptainHook'->_fire(
151             [
152             'MooX::CaptainHook'->can('_inflated'),
153 0         0 @{$on_inflation{$pkg}||[]}
154             ],
155             undef,
156             $args,
157             );
158 0         0 return $args->[0];
159 9         60 };
160 9         1450 __PACKAGE__;
161 9     9   3361 };
  9         14  
162              
163             sub on_inflation (&;$)
164             {
165 14     14 1 58 my ($code, $pkg) = @_;
166 14 100       47 $pkg = caller unless defined $pkg;
167 14   100     20 push @{$on_inflation{$pkg}||=[]}, $_[0];
  14         94  
168              
169 14         6128 return;
170             }
171              
172             {
173             package MooX::CaptainHook::HandleMoose::Hack;
174             our $AUTHORITY = 'cpan:TOBYINK';
175             our $VERSION = '0.011';
176 9     9   10979 use overload qw[bool] => sub { 0 };
  9     9   8842  
  9         93  
  9         39  
177             sub DESTROY {
178 0 0 0 0     'Moo::Role'->apply_single_role_to_package('Moo::HandleMoose', MooX::CaptainHook::ON_INFLATION)
179             if Moo::HandleMoose->can('inject_real_metaclass_for')
180             && !Role::Tiny::does_role('Moo::HandleMoose', MooX::CaptainHook::ON_INFLATION);
181             }
182             if ($Moo::HandleMoose::SETUP_DONE)
183             { __PACKAGE__->DESTROY }
184             else
185             { $Moo::HandleMoose::SETUP_DONE ||= bless [] }
186             }
187              
188             1;
189              
190             __END__