line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Module::Modular; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
Module::Modular - Create optional plugins for your module |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 DESCRIPTION |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
Module::Modular allows you, or others, to create plugins for your modules. They are not loaded by default - only |
10
|
|
|
|
|
|
|
when you want them to be. This means you can even load them further on down in your module if you wish. |
11
|
|
|
|
|
|
|
The idea is to have your plugins handle certain tasks you don't want cluttering the core code of your module. |
12
|
|
|
|
|
|
|
I started writing this B I came across another plugin module called L. So if you like |
13
|
|
|
|
|
|
|
how that one works better, or even prefer the name (I do), then go check it out. This one is different in the sense |
14
|
|
|
|
|
|
|
you explicitly tell your module what plugins to load, and each plugin may have an initialiser (C<__init>) that will |
15
|
|
|
|
|
|
|
get run once it has been loaded, which I found pretty neat. |
16
|
|
|
|
|
|
|
This module is modular itself. By importing C followed by an array of options you can extend the functionality |
17
|
|
|
|
|
|
|
of Module::Modular. Currently just the one option is available (C) which provides methods for accessing meta data of your plugins. |
18
|
|
|
|
|
|
|
A plugin can only be loaded if it's within the same namespace and within your path (ie: YourModule::Plugin::*) |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 SYNOPSIS |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# MyModule.pm |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
package MyModule; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
use Module::Modular; |
27
|
|
|
|
|
|
|
load_plugins qw; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
sub load_another_plugin { |
30
|
|
|
|
|
|
|
load_plugins 'DifferentOne'; |
31
|
|
|
|
|
|
|
} |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# MyModule::Plugin::Foo |
34
|
|
|
|
|
|
|
package MyModule::Plugin::Foo; |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub __init { |
37
|
|
|
|
|
|
|
my ($class, $name) = @_; |
38
|
|
|
|
|
|
|
# $class = MyModule::Plugin::Foo |
39
|
|
|
|
|
|
|
# $name = Foo |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# some code here to be run when loaded |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub foo { |
45
|
|
|
|
|
|
|
print "You have been foo'd!\n"; |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
Now, when you C |
49
|
|
|
|
|
|
|
It's quite simple to get a list of plugins, or you can get hold of a single plugin to do stuff with. |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# Run the foo() method within the Foo plugin |
52
|
|
|
|
|
|
|
my $foo_plugin = $module->plugin('Foo')->foo(); |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
Calling the C method will return an array of your loaded plugins. Each one will be blessed, so you have objects to work with which makes things easier. |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
# call the foo() method on every loaded plugin |
57
|
|
|
|
|
|
|
for my $plugin ($module->plugins) { |
58
|
|
|
|
|
|
|
$plugin->foo(); |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=head1 METHODS |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
C exports only a few functions into your module. They are... |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=head2 load_plugins |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
void load_plugins(@list) |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
Takes an array of plugins (Not their entire path, just the name of the plugin. For example, |
70
|
|
|
|
|
|
|
if I wanted to load C I would only have to use C. |
71
|
|
|
|
|
|
|
If it can't load the module for any reason it will print out a warnings and move onto the next one if it's specified. |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=head2 plugins |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
@array plugins(void) |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
Returns an array of your loaded plugins. It will only register those introduced by C, just having one in the right namespace and loaded by any other means will do nothing. |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=head2 plugin |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
$object plugin(string) |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
Returns a blessed reference of a plugin (ie: The plugin object). You only need to supply the name, not the entire path. For example |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
my $plugin = $module->plugin('Foo'); |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=head2 stash |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
This is a plugin method (Called from a plugin only). It's a really simple method used to get the data of a specific global variable from the base module. After all, what's the point of a plugin if you have absolutely no way to share data, right? |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
# MyModule.pm |
92
|
|
|
|
|
|
|
package MyModule; |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
use Module::Modular; |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
our $PluginStash = { bees => 'knees' }; |
97
|
|
|
|
|
|
|
load_plugins 'Bees'; |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# MyModule::Plugin::Bees |
100
|
|
|
|
|
|
|
package MyModule::Plugin::Bees; |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
sub __init { |
103
|
|
|
|
|
|
|
my ($self, $name) = @_; |
104
|
|
|
|
|
|
|
print "The $name have " . $self->stash('bees'); |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
It's not overly useful, secure, or remotely interesting, but if you need a quick and dirty way to get some data to your plugins, it works. I am trying to find a neater solution.. |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=head2 OPTIONS |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
When you C |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
B |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
Implements specific accessors to access the meta data of a plugin |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
# MyModule.pm |
118
|
|
|
|
|
|
|
use Module::Modular |
119
|
|
|
|
|
|
|
with => 'Accessors'; |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
load_plugins qw; |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
# test.pl |
124
|
|
|
|
|
|
|
for my $plugin ($module->plugins) { |
125
|
|
|
|
|
|
|
say "Name: " . $plugin->name; |
126
|
|
|
|
|
|
|
say "Version: " . $plugin->version; |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
B |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
Will not allow you to get a plugin object directly from outside the core modules scope |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
# MyModule.pm |
134
|
|
|
|
|
|
|
use Module::Modular |
135
|
|
|
|
|
|
|
with => [qw]; |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
load_plugins 'Foo'; |
138
|
|
|
|
|
|
|
sub get_plugin { my $self = shift; return $self->plugin('Foo'); } |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
# test.pl |
141
|
|
|
|
|
|
|
my $c = MyModule->new; |
142
|
|
|
|
|
|
|
$c->plugin('Foo')->foo(); # fails |
143
|
|
|
|
|
|
|
my $plugin = $c->get_plugin(); # works, because it went through the core module first |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=cut |
146
|
|
|
|
|
|
|
|
147
|
1
|
|
|
1
|
|
21654
|
use warnings; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
32
|
|
148
|
1
|
|
|
1
|
|
6
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
41
|
|
149
|
|
|
|
|
|
|
|
150
|
1
|
|
|
1
|
|
874
|
use Import::Into; |
|
1
|
|
|
|
|
2897
|
|
|
1
|
|
|
|
|
242
|
|
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
our $VERSION = '0.003'; |
153
|
|
|
|
|
|
|
our $LoadedPlugins = []; |
154
|
|
|
|
|
|
|
our $Config = { |
155
|
|
|
|
|
|
|
'accessors' => 0, |
156
|
|
|
|
|
|
|
'strict' => 0, |
157
|
|
|
|
|
|
|
}; |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
sub import { |
160
|
1
|
|
|
1
|
|
10
|
my ($class, %opts) = @_; |
161
|
1
|
|
|
|
|
3
|
my $caller = scalar caller; |
162
|
|
|
|
|
|
|
|
163
|
1
|
50
|
|
|
|
4
|
if (exists $opts{with}) { |
164
|
0
|
0
|
|
|
|
0
|
if (ref($opts{with})) { |
165
|
0
|
|
|
|
|
0
|
for my $with (@{$opts{with}}) { |
|
0
|
|
|
|
|
0
|
|
166
|
0
|
|
|
|
|
0
|
_enable_option($with); |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
else { |
170
|
0
|
|
|
|
|
0
|
_enable_option($opts{with}); |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
1
|
|
|
|
|
3
|
_import_defs($caller, |
175
|
|
|
|
|
|
|
qw); |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
sub _enable_option { |
179
|
0
|
|
|
0
|
|
0
|
my $opt = shift; |
180
|
0
|
|
|
|
|
0
|
for ($opt) { |
181
|
0
|
0
|
|
|
|
0
|
if (/Accessors/) { $Config->{accessors} = 1; } |
|
0
|
0
|
|
|
|
0
|
|
182
|
0
|
|
|
|
|
0
|
elsif (/Strict/) { $Config->{strict} = 1; } |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
sub _import_defs { |
187
|
1
|
|
|
1
|
|
3
|
my ($caller, @methods) = @_; |
188
|
1
|
|
|
|
|
101
|
importmethods: { |
189
|
1
|
|
|
1
|
|
5
|
no strict 'refs'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
2
|
|
190
|
1
|
|
|
|
|
2
|
foreach my $method (@methods) { |
191
|
3
|
|
|
|
|
5
|
*{"${caller}::${method}"} = \&$method; |
|
3
|
|
|
|
|
24
|
|
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
sub load_plugins { |
197
|
0
|
|
|
0
|
1
|
|
my (@plugins) = @_; |
198
|
0
|
|
|
|
|
|
my $caller = caller; |
199
|
0
|
|
|
|
|
|
my $name; |
200
|
1
|
|
|
|
|
525
|
loadplugins: { |
201
|
1
|
|
|
1
|
|
4
|
no strict 'refs'; |
|
1
|
|
|
|
|
7
|
|
|
0
|
|
|
|
|
|
|
202
|
0
|
|
|
|
|
|
foreach my $plugin (@plugins) { |
203
|
0
|
|
|
|
|
|
$name = $plugin; |
204
|
0
|
|
|
|
|
|
$plugin = "${caller}::Plugin::${plugin}"; |
205
|
0
|
|
|
|
|
|
eval "use $plugin;"; |
206
|
0
|
0
|
|
|
|
|
if ($@) { |
207
|
0
|
|
|
|
|
|
warn "Failed loading plugin ${plugin}: ${@}"; |
208
|
0
|
|
|
|
|
|
next; |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
|
211
|
0
|
|
|
|
|
|
$plugin->import::into($caller); |
212
|
0
|
|
|
|
|
|
*{"${plugin}::stash"} = sub { |
213
|
0
|
|
|
0
|
|
|
my ($mod, $key) = @_; |
214
|
0
|
0
|
|
|
|
|
if ($mod = ref($mod)) { |
215
|
0
|
|
|
|
|
|
my $module = (split('::', $mod))[0]; |
216
|
0
|
|
|
|
|
|
return ${"${module}::PluginStash"}->{$key}; |
|
0
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
} |
218
|
0
|
|
|
|
|
|
}; |
219
|
|
|
|
|
|
|
|
220
|
0
|
0
|
|
|
|
|
if ($plugin->can('__init')) { |
221
|
0
|
|
|
|
|
|
my $blessed_plugin = bless {}, $plugin; |
222
|
0
|
|
|
|
|
|
$blessed_plugin->__init($name); |
223
|
|
|
|
|
|
|
} |
224
|
0
|
|
0
|
|
|
|
push @$LoadedPlugins, bless { |
225
|
|
|
|
|
|
|
name => $name, |
226
|
|
|
|
|
|
|
version => $plugin->VERSION||'Unknown', |
227
|
|
|
|
|
|
|
}, $plugin; |
228
|
|
|
|
|
|
|
|
229
|
0
|
0
|
|
|
|
|
if ($Config->{accessors}) { |
230
|
0
|
|
|
0
|
|
|
*{"${plugin}::name"} = sub { return shift->{name}; }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
231
|
0
|
|
|
0
|
|
|
*{"${plugin}::version"} = sub { return shift->{version}; }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
sub plugin { |
238
|
0
|
|
|
0
|
1
|
|
my ($self, $plugin) = @_; |
239
|
0
|
|
|
|
|
|
my $caller = caller(); |
240
|
0
|
|
|
|
|
|
my @plugins = $self->plugins; |
241
|
0
|
0
|
|
|
|
|
if (my $first = $plugins[0]) { |
242
|
0
|
|
|
|
|
|
$plugin = ref($self) . "::Plugin::" . $plugin; |
243
|
0
|
0
|
0
|
|
|
|
if ($Config->{strict} and ref($first) ne "${caller}::Plugin::" . $first->{name}) { |
244
|
0
|
|
|
|
|
|
warn "Can't call plugin outside core modules scope"; |
245
|
0
|
|
|
|
|
|
return 0; |
246
|
|
|
|
|
|
|
} |
247
|
0
|
0
|
|
|
|
|
if (grep { ref($_) eq $plugin } @$LoadedPlugins) { |
|
0
|
|
|
|
|
|
|
248
|
0
|
|
|
|
|
|
return bless {}, $plugin; |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
else { |
251
|
0
|
|
|
|
|
|
warn "Could not get plugin ${plugin}: Not loaded"; |
252
|
0
|
|
|
|
|
|
return 0; |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
else { |
256
|
0
|
|
|
|
|
|
warn "No plugins loaded"; |
257
|
0
|
|
|
|
|
|
return 0; |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
sub plugins { |
262
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
263
|
0
|
|
|
|
|
|
return @$LoadedPlugins; |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
=head1 AUTHOR |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
Brad Haywood |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
=head1 LICENSE |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
=cut |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
1; |