File Coverage

blib/lib/Plugin/Simple.pm
Criterion Covered Total %
statement 104 108 96.3
branch 40 44 90.9
condition 5 6 83.3
subroutine 14 14 100.0
pod n/a
total 163 172 94.7


line stmt bran cond sub pod time code
1             package Plugin::Simple;
2 7     7   56879 use 5.006;
  7         20  
3 7     7   25 use strict;
  7         7  
  7         115  
4 7     7   20 use warnings;
  7         15  
  7         184  
5              
6 7     7   25 use Carp qw(croak);
  7         10  
  7         439  
7 7     7   29 use Cwd qw (abs_path);
  7         13  
  7         292  
8 7     7   3033 use Module::List qw(list_modules);
  7         137090  
  7         439  
9 7     7   3315 use Module::Load;
  7         6296  
  7         42  
10              
11             our $VERSION = '0.07';
12              
13             my $self;
14              
15             sub import {
16 10     10   1670 my ($class, %opts) = @_;
17              
18 10         33 $self = __PACKAGE__->_new(%opts);
19              
20 10 100       974 my $sub_name = $opts{sub_name} ? $opts{sub_name} : 'plugins';
21              
22             {
23 7     7   635 no warnings 'redefine';
  7         13  
  7         280  
  10         13  
24 7     7   30 no strict 'refs';
  7         9  
  7         5390  
25              
26 10         25 my $pkg = (caller)[0];
27 10         15 *{"$pkg\::$sub_name"} = \&_plugins;
  10         2949  
28             }
29             }
30             sub _new {
31 12     12   37 my ($class, %args) = @_;
32 12         26 my $self = bless \%args, $class;
33              
34 12         27 return $self;
35             }
36             sub _search {
37 6     6   904 my ($self, $pkg, $item) = @_;
38              
39 6         6 my @plugins;
40              
41 6 100       22 if ($item){
42 3 100       11 if ($item !~ /::$/){
43 2         5 push @plugins, $item;
44             }
45             else {
46 1         2 my $candidates;
47 1         2 eval { $candidates = list_modules(
  1         11  
48             $item,
49             {list_modules => 1, recurse => 1}
50             );
51             };
52 1         3784 push @plugins, keys %$candidates;
53             }
54             }
55             else {
56 3         5 my $path = $pkg;
57 3         7 $path .= '::Plugin::';
58 3         5 my $candidates = {};
59 3         17 eval { $candidates = list_modules(
  3         21  
60             $path,
61             {
62             list_modules => 1,
63             recurse => 1
64             }
65             );
66             };
67 3         1161 push @plugins, keys %$candidates;
68             }
69              
70 6         10 my @loaded;
71              
72 6         28 for (@plugins){
73 32         98 my $ok = $self->_load($_);
74 32         96 push @loaded, $ok;
75             }
76              
77 6         40 return @loaded;
78             }
79             sub _load {
80 42     42   979 my ($self, $plugin) = @_;
81              
82 42 100       221 if ($plugin =~ /(.*)\W(\w+)\.pm/){
    50          
83 9         28 unshift @INC, $1;
84 9         19 $plugin = $2;
85             }
86             elsif ($plugin =~ /^(\w+)\.pm$/){
87 0         0 unshift @INC, '.';
88 0         0 $plugin = $1;
89             }
90              
91 42         47 my $loaded = eval { load $plugin; 1; };
  42         148  
  41         11656093  
92              
93 42 100       670 if ($loaded) {
94 41         126 return $plugin;
95             }
96             }
97             sub _plugins {
98 12 100   12   4990 shift if ref $_[0]; # dump the calling object if present
99              
100 12         14 my ($item, $can);
101              
102 12 100 100     58 if ($_[0] && $_[0] eq 'can'){
103 1         2 shift;
104 1         1 $can = shift;
105             }
106             else {
107 11         14 $item = shift;
108 11         10 shift;
109 11         12 $can = shift;
110             }
111              
112 12 100       27 if (@_){
113 1         114 croak "usage: plugin(['Load::From'], [can => 'sub']), " .
114             "in that order\n";
115             }
116              
117 11         25 my $pkg = (caller)[0];
118 11         12 my @plugins;
119              
120 11 100       21 if ($item){
121 10 100       50 if ($item =~ /(?:\.pm|\.pl)/){
122 9         7 my $abs_path;
123 9         13 my $ok_file = eval { $abs_path = abs_path($item); 1 };
  9         256  
  9         14  
124              
125 9 50       19 if (! $ok_file){
126 0         0 croak
127             "\npackage $item can't be found, and no default plugin set\n";
128             }
129              
130 9 100       84 if (-e $abs_path){
131 8         21 @plugins = $self->_load($abs_path);
132             }
133             }
134             else{
135 1         5 @plugins = $self->_search($pkg, $item);
136             }
137             }
138 11 100       22 if (! @plugins){
139 2         22 @plugins = _search($pkg);
140             }
141 11 50 66     43 if (! $plugins[0] && $self->{default}){
142 0         0 push @plugins, $self->_load($self->{default});
143             }
144 11 100       20 if (! $plugins[0]){
145 3 100       10 if ($item){
146 2         282 croak
147             "\npackage $item can't be found, and no default plugin set\n";
148             }
149             else {
150 1         64 croak "\npackage can't be found, and no default plugin set\n";
151             }
152             }
153 8         7 my @wanted_plugins;
154              
155 8 100       14 if ($can) {
156 3         5 for my $mod (@plugins){
157 3         5 my $can_count = 0;
158 3         4 for my $sub (@$can){
159 4 100       19 if ($mod->can($sub)){
160 3         4 $can_count++;
161             }
162             }
163 3 100       8 push @wanted_plugins, $mod if $can_count == @$can;
164             }
165 3 50       10 return wantarray ? @wanted_plugins : $wanted_plugins[0];
166             }
167              
168 5 100       23 return wantarray ? @plugins : $plugins[0];
169             }
170              
171             1;
172              
173             =head1 NAME
174              
175             Plugin::Simple - Load plugins from files or modules.
176              
177             =for html
178            
179             Coverage Status
180              
181             =head1 SYNOPSIS
182              
183             use Plugin::Simple;
184              
185             # load a plugin module from a file
186              
187             @plugins = plugins('/path/to/MyModule.pm');
188              
189             # load all modules under '__PACKAGE__::Plugin' namespace
190              
191             my @plugins = plugins(); # call in scalar context to retrieve the first one
192              
193             # load all plugins under a specific namespace (note the trailing ::)
194              
195             @plugins = plugins('Any::Namespace::');
196              
197             # load/return only the plugins that can perform specific functions
198              
199             @plugins = plugins(can => ['foo', 'bar]); # foo and bar
200              
201             # instead of importing 'plugins()', change the name:
202              
203             use Plugin::Simple sub_name => 'foo';
204             @plugins = foo(...);
205              
206             # set a default fallback plugin if searching turns up nothing
207              
208             use Plugin::Simple default => 'My::Module::Plugin::DefaultPlugin'
209              
210             # do something with the plugins
211              
212             for my $plugin (@plugins){
213             $plugin->plugin_func(@args);
214             }
215              
216             # works in OO modules too simply by using it
217              
218             my @plugins = $self->plugins();
219              
220             =head1 DESCRIPTION
221              
222             There are many plugin modules available on the CPAN, but I wrote this one just
223             for fun. It's very simple, extremely lightweight, and is extremely minimalistic
224             in what it does.
225              
226             It searches for modules in installed packages or non-installed files, and loads
227             them (without string C). You can optionally have us return only the
228             plugins that C perform a specific task.
229              
230             =head1 LOAD OPTIONS
231              
232             By default, we force C into your namespace. To change this name:
233              
234             use Plugin::Simple sub_name => 'other_name';
235              
236             If searching fails, you can ensure a default known plugin gets loaded:
237              
238             use Plugin::Simple default => 'My::Plugin';
239              
240             To use both options, simply separate them with a comma.
241              
242             =head1 FUNCTIONS/METHODS
243              
244             None. We simply install a C function within the namespace of the
245             package that Cd us.
246              
247             =head1 AUTHOR
248              
249             Steve Bertrand, C<< >>
250              
251             =head2 CONTRIBUTING
252              
253             Any and all feedback and help is appreciated. A Pull Request is the preferred
254             method of receiving changes (L),
255             but regular patches through the bug tracker, or even just email discussions are
256             welcomed.
257              
258             =head1 BUGS
259              
260             L
261              
262             =head1 SUPPORT
263              
264             You can find documentation for this script and module with the perldoc command.
265              
266             perldoc Plugin::Simple;
267              
268             =head1 SEE ALSO
269              
270             There are far too many plugin import modules on the CPAN to mention here.
271              
272             =head1 LICENSE AND COPYRIGHT
273              
274             Copyright 2016 Steve Bertrand.
275              
276             This program is free software; you can redistribute it and/or modify it
277             under the terms of either: the GNU General Public License as published
278             by the Free Software Foundation; or the Artistic License.
279              
280             See L for more information.
281