File Coverage

blib/lib/Plugin/Simple.pm
Criterion Covered Total %
statement 100 103 97.0
branch 39 42 92.8
condition 5 6 83.3
subroutine 14 14 100.0
pod n/a
total 158 165 95.7


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