File Coverage

blib/lib/Module/Pluggable/Fast.pm
Criterion Covered Total %
statement 43 82 52.4
branch 2 26 7.6
condition 5 14 35.7
subroutine 11 15 73.3
pod n/a
total 61 137 44.5


line stmt bran cond sub pod time code
1             package Module::Pluggable::Fast;
2              
3 3     3   119150 use strict;
  3         8  
  3         534  
4 3     3   16 use vars '$VERSION';
  3         7  
  3         141  
5 3     3   3053 use UNIVERSAL::require;
  3         5907  
  3         30  
6 3     3   100 use Carp qw/croak carp/;
  3         6  
  3         159  
7 3     3   17 use File::Find ();
  3         6  
  3         48  
8 3     3   16 use File::Basename;
  3         6  
  3         316  
9 3     3   2964 use File::Spec::Functions qw/splitdir catdir abs2rel/;
  3         2246  
  3         456  
10              
11             $VERSION = '0.19';
12              
13             =head1 NAME
14              
15             Module::Pluggable::Fast - Fast plugins with instantiation
16              
17             =head1 SYNOPSIS
18              
19             package MyClass;
20             use Module::Pluggable::Fast
21             name => 'components',
22             search => [ qw/MyClass::Model MyClass::View MyClass::Controller/ ];
23              
24             package MyOtherClass;
25             use MyClass;
26             my @components = MyClass->components;
27              
28             =head1 DESCRIPTION
29              
30             Similar to C but instantiates plugins as soon as they're
31             found, useful for code generators like C.
32              
33             =head2 OPTIONS
34              
35             =head3 name
36              
37             Name for the exported method.
38             Defaults to plugins.
39              
40             =head3 require
41              
42             If true, only require plugins.
43              
44             =head3 callback
45              
46             Codref to be called instead of the default instantiate callback.
47              
48             =head3 search
49              
50             Arrayref containing a list of namespaces to search for plugins.
51             Defaults to the ::Plugin:: namespace of the calling class.
52              
53             =cut
54              
55             sub import {
56 3     3   38 my ( $class, %args ) = @_;
57 3         9 my $caller = caller;
58 3     3   19 no strict 'refs';
  3         5  
  3         2859  
59 3   50     106 *{ "$caller\::" . ( $args{name} || 'plugins' ) } = sub {
60 2     2   36 my $self = shift;
61 2   50     24 $args{search} ||= ["$caller\::Plugin"];
62 2   50     14 $args{require} ||= 0;
63             $args{callback} ||= sub {
64 0     0   0 my $plugin = shift;
65 0         0 my $obj = $plugin;
66 0         0 eval { $obj = $plugin->new(@_) };
  0         0  
67 0 0       0 carp qq/Couldn't instantiate "$plugin", "$@"/ if $@;
68 0         0 return $obj;
69 2   50     25 };
70              
71 2         4 my %plugins;
72 2 50       15 foreach my $dir ( exists $INC{'blib.pm'} ? grep { /blib/ } @INC : @INC )
  20         52  
73             {
74 8         9 foreach my $searchpath ( @{ $args{search} } ) {
  8         20  
75 8         50 my $sp = catdir( $dir, ( split /::/, $searchpath ) );
76 8 50 33     224 next unless ( -e $sp && -d $sp );
77 0         0 foreach my $file ( _find_packages($sp) ) {
78 0         0 my ( $name, $directory ) = fileparse $file, qr/\.pm/;
79 0         0 $directory = abs2rel $directory, $sp;
80 0         0 my $plugin = join '::', splitdir catdir $searchpath,
81             $directory, $name;
82 0         0 $plugin->require;
83 0         0 my $error = $UNIVERSAL::require::ERROR;
84 0 0       0 die qq/Couldn't load "$plugin", "$error"/ if $error;
85              
86 0 0       0 unless ( $plugins{$plugin} ) {
87 0 0       0 $plugins{$plugin} =
88             $args{require}
89             ? $plugin
90             : $args{callback}->( $plugin, @_ );
91             }
92              
93 0         0 for my $class ( _list_packages($plugin) ) {
94 0 0       0 next if $plugins{$class};
95 0 0       0 $plugins{$class} =
96             $args{require}
97             ? $class
98             : $args{callback}->( $class, @_ );
99             }
100             }
101             }
102             }
103 2         16 return values %plugins;
104 3         23 };
105             }
106              
107             sub _find_packages {
108 0     0     my $search = shift;
109              
110 0           my @files = ();
111              
112             my $wanted = sub {
113 0     0     my $path = $File::Find::name;
114 0 0         return unless $path =~ /\w+\.pm$/;
115 0 0         return unless $path =~ /\A(.+)\z/;
116 0           $path = $1; # untaint
117              
118             # don't include symbolig links pointing into nowhere
119             # (e.g. emacs lock-files)
120 0 0 0       return if -l $path && !-e $path;
121 0           $path =~ s#^\\./##;
122 0           push @files, $path;
123 0           };
124              
125 0           File::Find::find( { no_chdir => 1, wanted => $wanted }, $search );
126              
127 0           return @files;
128             }
129              
130             sub _list_packages {
131 0     0     my $class = shift;
132 0 0         $class .= '::' unless $class =~ m!::$!;
133 3     3   28 no strict 'refs';
  3         11  
  3         1184  
134 0           my @classes;
135 0           for my $subclass ( grep !/^main::$/, grep /::$/, keys %$class ) {
136 0           $subclass =~ s!::$!!;
137 0 0         next if $subclass =~ /^::/;
138 0           push @classes, "$class$subclass";
139 0           push @classes, _list_packages("$class$subclass");
140             }
141 0           return @classes;
142             }
143              
144             =head1 AUTHOR
145              
146             Sebastian Riedel, C
147              
148             =head1 COPYRIGHT
149              
150             This program is free software, you can redistribute it and/or modify it under
151             the same terms as Perl itself.
152              
153             =head1 SEE ALSO
154              
155             L
156              
157             =cut
158              
159             1;