File Coverage

blib/lib/MooX/PluginKit/Core.pm
Criterion Covered Total %
statement 109 114 95.6
branch 33 48 68.7
condition 6 7 85.7
subroutine 26 27 96.3
pod 0 17 0.0
total 174 213 81.6


line stmt bran cond sub pod time code
1             package MooX::PluginKit::Core;
2              
3             our $VERSION = '0.05';
4              
5             =head1 NAME
6              
7             MooX::PluginKit::Core - The PluginKit internal guts.
8              
9             =head2 DESCRIPTION
10              
11             This module tracks metadata about consumers and plugins as well as
12             providing much of the underlying logic behind the other PluginKit
13             modules.
14              
15             Currently this module is not documented because it is not intended
16             to be used directly. This may change.
17              
18             =cut
19              
20 5     5   435932 use Carp qw( croak );
  5         21  
  5         239  
21 5     5   2169 use Moo::Role qw();
  5         55842  
  5         141  
22 5     5   36 use Module::Runtime qw( require_module is_module_name );
  5         10  
  5         33  
23              
24             require UNIVERSAL::DOES
25             unless defined &UNIVERSAL::DOES;
26              
27 5     5   348 use strictures 2;
  5         41  
  5         212  
28 5     5   3260 use namespace::clean;
  5         51970  
  5         30  
29              
30 5     5   1262 use Exporter qw( import );
  5         11  
  5         6893  
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 37 my ($plugin_name) = @_;
57 16         35 $plugins{$plugin_name} = {};
58 16         38 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         45 local $Carp::Internal{ (__PACKAGE__) } = 1;
69              
70 22 50       45 croak "An undefined plugin name cannot be resolved"
71             if !defined $plugin_name;
72              
73 22 100       58 if ($plugin_name =~ m{^::}) {
74 8 50       23 croak "The relative plugin $plugin_name cannot be made absolute without a namespace"
75             if !defined $namespace;
76              
77 8         19 $plugin_name = $namespace . $plugin_name;
78             }
79              
80 22 50       58 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       329 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       39 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 12344 my ($plugin_name, $class) = @_;
100              
101 73         145 my $sub = get_plugin_applies_to( $plugin_name );
102              
103 73 100       156 return $sub->( $class ) ? 1 : 0;
104             }
105              
106             sub find_applicable_plugins {
107 39     39 0 86 my ($class, @plugins) = @_;
108              
109 39         60 my @final_plugins;
110 39         116 while (@plugins) {
111 45         111 my $plugin = shift( @plugins );
112 45 100       81 next if !does_plugin_apply( $plugin, $class );
113 38         70 push @final_plugins, $plugin;
114 38         57 unshift @plugins, @{ get_plugin_includes( $plugin ) };
  38         59  
115             }
116              
117 39         178 return \@final_plugins;
118             }
119              
120             sub build_class_with_plugins {
121 39     39 0 41861 my ($base_class, @plugins) = @_;
122              
123 39         91 my $roles = find_applicable_plugins( $base_class, @plugins );
124 39 100       128 return $base_class if !@$roles;
125              
126 19         68 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 25 my ($plugin_name, $sub) = @_;
134 11         25 my $plugin = $plugins{$plugin_name};
135 11         23 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       41 if (!ref $sub) {
    100          
    100          
141 6         9 my $package = $sub;
142 6 100   16   49 $sub = sub{ $_[0]->isa( $package ) or $_[0]->DOES( $package ) };
  16         232  
143             }
144             elsif (ref($sub) eq 'ARRAY') {
145 1         2 my $methods = $sub;
146             $sub = sub{
147 4     4   10 foreach my $method (@$methods) {
148 5 100       47 next if $_[0]->can($method);
149 3         13 return 0;
150             }
151 1         5 return 1;
152 1         6 };
153             }
154             elsif (ref($sub) eq 'Regexp') {
155 1         2 my $re = $sub;
156             $sub = sub{
157 4 100   4   30 return ($_[0] =~ $re) ? 1 : 0;
158 1         4 };
159             }
160              
161 11 50       34 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         25 $plugin->{applies_to} = $sub;
165              
166 11         30 return;
167             }
168              
169             sub get_plugin_applies_to {
170 73     73 0 118 my ($plugin_name) = @_;
171 73         130 my $plugin = $plugins{$plugin_name};
172              
173 73   100 37   346 return $plugin->{applies_to} || sub{ 1 };
  37         146  
174             }
175              
176             sub set_plugin_includes {
177 6     6 0 15 my ($plugin_name, @includes) = @_;
178 6         12 my $plugin = $plugins{$plugin_name};
179 6         13 local $Carp::Internal{ (__PACKAGE__) } = 1;
180              
181             croak "The includes for the $plugin_name plugin has already been set"
182 6 50       15 if exists $plugin->{includes};
183              
184             $plugin->{includes} = [
185 6         10 map { resolve_plugin($_, $plugin_name) }
  10         81  
186             @includes
187             ];
188              
189 6         98 return;
190             }
191              
192             sub get_plugin_includes {
193 38     38 0 67 my ($plugin_name) = @_;
194 38         59 my $plugin = $plugins{$plugin_name};
195              
196 38   100     173 return $plugin->{includes} || [];
197             }
198              
199             sub init_consumer {
200 12     12 0 29 my ($consumer_name) = @_;
201 12         32 my $consumer = $consumers{$consumer_name} = {};
202 12         107 $consumer->{moo_extends} = $consumer_name->can('extends');
203 12         62 $consumer->{moo_with} = $consumer_name->can('with');
204 12         35 $consumer->{moo_has} = $consumer_name->can('has');
205 12         27 return;
206             }
207              
208             sub is_consumer {
209 11     11 0 26 my ($consumer_name) = @_;
210 11 50       67 return $consumers{$consumer_name} ? 1 : 0;
211             }
212              
213             sub get_consumer_moo_extends {
214 12     12 0 24 my ($consumer_name) = @_;
215 12         23 my $consumer = $consumers{$consumer_name};
216 12         44 return $consumer->{moo_extends};
217             }
218              
219             sub get_consumer_moo_with {
220 12     12 0 26 my ($consumer_name) = @_;
221 12         22 my $consumer = $consumers{$consumer_name};
222 12         90 return $consumer->{moo_with};
223             }
224              
225             sub get_consumer_moo_has {
226 9     9 0 39 my ($consumer_name) = @_;
227 9         22 my $consumer = $consumers{$consumer_name};
228 9         31 return $consumer->{moo_has};
229             }
230              
231             sub set_consumer_namespace {
232 1     1 0 2 my ($consumer_name, $namespace) = @_;
233 1         3 my $consumer = $consumers{$consumer_name};
234 1         2 local $Carp::Internal{ (__PACKAGE__) } = 1;
235              
236             croak "The plugin namespace for $consumer has already been set"
237 1 50       3 if exists $consumer->{namespace};
238              
239 1 50       3 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         16 $consumer->{namespace} = $namespace;
246              
247 1         3 return;
248             }
249              
250             sub get_consumer_namespace {
251 36     36 0 76 my ($consumer_name) = @_;
252 36         68 my $consumer = $consumers{$consumer_name};
253 36         95 local $Carp::Internal{ (__PACKAGE__) } = 1;
254              
255 36   66     674 return $consumer->{namespace} || $consumer_name;
256             }
257              
258             1;
259             __END__