File Coverage

blib/lib/MooX/PluginKit/Core.pm
Criterion Covered Total %
statement 111 116 95.6
branch 33 48 68.7
condition 6 7 85.7
subroutine 27 28 96.4
pod 0 17 0.0
total 177 216 81.9


line stmt bran cond sub pod time code
1             package MooX::PluginKit::Core;
2 5     5   426155 use 5.008001;
  5         27  
3 5     5   28 use strictures 2;
  5         31  
  5         208  
4             our $VERSION = '0.06';
5              
6             =head1 NAME
7              
8             MooX::PluginKit::Core - The PluginKit internal guts.
9              
10             =head2 DESCRIPTION
11              
12             This module tracks metadata about consumers and plugins as well as
13             providing much of the underlying logic behind the other PluginKit
14             modules.
15              
16             Currently this module is not documented because it is not intended
17             to be used directly. This may change.
18              
19             =cut
20              
21 5     5   980 use Carp qw( croak );
  5         10  
  5         259  
22 5     5   2095 use Moo::Role qw();
  5         56165  
  5         141  
23 5     5   36 use Module::Runtime qw( require_module is_module_name );
  5         11  
  5         40  
24              
25             require UNIVERSAL::DOES
26             unless defined &UNIVERSAL::DOES;
27              
28 5     5   2654 use namespace::clean;
  5         52886  
  5         42  
29              
30 5     5   1264 use Exporter qw( import );
  5         12  
  5         6792  
31              
32             our @EXPORT = qw(
33             init_plugin
34             is_plugin
35             resolve_plugin
36             does_plugin_apply
37             find_applicable_plugins
38             build_class_with_plugins
39             set_plugin_applies_to
40             get_plugin_applies_to
41             set_plugin_includes
42             get_plugin_includes
43             init_consumer
44             is_consumer
45             get_consumer_moo_extends
46             get_consumer_moo_with
47             get_consumer_moo_has
48             set_consumer_namespace
49             get_consumer_namespace
50             );
51              
52             my %plugins; # Metadata about roles.
53             my %consumers; # Metadata about classes.
54              
55             sub init_plugin {
56 16     16 0 41 my ($plugin_name) = @_;
57 16         35 $plugins{$plugin_name} = {};
58 16         36 return;
59             }
60              
61             sub is_plugin {
62 0     0 0 0 my ($plugin_name) = @_;
63 0 0       0 return $plugins{$plugin_name} ? 1 : 0;
64             }
65              
66             sub resolve_plugin {
67 22     22 0 43 my ($plugin_name, $namespace) = @_;
68 22         47 local $Carp::Internal{ (__PACKAGE__) } = 1;
69              
70 22 50       59 croak "An undefined plugin name cannot be resolved"
71             if !defined $plugin_name;
72              
73 22 100       63 if ($plugin_name =~ m{^::}) {
74 8 50       19 croak "The relative plugin $plugin_name cannot be made absolute without a namespace"
75             if !defined $namespace;
76              
77 8         23 $plugin_name = $namespace . $plugin_name;
78             }
79              
80 22 50       65 croak "The plugin $plugin_name does not appear to be a valid module name"
81             if !is_module_name( $plugin_name );
82              
83 22 100       298 return $plugin_name if exists $plugins{$plugin_name};
84              
85             # Go ahead and shortcircuit here as Moo::Role does not add inlined packages into
86             # %INC so the require_module() call could fail in some cases if the module has already
87             # been setup but isn't on the filesystem in the expected locations.
88 14 50       42 return $plugin_name if Moo::Role->is_role( $plugin_name );
89              
90 0         0 require_module( $plugin_name );
91              
92 0 0       0 croak "Plugin $plugin_name does not appear to be a Moo::Role"
93             if !Moo::Role->is_role( $plugin_name );
94              
95 0         0 return $plugin_name;
96             }
97              
98             sub does_plugin_apply {
99 73     73 0 12246 my ($plugin_name, $class) = @_;
100              
101 73         145 my $sub = get_plugin_applies_to( $plugin_name );
102              
103 73 100       164 return $sub->( $class ) ? 1 : 0;
104             }
105              
106             sub find_applicable_plugins {
107 39     39 0 75 my ($class, @plugins) = @_;
108              
109 39         54 my @final_plugins;
110 39         105 while (@plugins) {
111 45         116 my $plugin = shift( @plugins );
112 45 100       94 next if !does_plugin_apply( $plugin, $class );
113 38         74 push @final_plugins, $plugin;
114 38         55 unshift @plugins, @{ get_plugin_includes( $plugin ) };
  38         63  
115             }
116              
117 39         158 return \@final_plugins;
118             }
119              
120             sub build_class_with_plugins {
121 39     39 0 41833 my ($base_class, @plugins) = @_;
122              
123 39         97 my $roles = find_applicable_plugins( $base_class, @plugins );
124 39 100       120 return $base_class if !@$roles;
125              
126 19         77 return Moo::Role->create_class_with_roles(
127             $base_class,
128             @$roles,
129             );
130             }
131              
132             sub set_plugin_applies_to {
133 11     11 0 26 my ($plugin_name, $sub) = @_;
134 11         23 my $plugin = $plugins{$plugin_name};
135 11         22 local $Carp::Internal{ (__PACKAGE__) } = 1;
136              
137             croak "The applies_to for the $plugin_name plugin has already been set"
138 11 50       29 if exists $plugin->{applies_to};
139              
140 11 100       39 if (!ref $sub) {
    100          
    100          
141 6         11 my $package = $sub;
142 6 100   16   27 $sub = sub{ $_[0]->isa( $package ) or $_[0]->DOES( $package ) };
  16         228  
143             }
144             elsif (ref($sub) eq 'ARRAY') {
145 1         3 my $methods = $sub;
146             $sub = sub{
147 4     4   9 foreach my $method (@$methods) {
148 5 100       41 next if $_[0]->can($method);
149 3         11 return 0;
150             }
151 1         4 return 1;
152 1         4 };
153             }
154             elsif (ref($sub) eq 'Regexp') {
155 1         2 my $re = $sub;
156             $sub = sub{
157 4 100   4   28 return ($_[0] =~ $re) ? 1 : 0;
158 1         3 };
159             }
160              
161 11 50       33 croak 'Plugin applies_to must be a class name, arrayref of methods, regex, or code ref'
162             if ref($sub) ne 'CODE';
163              
164 11         23 $plugin->{applies_to} = $sub;
165              
166 11         30 return;
167             }
168              
169             sub get_plugin_applies_to {
170 73     73 0 117 my ($plugin_name) = @_;
171 73         128 my $plugin = $plugins{$plugin_name};
172              
173 73   100 37   310 return $plugin->{applies_to} || sub{ 1 };
  37         159  
174             }
175              
176             sub set_plugin_includes {
177 6     6 0 16 my ($plugin_name, @includes) = @_;
178 6         13 my $plugin = $plugins{$plugin_name};
179 6         14 local $Carp::Internal{ (__PACKAGE__) } = 1;
180              
181             croak "The includes for the $plugin_name plugin has already been set"
182 6 50       13 if exists $plugin->{includes};
183              
184             $plugin->{includes} = [
185 6         12 map { resolve_plugin($_, $plugin_name) }
  10         78  
186             @includes
187             ];
188              
189 6         98 return;
190             }
191              
192             sub get_plugin_includes {
193 38     38 0 76 my ($plugin_name) = @_;
194 38         61 my $plugin = $plugins{$plugin_name};
195              
196 38   100     166 return $plugin->{includes} || [];
197             }
198              
199             sub init_consumer {
200 12     12 0 30 my ($consumer_name) = @_;
201 12         32 my $consumer = $consumers{$consumer_name} = {};
202 12         128 $consumer->{moo_extends} = $consumer_name->can('extends');
203 12         46 $consumer->{moo_with} = $consumer_name->can('with');
204 12         45 $consumer->{moo_has} = $consumer_name->can('has');
205 12         34 return;
206             }
207              
208             sub is_consumer {
209 11     11 0 22 my ($consumer_name) = @_;
210 11 50       43 return $consumers{$consumer_name} ? 1 : 0;
211             }
212              
213             sub get_consumer_moo_extends {
214 12     12 0 25 my ($consumer_name) = @_;
215 12         22 my $consumer = $consumers{$consumer_name};
216 12         49 return $consumer->{moo_extends};
217             }
218              
219             sub get_consumer_moo_with {
220 12     12 0 28 my ($consumer_name) = @_;
221 12         22 my $consumer = $consumers{$consumer_name};
222 12         39 return $consumer->{moo_with};
223             }
224              
225             sub get_consumer_moo_has {
226 9     9 0 18 my ($consumer_name) = @_;
227 9         19 my $consumer = $consumers{$consumer_name};
228 9         28 return $consumer->{moo_has};
229             }
230              
231             sub set_consumer_namespace {
232 1     1 0 5 my ($consumer_name, $namespace) = @_;
233 1         3 my $consumer = $consumers{$consumer_name};
234 1         5 local $Carp::Internal{ (__PACKAGE__) } = 1;
235              
236             croak "The plugin namespace for $consumer has already been set"
237 1 50       5 if exists $consumer->{namespace};
238              
239 1 50       4 croak "An undefined plugin namespace cannot be set"
240             if !defined $namespace;
241              
242 1 50       6 croak "The plugin namespace $namespace does not appear to be a valid module name"
243             if !is_module_name( $namespace );
244              
245 1         21 $consumer->{namespace} = $namespace;
246              
247 1         24 return;
248             }
249              
250             sub get_consumer_namespace {
251 36     36 0 69 my ($consumer_name) = @_;
252 36         64 my $consumer = $consumers{$consumer_name};
253 36         92 local $Carp::Internal{ (__PACKAGE__) } = 1;
254              
255 36   66     675 return $consumer->{namespace} || $consumer_name;
256             }
257              
258             1;
259             __END__