File Coverage

blib/lib/Module/Install/Admin.pm
Criterion Covered Total %
statement 42 125 33.6
branch 8 58 13.7
condition 1 24 4.1
subroutine 9 18 50.0
pod 0 8 0.0
total 60 233 25.7


line stmt bran cond sub pod time code
1             package Module::Install::Admin;
2              
3 3     3   213050 use strict 'vars';
  3         32  
  3         99  
4 3     3   17 use File::Path ();
  3         6  
  3         40  
5 3     3   1391 use inc::Module::Install ();
  3         7  
  3         97  
6              
7 3     3   19 use vars qw{$VERSION @ISA};
  3         6  
  3         185  
8             BEGIN {
9 3     3   11 $VERSION = '1.21';
10 3         284 @ISA = 'Module::Install';
11             }
12              
13             =pod
14              
15             =head1 NAME
16              
17             Module::Install::Admin - Author-side manager for Module::Install
18              
19             =head1 SYNOPSIS
20              
21             In a B<Module::Install> extension module:
22              
23             sub extension_method {
24             my $self = shift;
25             $self->admin->some_method(@args);
26             }
27              
28             As an one-liner:
29              
30             % perl "-MModule::Install::Admin" -e'&some_method(@args);'
31              
32             The two snippets above are really shorthands for
33              
34             $some_obj->some_method(@args)
35              
36             where C<$some_obj> is the singleton object of a class under the
37             C<Module::Install::Admin::*> namespace that provides the method
38             C<some_method>. See L</METHODS> for a list of built-in methods.
39              
40             =head1 DESCRIPTION
41              
42             This module implements the internal mechanism for initializing,
43             including and managing extensions, and should only be of interest to
44             extension developers; it is I<never> included under a distribution's
45             F<inc/> directory, nor are any of the B<Module::Install::Admin::*>
46             extensions.
47              
48             For normal usage of B<Module::Install>, please see L<Module::Install>
49             and L<Module::Install/"COOKBOOK / EXAMPLES"> instead.
50              
51             =head2 Bootstrapping
52              
53             When someone runs a F<Makefile.PL> that has C<use inc::Module::Install>,
54             and there is no F<inc/> in the current directory, B<Module::Install>
55             will load this module bootstrap itself, through the steps below:
56              
57             =over 4
58              
59             =item *
60              
61             First, F<Module/Install.pm> is POD-stripped and copied from C<@INC> to
62             F<inc/>. This should only happen on the author's side, never on the
63             end-user side.
64              
65             =item *
66              
67             Reload F<inc/Module/Install.pm> if the current file is somewhere else.
68             This ensures that the included version of F<inc/Module/Install.pm> is
69             always preferred over the installed version.
70              
71             =item *
72              
73             Look at F<inc/Module/Install/*.pm> and load all of them.
74              
75             =item *
76              
77             Set up a C<main::AUTOLOAD> function to delegate missing function calls
78             to C<Module::Install::Admin::load> -- again, this should only happen
79             at the author's side.
80              
81             =item *
82              
83             Provide a C<Module::Install::purge_self> function for removing included
84             files under F<inc/>.
85              
86             =back
87              
88             =head1 METHODS
89              
90             =cut
91              
92             sub import {
93 2     2   20 my $class = shift;
94 2         8 my $self = $class->new( _top => Module::Install->new, @_ );
95 2         11 local $^W;
96 2         81 *{caller(0) . "::AUTOLOAD"} = sub {
97 3     3   23 no strict 'vars';
  3         7  
  3         4812  
98 0 0   0   0 $AUTOLOAD =~ /([^:]+)$/ or die "Cannot load";
99 0 0       0 return if uc($1) eq $1;
100 0 0       0 my $obj = $self->load($1) or return;
101 0         0 unshift @_, $obj;
102 0         0 goto &{$obj->can($1)};
  0         0  
103 2         11 };
104             }
105              
106             sub new {
107 2     2 0 9 my ($class, %args) = @_;
108             return $class->SUPER::new(
109 2         4 %{$args{_top}}, %args,
  2         22  
110             extensions => undef,
111             pathnames => undef,
112             );
113             }
114              
115             sub init {
116 0     0 0 0 my $self = shift;
117 0         0 $self->copy($INC{"$self->{path}.pm"} => $self->{file});
118              
119 0 0       0 unless ( grep { $_ eq $self->{prefix} } @INC ) {
  0         0  
120 0         0 unshift @INC, $self->{prefix};
121             }
122 0         0 delete $INC{"$self->{path}.pm"};
123              
124 0         0 local $^W;
125 0         0 do "$self->{path}.pm";
126             }
127              
128             sub copy {
129 1     1 0 6807 my ($self, $from, $to) = @_;
130              
131 1         5 my @parts = split('/', $to);
132 1 50       5 File::Path::mkpath([ join('/', @parts[ 0 .. $#parts-1 ])])
133             if @parts > 1;
134              
135 1         6 chomp $to;
136              
137 1         2 local ($_);
138 1 50       42 open my $FROM, "<", $from or die "Can't open $from for input:\n$!";
139 1 50       68 open my $TO, ">", $to or die "Can't open $to for output:\n$!";
140 1         5 binmode $FROM;
141 1         2 binmode $TO;
142 1         5 print $TO "#line 1\n";
143              
144 1         3 my $content;
145             my $in_pod;
146              
147 1         26 while ( <$FROM> ) {
148 3 50 33     16 if ( /^=(?:b(?:egin|ack)|head\d|(?:po|en)d|item|(?:ove|fo)r)/ ) {
    50          
    50          
149 0         0 $in_pod = 1;
150             } elsif ( /^=cut\s*\z/ and $in_pod ) {
151 0         0 $in_pod = 0;
152 0         0 print $TO "#line $.\n";
153             } elsif ( ! $in_pod ) {
154 3         19 print $TO $_;
155             }
156             }
157              
158 1 50       15 close $FROM or die "Can't close $from for input:\n$!";
159 1 50       35 close $TO or die "Can't close $to for output:\n$!";
160              
161 1         67 print "include $to\n";
162             }
163              
164             # scan through our target to find
165             sub load_all_extensions {
166 0     0 0   my $self = shift;
167 0 0         unless ($self->{extensions}) {
168 0           $self->{extensions} = [];
169 0           foreach my $inc (@INC) {
170 0 0 0       next if ref($inc) or $inc eq $self->{prefix};
171 0           $self->load_extensions("$inc/$self->{path}", $self->{_top});
172             }
173             }
174 0           return @{$self->{extensions}};
  0            
175             }
176              
177             sub load {
178 0     0 0   my ($self, $method, $copy) = @_;
179              
180 0           my @extobj;
181 0           foreach my $obj ($self->load_all_extensions) {
182 0 0         next unless defined &{ref($obj)."::$method"};
  0            
183 0           my $is_admin = (ref($obj) =~ /^\Q$self->{name}::$self->{dispatch}::/);
184             # Don't ever include admin modules, and vice versa.
185             # $copy = 0 if $XXX and $is_admin;
186 0 0 0       push @extobj, $obj if $copy xor $is_admin;
187             }
188 0 0         unless ( @extobj ) {
189 0           die "Cannot find an extension with method '$method'";
190             }
191              
192             # XXX - do we need to reload $obj from the new location?
193 0           my $obj = $self->pick($method, \@extobj);
194 0 0         $self->copy_package(ref($obj)) if $copy;
195              
196 0           return $obj;
197             }
198              
199             # Copy a package to inc/, with its @ISA tree. $pathname is optional.
200             sub copy_package {
201 0     0 0   my ($self, $pkg, $pathname) = @_;
202 0 0 0       return unless ($pathname ||= $self->{pathnames}{$pkg});
203              
204 0           my $file = $pkg; $file =~ s!::!/!g;
  0            
205 0           $file = "$self->{prefix}/$file.pm";
206 0 0         return if -f $file; # prevents infinite recursion
207              
208 0           $self->copy($pathname => $file);
209 0           foreach my $pkg (@{"$pkg\::ISA"}) {
  0            
210 0           $self->copy_package($pkg);
211             }
212             }
213              
214             sub pick {
215             # determine which name to load
216 0     0 0   my ($self, $method, $objects) = @_;
217              
218             # XXX this whole thing needs to be discussed
219 0 0 0       return $objects->[0] unless $#{$objects} > 0 and -t STDIN;
  0            
220              
221             # sort by last modified time
222 0           @$objects = map { $_->[0] }
223 0           sort { $a->[1] <=> $b->[1] }
224 0           map { [ $_ => -M $self->{pathnames}{ref($_)} ] } @$objects;
  0            
225              
226 0           print "Multiple extensions found for method '$method':\n";
227 0           foreach my $i ( 1 .. @$objects ) {
228 0           print "\t$i. ", ref($objects->[$i-1]), "\n";
229             }
230              
231 0           while ( 1 ) {
232 0           print "Please select one [1]: ";
233 0           chomp(my $choice = <STDIN>);
234 0   0       $choice ||= 1;
235 0 0 0       return $objects->[$choice-1] if $choice > 0 and $choice <= @$objects;
236 0           print "Invalid choice. ";
237             }
238             }
239              
240             sub delete_package {
241 0     0 0   my ($self, $pkg) = @_;
242              
243             # expand to full symbol table name if needed
244 0 0         unless ( $pkg =~ /^main::.*::$/ ) {
245 0 0         $pkg = "main$pkg" if $pkg =~ /^::/;
246 0 0         $pkg = "main::$pkg" unless $pkg =~ /^main::/;
247 0 0         $pkg .= '::' unless $pkg =~ /::$/;
248             }
249              
250 0           my($stem, $leaf) = $pkg =~ m/(.*::)(\w+::)$/;
251 0           my $stem_symtab = *{$stem}{HASH};
  0            
252 0 0 0       return unless defined $stem_symtab and exists $stem_symtab->{$leaf};
253              
254             # free all the symbols in the package
255 0           my $leaf_symtab = *{$stem_symtab->{$leaf}}{HASH};
  0            
256 0           foreach my $name (keys %$leaf_symtab) {
257 0 0         next if $name eq "$self->{dispatch}::";
258 0           undef *{$pkg . $name};
  0            
259             }
260              
261             # delete the symbol table
262 0           foreach my $name (keys %$leaf_symtab) {
263 0 0         next if $name eq "$self->{dispatch}::";
264 0           delete $leaf_symtab->{$name};
265             }
266             }
267              
268             sub AUTOLOAD {
269 0     0     goto &{shift->autoload};
  0            
270             }
271              
272       0     sub DESTROY { }
273              
274             1;
275              
276             __END__
277              
278             =pod
279              
280             =head1 SEE ALSO
281              
282             L<Module::Install>
283              
284             =head1 AUTHORS
285              
286             Audrey Tang E<lt>autrijus@autrijus.orgE<gt>
287              
288             =head1 COPYRIGHT
289              
290             Copyright 2003, 2004 by Audrey Tang E<lt>autrijus@autrijus.orgE<gt>.
291              
292             This program is free software; you can redistribute it and/or modify it
293             under the same terms as Perl itself.
294              
295             See L<http://www.perl.com/perl/misc/Artistic.html>
296              
297             =cut