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