File Coverage

blib/lib/Class/Plugin/Util.pm
Criterion Covered Total %
statement 29 142 20.4
branch 0 58 0.0
condition 0 21 0.0
subroutine 10 19 52.6
pod 7 7 100.0
total 46 247 18.6


line stmt bran cond sub pod time code
1             # $Id: Util.pm,v 1.5 2007/07/11 23:54:29 ask Exp $
2             # $Source: /opt/CVS/classpluginutil/lib/Class/Plugin/Util.pm,v $
3             # $Author: ask $
4             # $HeadURL$
5             # $Revision: 1.5 $
6             # $Date: 2007/07/11 23:54:29 $
7             package Class::Plugin::Util;
8 2     2   90908 use strict;
  2         5  
  2         84  
9 2     2   12 use warnings;
  2         2  
  2         54  
10 2     2   11 use warnings::register;
  2         4  
  2         373  
11             our $VERSION = 0.009;
12 2     2   128 use 5.006001;
  2         6  
  2         100  
13             {
14 2     2   833 use English qw( -no_match_vars );
  2         4857  
  2         14  
15 2     2   3910 use Module::Find;
  2         3189  
  2         370  
16              
17             # List of subs to export.
18             my %EXPORT = (
19             supports => \&supports,
20             doesnt_support => \&doesnt_support,
21             factory_new => \&factory_new,
22             first_available_new => \&first_available_new,
23             require_class => \&require_class,
24             load_plugins => \&load_plugins,
25             get_plugins => \&get_plugins,
26             );
27              
28             my $CALL_LEVEL = 0;
29              
30             my $CLASS_SEPARATOR = q{::};
31              
32             # Cache of modules already tested.
33             my %probe_cache = ( );
34              
35             # Cache of modules that we know doesn't exist.
36             my %probe_fail_cache = ( );
37              
38             # Cache of class names to file names.
39             my %class_to_filename_cache = ( );
40              
41             my %plugins_for_superclass = ( );
42              
43             # To be backward compatible with < 1.0
44             BEGIN { ## no critic
45 2     2   73 *Class::Plugin::Util::_require_class
46             = \&Class::Plugin::Util::require_class;
47             }
48              
49             #------------------------------------------------------------------------
50             # ::import
51             #
52             # Our own Exporter functionality.
53             # We don't wanna load in all the excess code of Exporter.
54             #------------------------------------------------------------------------
55             sub import {
56 2     2   21 shift; ### delete package name from @_.
57 2         7 my $caller = caller;
58            
59 2     2   12 no strict 'refs'; ## no critic
  2         4  
  2         2164  
60 2         11 while (@_) {
61 0         0 my $export_attr = shift @_;
62 0         0 my $sub_coderef = $EXPORT{$export_attr};
63              
64 0 0       0 if (not $sub_coderef) {
65 0         0 require Carp;
66 0         0 Carp->import('croak');
67              
68             ## no critic;
69 0         0 croak("Class::Plugin::Util does not export '$export_attr'");
70             }
71              
72 0         0 my $new_package_address = join q{::}, ($caller, $export_attr);
73 0         0 *{ $new_package_address } = $sub_coderef;
  0         0  
74             }
75              
76 2         146548 return;
77             }
78              
79             sub _ensure_hashref {
80 0     0     my ($orig_ref, $value) = @_;
81 0 0         return { } if not $orig_ref;
82 0           my %result;
83 0   0       $value ||= 1;
84              
85 0 0         if (ref $orig_ref eq 'HASH') {
    0          
86 0           %result = %{ $orig_ref };
  0            
87             }
88             elsif (ref $orig_ref eq 'ARRAY') {
89 0           %result = map {$_ => $value} @{ $orig_ref };
  0            
  0            
90             }
91             else {
92 0           $result{$orig_ref} = $value;
93             }
94              
95 0           return \%result;
96             }
97              
98             #------------------------------------------------------------------------
99             # ::load_plugins( $superclass, @$opt_ignore_ref )
100             #
101             # Load all modules that is a subclass of superclass and that has
102             # a register_plugin method. The register plugin method should return
103             # a hashref like this:
104             #
105             # return {
106             # name => 'plugin_name',
107             # class => __PACKAGE__,
108             # aliases => [ qw(Foo foo bar BAR) ],
109             #------------------------------------------------------------------------
110             sub load_plugins {
111 0     0 1   my ($superclass, $ignore_ref) = @_;
112 0   0       $superclass ||= caller;
113 0 0         return 1 if $plugins_for_superclass{$superclass};
114              
115 0           my @subclasses = Module::Find::findallmod($superclass);
116              
117 0           $ignore_ref = _ensure_hashref($ignore_ref);
118              
119 0           my %plugins;
120            
121             SUBCLASS:
122 0           for my $subclass (@subclasses) {
123 0           my $colcol_pos = rindex $subclass, q{::};
124 0 0         my $last_name = $colcol_pos >= 0
125             ? substr $subclass, $colcol_pos + 2, length $subclass
126             : $subclass;
127 0 0         next SUBCLASS if $ignore_ref->{$last_name};
128 0           my $req_ret = require_class($subclass) ;
129 0 0         next SUBCLASS if not $req_ret;
130 0 0         next SUBCLASS if not $subclass->can('register_plugin');
131              
132 0           my $plugin_info = $subclass->register_plugin( );
133 0   0       $plugin_info ||= { };
134 0   0       $plugin_info->{name} ||= $last_name;
135 0   0       $plugin_info->{class} ||= $subclass;
136              
137 0           my $aliases = $plugin_info->{aliases};
138 0           $aliases = _ensure_hashref($aliases, $subclass);
139 0           $aliases->{$last_name} = $subclass;
140              
141 0           while (my ($alias, $target) = each %{ $aliases }) {
  0            
142 0           $plugins_for_superclass{$superclass}{$alias} = $target;
143             }
144            
145             };
146              
147             #$plugins_for_superclass{$superclass} = \%plugins;
148            
149 0           return 1;
150             }
151              
152             sub get_plugins {
153 0     0 1   my ($superclass) = @_;
154 0   0       $superclass ||= caller;
155              
156 0           my $plugins = $plugins_for_superclass{$superclass};
157 0 0         return ref $plugins ? $plugins
158             : { };
159             }
160              
161             #------------------------------------------------------------------------
162             # ::supports( @modules )
163             #
164             # Return true if all the modules are available.
165             #------------------------------------------------------------------------
166             sub supports {
167 0     0 1   my (@modules) = @_;
168            
169 0           return !doesnt_support(@modules);
170             }
171              
172             #------------------------------------------------------------------------
173             # ::doesnt_support( @modules )
174             #
175             # Return the first module not available.
176             #------------------------------------------------------------------------
177             sub doesnt_support {
178 0     0 1   my (@modules) = @_;
179              
180             PROBE:
181 0           for my $required_module (@modules) {
182 0 0         if (! exists $probe_cache{$required_module}) {
183 0 0         if (! require_class($required_module)) {
184 0           return $required_module;
185             }
186             }
187 0           $probe_cache{$required_module}++;
188             }
189              
190             # if we made it this far, everything was supported.
191 0           return;
192             }
193              
194             #------------------------------------------------------------------------
195             # ::first_available_new( \@classes_to_try, @arguments_to_new )
196             #
197             # Return a new instance of the first class in the list of classes to try
198             # that are available.
199             #------------------------------------------------------------------------
200             sub first_available_new {
201 0     0 1   my $classes_to_try_ref = shift;
202              
203 0           CLASS:
204 0           for my $class (@{ $classes_to_try_ref }) {
205 0 0         next CLASS if exists $probe_fail_cache{$class};
206 0 0         next CLASS if ! _CLASS($class);
207 0 0         next CLASS if ! require_class($class);
208              
209 0           my $try_this_object = $class->new( @_ );
210              
211 0 0         if (! $try_this_object) {
212 0           $probe_fail_cache{$class} = 1;
213 0           next CLASS;
214             }
215              
216 0           return $try_this_object;
217             }
218              
219 0           return;
220             }
221              
222             #------------------------------------------------------------------------
223             # ->factory_new($class, @arguments_to_new)
224             #
225             # Return new instance of class in variable.
226             # The class will be required.
227             #------------------------------------------------------------------------
228             sub factory_new {
229 0     0 1   my $class = shift;
230            
231 0 0         require_class($class) or return;
232              
233 0           return $class->new(@_);
234             }
235              
236             #------------------------------------------------------------------------
237             # ->require_class($class, $opt_import)
238             #
239             # Load module by class name.
240             # Does not die on error. (like missing file).
241             #
242             # If $opt_import is set, require_class will behave as new and will
243             # import the module into the callers namespace. (@opt_imports specifies
244             # what to import).
245             #
246             #------------------------------------------------------------------------
247             sub require_class {
248 0     0 1   my ($class, $options_ref) = @_;
249 0   0       $options_ref ||= { };
250              
251             # Must be valid Perl class name.
252 0 0         if (! _CLASS($class)) {
253 0           require Carp;
254 0           Carp->import('croak');
255             ## no critic
256 0           croak("$class is not a valid class name.");
257             }
258              
259             NOSTRICT: {
260 2     2   15 no strict 'refs'; ## no critic;
  2         5  
  2         1834  
  0            
261              
262             # It's already loaded if $VERSION or @ISA is defined in the class.
263 0 0         return 1 if defined ${"${class}::VERSION"};
  0            
264 0 0         return 1 if defined @{"${class}::ISA"};
  0            
265              
266             # It's also loaded if we find a function in that class.
267 0           METHOD:
268 0           for my $namespace_entry (keys %{"${class}::"}) {
269 0 0         if (substr($namespace_entry, -2, 2) eq $CLASS_SEPARATOR) {
270             # It's a subclass, so skip it.
271 0           next METHOD;
272             }
273 0 0         return 1 if defined &{"${class}::$namespace_entry"};
  0            
274             }
275             }
276              
277             # Convert class to filename (Cached).
278             # (Does not have to be cross-platform compatible paths
279             # as perl takes care of this in the background).
280 0           my $class_filename = $class_to_filename_cache{$class};
281 0 0         if (! defined $class_filename) {
282 0           $class_filename = $class . q{.pm};
283 0           $class_filename =~ s{::}{/}xmsg;
284 0           $class_to_filename_cache{$class} = $class_filename;
285             }
286              
287             # Load the module if it's not already loaded.
288 0 0         if (!$INC{$class_filename}) {
289 0           my ($call_pkg, $call_file, $call_line) = caller $CALL_LEVEL;
290            
291 0           my $require_codetext = <<"ENDTEXT"
292             #line $call_line "$call_file"
293             CORE::require(\$class_filename)
294             ENDTEXT
295             ;
296 0 0         if ($options_ref->{'import'}) {
297 0           my @imports;
298 0 0         if (ref $options_ref->{'import'} eq 'HASH') {
299 0           @imports = @{ $options_ref->{'import'} };
  0            
300             }
301             $require_codetext .= <<"ENDTEXT"
302             package $call_pkg;
303             \$module->import(\@opt_imports);
304             ENDTEXT
305 0           ;
306             }
307 0           $require_codetext =~ s/\A\s+//xmsg;
308 0           eval $require_codetext; ## no critic
309              
310 0 0         if ($EVAL_ERROR) {
311 0           my $error_msg = $EVAL_ERROR;
312 0 0         if (warnings::enabled) { ## no critic
313 0           warnings::warn(__PACKAGE__, "load class: $error_msg"); ## no critic
314             }
315 0           return;
316             }
317              
318             }
319              
320 0           return 1;
321             }
322              
323             #------------------------------------------------------------------------
324             # ->_CLASS( $class_name )
325             #
326             # Copied and pasted from Params::Util.
327             # Thanks to Adam Kennedy
328             #------------------------------------------------------------------------
329             sub _CLASS { ## no critic
330 0 0 0 0     (defined $_[0] and ! ref $_[0] and $_[0]
331             =~ m/^[^\W\d]\w*(?:::\w+)*$/s) ? $_[0] : undef; ## no critic;
332             } ## no critic
333              
334             }
335              
336             1; # keep require happy.
337              
338             __END__