File Coverage

lib/CPANPLUS/Configure.pm
Criterion Covered Total %
statement 188 209 89.9
branch 48 74 64.8
condition 11 22 50.0
subroutine 23 23 100.0
pod 5 5 100.0
total 275 333 82.5


line stmt bran cond sub pod time code
1             package CPANPLUS::Configure;
2 20     20   1384732 use strict;
  20         253  
  20         668  
3              
4              
5 20     20   7198 use CPANPLUS::Internals::Constants;
  20         69  
  20         6728  
6 20     20   168 use CPANPLUS::Error;
  20         78  
  20         1163  
7 20     20   8088 use CPANPLUS::Config;
  20         104  
  20         1221  
8              
9 20     20   145 use Log::Message;
  20         44  
  20         190  
10 20     20   4450 use Module::Load qw[load];
  20         53  
  20         205  
11 20     20   1485 use Params::Check qw[check];
  20         45  
  20         1050  
12 20     20   130 use File::Basename qw[dirname];
  20         53  
  20         831  
13 20     20   10126 use Module::Loaded ();
  20         14305  
  20         549  
14 20     20   149 use Locale::Maketext::Simple Class => 'CPANPLUS', Style => 'gettext';
  20         46  
  20         195  
15              
16 20     20   5640 use vars qw[$AUTOLOAD $VERSION $MIN_CONFIG_VERSION];
  20         47  
  20         1038  
17 20     20   118 use base qw[CPANPLUS::Internals::Utils];
  20         49  
  20         2539  
18              
19             local $Params::Check::VERBOSE = 1;
20              
21             ### require, avoid circular use ###
22             require CPANPLUS::Internals;
23             $VERSION = "0.9912";
24              
25             ### can't use O::A as we're using our own AUTOLOAD to get to
26             ### the config options.
27             for my $meth ( qw[conf _lib _perl5lib]) {
28 20     20   131 no strict 'refs';
  20         50  
  20         27914  
29              
30             *$meth = sub {
31 3969     3969   6987 my $self = shift;
32 3969 100       9640 $self->{'_'.$meth} = $_[0] if @_;
33 3969         11954 return $self->{'_'.$meth};
34             }
35             }
36              
37              
38             =pod
39              
40             =head1 NAME
41              
42             CPANPLUS::Configure - configuration for CPANPLUS
43              
44             =head1 SYNOPSIS
45              
46             $conf = CPANPLUS::Configure->new( );
47              
48             $bool = $conf->can_save;
49             $bool = $conf->save( $where );
50              
51             @opts = $conf->options( $type );
52              
53             $make = $conf->get_program('make');
54             $verbose = $conf->set_conf( verbose => 1 );
55              
56             =head1 DESCRIPTION
57              
58             This module deals with all the configuration issues for CPANPLUS.
59             Users can use objects created by this module to alter the behaviour
60             of CPANPLUS.
61              
62             Please refer to the C<CPANPLUS::Backend> documentation on how to
63             obtain a C<CPANPLUS::Configure> object.
64              
65             =head1 METHODS
66              
67             =head2 $Configure = CPANPLUS::Configure->new( load_configs => BOOL )
68              
69             This method returns a new object. Normal users will never need to
70             invoke the C<new> method, but instead retrieve the desired object via
71             a method call on a C<CPANPLUS::Backend> object.
72              
73             =over 4
74              
75             =item load_configs
76              
77             Controls whether or not additional user configurations are to be loaded
78             or not. Defaults to C<true>.
79              
80             =back
81              
82             =cut
83              
84             ### store the CPANPLUS::Config object in a closure, so we only
85             ### initialize it once.. otherwise, on a 2nd ->new, settings
86             ### from configs on top of this one will be reset
87             { my $Config;
88              
89             sub new {
90 17     17 1 3095 my $class = shift;
91 17         79 my %hash = @_;
92              
93             ### XXX pass on options to ->init() like rescan?
94 17         41 my ($load);
95 17         102 my $tmpl = {
96             load_configs => { default => 1, store => \$load },
97             };
98              
99 17 50       128 check( $tmpl, \%hash ) or (
100             warn(Params::Check->last_error), return
101             );
102              
103 17   33     1962 $Config ||= CPANPLUS::Config->new;
104 17         58 my $self = bless {}, $class;
105 17         110 $self->conf( $Config );
106              
107             ### you want us to load other configs?
108             ### these can override things in the default config
109 17 100       68 $self->init if $load;
110              
111             ### after processing the config files, check what
112             ### @INC and PERL5LIB are set to.
113 17         133 $self->_lib( \@INC );
114 17         110 $self->_perl5lib( $ENV{'PERL5LIB'} );
115              
116 17         108 return $self;
117             }
118             }
119              
120             =head2 $bool = $Configure->init( [rescan => BOOL])
121              
122             Initialize the configure with other config files than just
123             the default 'CPANPLUS::Config'.
124              
125             Called from C<new()> to load user/system configurations
126              
127             If the C<rescan> option is provided, your disk will be
128             examined again to see if there are new config files that
129             could be read. Defaults to C<false>.
130              
131             Returns true on success, false on failure.
132              
133             =cut
134              
135             ### move the Module::Pluggable detection to runtime, rather
136             ### than compile time, so that a simple 'require CPANPLUS'
137             ### doesn't start running over your filesystem for no good
138             ### reason. Make sure we only do the M::P call once though.
139             ### we use $loaded to mark it
140             { my $loaded;
141             my $warned;
142             sub init {
143 4     4 1 3479 my $self = shift;
144 4         25 my $obj = $self->conf;
145 4         18 my %hash = @_;
146              
147 4         11 my ($rescan);
148 4         24 my $tmpl = {
149             rescan => { default => 0, store => \$rescan },
150             };
151              
152 4 50       24 check( $tmpl, \%hash ) or (
153             warn(Params::Check->last_error), return
154             );
155              
156             ### if the base dir is changed, we have to rescan it
157             ### for any CPANPLUS::Config::* files as well, so keep
158             ### track of it
159 4         373 my $cur_base = $self->get_conf('base');
160              
161             ### warn if we find an old style config specified
162             ### via environment variables
163 4         12 { my $env = ENV_CPANPLUS_CONFIG;
164 4 100 66     25 if( $ENV{$env} and not $warned ) {
165 1         4 $warned++;
166 1         8 error(loc("Specifying a config file in your environment " .
167             "using %1 is obsolete.\nPlease follow the ".
168             "directions outlined in %2 or use the '%3' command\n".
169             "in the default shell to use custom config files.",
170             $env, "CPANPLUS::Configure->save", 's save'));
171             }
172             }
173              
174             { ### make sure that the homedir is included now
175 4         11 local @INC = ( LIB_DIR->($cur_base), @INC );
  4         20  
  4         20  
176              
177             ### only set it up once
178 4 100 100     32 if( !$loaded++ or $rescan ) {
179             ### find plugins & extra configs
180             ### check $home/.cpanplus/lib as well
181 3         1094 require Module::Pluggable;
182              
183 3         18936 Module::Pluggable->import(
184             search_path => ['CPANPLUS::Config'],
185             search_dirs => [ LIB_DIR->($cur_base) ],
186             except => qr/::SUPER$/,
187             sub_name => 'configs'
188             );
189             }
190              
191              
192             ### do system config, user config, rest.. in that order
193             ### apparently, on a 2nd invocation of -->configs, a
194             ### ::ISA::CACHE package can appear.. that's bad...
195 6         24 my %confs = map { $_ => $_ }
196 4         316 grep { $_ !~ /::ISA::/ } __PACKAGE__->configs;
  6         11656  
197 8         21 my @confs = grep { defined }
198 4         15 map { delete $confs{$_} } CONFIG_SYSTEM, CONFIG_USER;
  8         26  
199 4         20 push @confs, sort keys %confs;
200              
201 4         15 for my $plugin ( @confs ) {
202 6         32 msg(loc("Found config '%1'", $plugin),0);
203              
204             ### if we already did this the /last/ time around don't
205             ### run the setup again.
206 6 100       73 if( my $loc = Module::Loaded::is_loaded( $plugin ) ) {
207 3         80 msg(loc(" Already loaded '%1' (%2)", $plugin, $loc), 0);
208 3         30 next;
209             } else {
210 3         83 msg(loc(" Loading config '%1'", $plugin),0);
211              
212 3 50       28 if( eval { load $plugin; 1 } ) {
  3         18  
  3         1066  
213 3         18 msg(loc(" Loaded '%1' (%2)",
214             $plugin, Module::Loaded::is_loaded( $plugin ) ), 0);
215             } else {
216 0         0 error(loc(" Error loading '%1': %2", $plugin, $@));
217             }
218             }
219              
220 3 50       50 if( $@ ) {
221 0         0 error(loc("Could not load '%1': %2", $plugin, $@));
222 0         0 next;
223             }
224              
225 3         38 my $sub = $plugin->can('setup');
226 3 50       18 $sub->( $self ) if $sub;
227             }
228             }
229              
230             ### did one of the plugins change the base dir? then we should
231             ### scan the dirs again
232 4 50       34 if( $cur_base ne $self->get_conf('base') ) {
233 0         0 msg(loc("Base dir changed from '%1' to '%2', rescanning",
234             $cur_base, $self->get_conf('base')), 0);
235 0         0 $self->init( @_, rescan => 1 );
236             }
237              
238             ### clean up the paths once more, just in case
239 4         24 $obj->_clean_up_paths;
240              
241             ### XXX in case the 'lib' param got changed, we need to
242             ### add that now, or it's not propagating ;(
243 4         12 { my $lib = $self->get_conf('lib');
  4         18  
244 4         15 my %inc = map { $_ => $_ } @INC;
  53         128  
245 4         18 for my $l ( @$lib ) {
246 0 0       0 push @INC, $l unless $inc{$l};
247             }
248 4         16 $self->_lib( \@INC );
249             }
250              
251 4         27 return 1;
252             }
253             }
254             =pod
255              
256             =head2 can_save( [$config_location] )
257              
258             Check if we can save the configuration to the specified file.
259             If no file is provided, defaults to your personal config.
260              
261             Returns true if the file can be saved, false otherwise.
262              
263             =cut
264              
265             sub can_save {
266 1     1 1 3 my $self = shift;
267 1   33     4 my $file = shift || CONFIG_USER_FILE->();
268              
269 1 50       27 return 1 unless -e $file;
270              
271 0         0 chmod 0644, $file;
272 0         0 return (-w $file);
273             }
274              
275             =pod
276              
277             =head2 $file = $conf->save( [$package_name] )
278              
279             Saves the configuration to the package name you provided.
280             If this package is not C<CPANPLUS::Config::System>, it will
281             be saved in your C<.cpanplus> directory, otherwise it will
282             be attempted to be saved in the system wide directory.
283              
284             If no argument is provided, it will default to your personal
285             config.
286              
287             Returns the full path to the file if the config was saved,
288             false otherwise.
289              
290             =cut
291              
292             sub _config_pm_to_file {
293 1     1   3 my $self = shift;
294 1 50       4 my $pm = shift or return;
295 1   33     4 my $dir = shift || CONFIG_USER_LIB_DIR->();
296              
297             ### only 3 types of files know: home, system and 'other'
298             ### so figure out where to save them based on their type
299 1         2 my $file;
300 1 50       6 if( $pm eq CONFIG_USER ) {
    50          
301 0         0 $file = CONFIG_USER_FILE->();
302              
303             } elsif ( $pm eq CONFIG_SYSTEM ) {
304 0         0 $file = CONFIG_SYSTEM_FILE->();
305              
306             ### third party file
307             } else {
308 1         3 my $cfg_pkg = CONFIG . '::';
309 1 50       41 unless( $pm =~ /^$cfg_pkg/ ) {
310 0         0 error(loc(
311             "WARNING: Your config package '%1' is not in the '%2' ".
312             "namespace and will not be automatically detected by %3",
313             $pm, $cfg_pkg, 'CPANPLUS'
314             ));
315             }
316              
317 1         14 $file = File::Spec->catfile(
318             $dir,
319             split( '::', $pm )
320             ) . '.pm';
321             }
322              
323 1         6 return $file;
324             }
325              
326              
327             sub save {
328 1     1 1 699 my $self = shift;
329 1   50     7 my $pm = shift || CONFIG_USER;
330 1   50     5 my $savedir = shift || '';
331              
332 1 50       6 my $file = $self->_config_pm_to_file( $pm, $savedir ) or return;
333 1         73 my $dir = dirname( $file );
334              
335 1 50       62 unless( -d $dir ) {
336 1 50       15 $self->_mkdir( dir => $dir ) or (
337             error(loc("Can not create directory '%1' to save config to",$dir)),
338             return
339             )
340             }
341 1 50       9 return unless $self->can_save($file);
342              
343             ### find only accessors that are not private
344 1         9 my @acc = sort grep { $_ !~ /^_/ } $self->conf->ls_accessors;
  6         50  
345              
346             ### for dumping the values
347 20     20   187 use Data::Dumper;
  20         88  
  20         20233  
348              
349 1         3 my @lines;
350 1         5 for my $acc ( @acc ) {
351              
352 2         12 push @lines, "### $acc section", $/;
353              
354 2         9 for my $key ( $self->conf->$acc->ls_accessors ) {
355 43         489 my $val = Dumper( $self->conf->$acc->$key );
356              
357 43         9359 $val =~ s/\$VAR1\s+=\s+//;
358 43         106 $val =~ s/;\n//;
359              
360 43         195 push @lines, '$'. "conf->set_${acc}( $key => $val );", $/;
361             }
362 2         15 push @lines, $/,$/;
363              
364             }
365              
366 1         7 my $str = join '', map { " $_" } @lines;
  94         169  
367              
368             ### use a variable to make sure the pod parser doesn't snag it
369 1         8 my $is = '=';
370 1         46 my $time = gmtime;
371              
372              
373 1         16 my $msg = <<_END_OF_CONFIG_;
374             ###############################################
375             ###
376             ### Configuration structure for $pm
377             ###
378             ###############################################
379              
380             #last changed: $time GMT
381              
382             ### minimal pod, so you can find it with perldoc -l, etc
383             ${is}pod
384              
385             ${is}head1 NAME
386              
387             $pm
388              
389             ${is}head1 DESCRIPTION
390              
391             This is a CPANPLUS configuration file. Editing this
392             config changes the way CPANPLUS will behave
393              
394             ${is}cut
395              
396             package $pm;
397              
398             use strict;
399              
400             sub setup {
401             my \$conf = shift;
402              
403             $str
404              
405             return 1;
406             }
407              
408             1;
409              
410             _END_OF_CONFIG_
411              
412 1 50       51 $self->_move( file => $file, to => "$file~" ) if -f $file;
413              
414 1         18 my $fh = new FileHandle;
415 1 50       71 $fh->open(">$file")
416             or (error(loc("Could not open '%1' for writing: %2", $file, $!)),
417             return );
418              
419 1         170 $fh->print($msg);
420 1         55 $fh->close;
421              
422 1         110 return $file;
423             }
424              
425             =pod
426              
427             =head2 options( type => TYPE )
428              
429             Returns a list of all valid config options given a specific type
430             (like for example C<conf> of C<program>) or false if the type does
431             not exist
432              
433             =cut
434              
435             sub options {
436 6     6 1 3931 my $self = shift;
437 6         20 my $conf = $self->conf;
438 6         24 my %hash = @_;
439              
440 6         9 my $type;
441 6         33 my $tmpl = {
442             type => { required => 1, default => '',
443             strict_type => 1, store => \$type },
444             };
445              
446 6 50       25 check($tmpl, \%hash) or return;
447              
448 6         553 my %seen;
449 63         1151 return sort grep { !$seen{$_}++ }
450 6 50       19 map { $_->$type->ls_accessors if $_->can($type) }
  6         20  
451             $self->conf;
452             }
453              
454             =pod
455              
456             =head1 ACCESSORS
457              
458             Accessors that start with a C<_> are marked private -- regular users
459             should never need to use these.
460              
461             See the C<CPANPLUS::Config> documentation for what items can be
462             set and retrieved.
463              
464             =head2 get_SOMETHING( ITEM, [ITEM, ITEM, ... ] );
465              
466             The C<get_*> style accessors merely retrieves one or more desired
467             config options.
468              
469             =head2 set_SOMETHING( ITEM => VAL, [ITEM => VAL, ITEM => VAL, ... ] );
470              
471             The C<set_*> style accessors set the current value for one
472             or more config options and will return true upon success, false on
473             failure.
474              
475             =head2 add_SOMETHING( ITEM => VAL, [ITEM => VAL, ITEM => VAL, ... ] );
476              
477             The C<add_*> style accessor adds a new key to a config key.
478              
479             Currently, the following accessors exist:
480              
481             =over 4
482              
483             =item set|get_conf
484              
485             Simple configuration directives like verbosity and favourite shell.
486              
487             =item set|get_program
488              
489             Location of helper programs.
490              
491             =item _set|_get_build
492              
493             Locations of where to put what files for CPANPLUS.
494              
495             =item _set|_get_source
496              
497             Locations and names of source files locally.
498              
499             =item _set|_get_mirror
500              
501             Locations and names of source files remotely.
502              
503             =item _set|_get_fetch
504              
505             Special settings pertaining to the fetching of files.
506              
507             =back
508              
509             =cut
510              
511             sub AUTOLOAD {
512 3841     3841   120654 my $self = shift;
513 3841         9413 my $conf = $self->conf;
514              
515 3841         7218 my $name = $AUTOLOAD;
516 3841         22267 $name =~ s/.+:://;
517              
518 3841         24017 my ($private, $action, $field) =
519             $name =~ m/^(_)?((?:[gs]et|add))_([a-z]+)$/;
520              
521 3841         9110 my $type = '';
522 3841 100       9493 $type .= '_' if $private;
523 3841 100       8797 $type .= $field if $field;
524              
525 3841         13113 my $type_code = $conf->can($type);
526 3841 100       68750 unless ( $type_code ) {
527 1         5 error( loc("Invalid method type: '%1'", $name) );
528 1         13 return;
529             }
530 3840         7748 my $type_obj = $type_code->();
531              
532 3840 50       430300 unless( scalar @_ ) {
533 0         0 error( loc("No arguments provided!") );
534 0         0 return;
535             }
536              
537             ### retrieve a current value for an existing key ###
538 3840 100       9809 if( $action eq 'get' ) {
    100          
    50          
539 3500         8006 for my $key (@_) {
540 3500         6294 my @list = ();
541              
542             ### get it from the user config first
543 3500 100 33     8534 if( my $code = $type_obj->can($key) ) {
    50          
544 3499         67456 push @list, $code->();
545              
546             ### XXX EU::AI compatibility hack to provide lookups like in
547             ### cpanplus 0.04x; we renamed ->_get_build('base') to
548             ### ->get_conf('base')
549             } elsif ( $type eq '_build' and $key eq 'base' ) {
550 1         28 return $self->get_conf($key);
551              
552             } else {
553 0         0 error( loc(q[No such key '%1' in field '%2'], $key, $type) );
554 0         0 return;
555             }
556              
557 3499 100       337138 return wantarray ? @list : $list[0];
558             }
559              
560             ### set an existing key to a new value ###
561             } elsif ( $action eq 'set' ) {
562 334         1281 my %args = @_;
563              
564 334         1303 while( my($key,$val) = each %args ) {
565              
566 334 50       939 if( my $code = $type_obj->can($key) ) {
567 334         5248 $code->( $val );
568              
569             } else {
570 0         0 error( loc(q[No such key '%1' in field '%2'], $key, $type) );
571 0         0 return;
572             }
573             }
574              
575 334         33879 return 1;
576              
577             ### add a new key to the config ###
578             } elsif ( $action eq 'add' ) {
579 6         21 my %args = @_;
580              
581 6         28 while( my($key,$val) = each %args ) {
582              
583 6 50       16 if( $type_obj->can($key) ) {
584 0         0 error( loc( q[Key '%1' already exists for field '%2'],
585             $key, $type));
586 0         0 return;
587             } else {
588 6         89 $type_obj->mk_accessors( $key );
589 6         151 $type_obj->$key( $val );
590             }
591             }
592 6         608 return 1;
593              
594             } else {
595              
596 0         0 error( loc(q[Unknown action '%1'], $action) );
597 0         0 return;
598             }
599             }
600              
601 3     3   2880 sub DESTROY { 1 };
602              
603             1;
604              
605             =pod
606              
607             =head1 BUG REPORTS
608              
609             Please report bugs or other issues to E<lt>bug-cpanplus@rt.cpan.org<gt>.
610              
611             =head1 AUTHOR
612              
613             This module by Jos Boumans E<lt>kane@cpan.orgE<gt>.
614              
615             =head1 COPYRIGHT
616              
617             The CPAN++ interface (of which this module is a part of) is copyright (c)
618             2001 - 2007, Jos Boumans E<lt>kane@cpan.orgE<gt>. All rights reserved.
619              
620             This library is free software; you may redistribute and/or modify it
621             under the same terms as Perl itself.
622              
623             =head1 SEE ALSO
624              
625             L<CPANPLUS::Backend>, L<CPANPLUS::Configure::Setup>, L<CPANPLUS::Config>
626              
627             =cut
628              
629             # Local variables:
630             # c-indentation-style: bsd
631             # c-basic-offset: 4
632             # indent-tabs-mode: nil
633             # End:
634             # vim: expandtab shiftwidth=4:
635