File Coverage

lib/Fry/Lib/CPANPLUS.pm
Criterion Covered Total %
statement 9 11 81.8
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 13 15 86.6


line stmt bran cond sub pod time code
1             package Fry::Lib::CPANPLUS;
2 1     1   970 use strict;
  1         2  
  1         53  
3             BEGIN {
4 1     1   6 use vars qw(@ISA $VERSION);
  1         2  
  1         72  
5 1     1   26 @ISA = qw/CPANPLUS::Shell::_Base/;
6 1         18 $VERSION = '0.01';
7             }
8             #our @ISA = qw/CPANPLUS::Shell::_Base/;
9              
10 1     1   481 use CPANPLUS::Shell ();
  0            
  0            
11             use CPANPLUS::Backend;
12             use CPANPLUS::I18N;
13             use CPANPLUS::Tools::Term;
14             use CPANPLUS::Tools::Check qw[check];
15              
16             use Cwd;
17             use Data::Dumper;
18             my ($cpan,$cp,@module_list,@author_list);
19             my $basic_format = "%5s %-55s %8s %-10s\n";
20              
21             sub _default_data {
22             return {
23             cmds=>{
24             cpanHelp=>{qw/a ch/,d=>'show cpanplus style help for cpanplus functions'},
25             reloadIndices=>{qw/a cx/,d=>'reload CPAN indices'},
26             writeBundleFile=>{qw/a cb/,d=>'write a bundle file for your configuration'},
27             displayLastSearch=>{qw/a cw/,d=>'display the result of your last search again'},
28             listModulesToUpdate=>{qw/a co arg @module/,
29             d=>'list installed module(s) that aren\'t up to date'},
30             testModules=>{qw/a ct arg @module/,d=>'test module(s)'},
31             installModules=>{qw/a ci arg @module/,d=>'install module(s)' },
32             downloadModules=>{qw/a cd arg @module/,d=>'download module(s)'},
33             moduleDetails=>{qw/a cl arg @module/,d=>'display detailed information about module(s)'},
34             checkReports=>{qw/a cc arg @module/,d=>'check for module report(s) from cpan-testers'},
35             readMe=>{qw/a cr arg @module/,d=>'display README files of module(s)'},
36             authorDistros=>{qw/a cf arg @author/,d=>'list all distributions by author(s)'},
37             uninstallModules=>{qw/a cu arg @module/,d=>'uninstall module(s)'},
38             expandINC=>{qw/a ce arg $dir/,d=>'add directories to your @INC'},
39             promptInModule=>{qw/a cz arg @module/,
40             d=>'extract module(s) and open command prompt in it' },
41             searchModulesbyAuthor=>{qw/a ca arg @author/,d=>'search by author(s)'},
42             searchModulesbyModule=>{qw/a cm arg @module/,d=>'search by module(s)'},
43             setConfigOptions=>{qw/a cs arg $cplus_option/,d=>'set configuration options for this session'},
44             changeOrSaveConfiguration=>{qw/a cS/,
45             d=>'reconfigure settings / save settings'},
46             printErrorStack=>{qw/a cp/,d=>'print the error stack (optionally to a file)'},
47             listInstalled=>{qw/a cli/,d=>'determines if module(s) are installed'},
48             listInstalledRegex=>{qw/a clir/,d=>'returns installed modules matching regexp'},
49             listAuthorsRegex=>{qw/a car/,d=>'returns authors matching regexp'},
50             }
51             }
52             }
53             #obj: $cpan,$mod,$auth,$conf,$status
54             sub _initLib {
55             $cpan = new CPANPLUS::Backend;
56             }
57              
58             #commands
59             sub cpanHelp {
60             my $o = shift;
61             my $output;
62             $output .= "[General]\n";
63             $output .= $o->printHelpAttr(qw/cpanHelp quit/);
64              
65             $output .= "[Search]\n";
66             $output .= $o->printHelpAttr(qw/searchModulesbyAuthor
67             searchModulesbyModule authorDistros listModulesToUpdate displayLastSearch/);
68              
69             $output .= "[Operations]\n";
70             $output .= $o->printHelpAttr(qw/installModules testModules
71             uninstallModules downloadModules moduleDetails readMe checkReports
72             promptInModule/);
73              
74             $output .= "[Local Administration]\n";
75             $output .= $o->printHelpAttr(qw/expandINC writeBundleFile setConfigOptions
76             changeOrSaveConfiguration perlExe printErrorStack reloadIndices/);
77             $output .= "[New]\n";
78             $output .= $o->printHelpAttr(qw/listAuthorsRegex listInstalledRegex
79             listInstalled/);
80              
81             $o->view($output);
82             }
83             #sub blah { $cpan->error_object->trap(error=>"@_"); }
84             sub displayLastSearch {
85             my ($o,@input) = shift;
86             my $output;
87             my $title = (defined @{$o->Var('lines')})
88             ? (loc("Here is a listing of your previous search result:"). "\n")
89             : (loc("No search was done yet."). "\n");
90             $output .= $title;
91              
92             my $i;
93             for my $obj (@{$o->Var('lines')}) {
94              
95             my $fmt_version = $o->_format_version( version => $obj->version );
96             $output .= sprintf $basic_format, ($i+1), ($obj->module, $fmt_version, $obj->author);
97             $i++;
98             }
99             $o->view($output);
100             }
101             ##$cpan
102             sub reloadIndices {
103             my ($o,@arg) = @_;
104              
105             $o->view(loc("Fetching new indices and rebuilding the module tree"), "\n");
106             $o->view(loc("This may take a while..."), "\n");
107              
108             #$cpan->reload_indices(update_source => 0);#, %$options);
109             $cpan->reload_indices(@arg);#, %$options);
110             }
111             sub writeBundleFile {
112             my $o = shift;
113             $o->view(loc(qq[Writing bundle file... This may take a while\n]));
114             my $rv = $cpan->autobundle;
115              
116             $o->view(($rv->ok)
117             ? loc( qq[\nWrote autobundle to %1\n], $rv->rv )
118             : loc( qq[\nCould not create autobundle\n] )
119             );
120             }
121             sub testModules { shift->testInstall('test',@_) }
122             sub installModules { shift->testInstall('install',@_) }
123             sub testInstall {
124             my ($o,$target,@modules) = @_;
125             my $prompt = ($target eq "install") ? loc('Installing') : loc('Testing');
126             $o->_statusMessages($prompt,@modules);
127              
128             ### try to install them, get the return status back
129             my $href = $cpan->install( target => $target, modules => [ @modules ],);
130             my $status = $href->rv;
131              
132             #view
133             for my $key ( sort keys %$status ) {
134              
135             $o->view( $status->{$key}
136             ? (loc("Successfully %tense(%1,past) %2", $target, $key), "\n")
137             : (loc("Error %tense(%1,present) %2", $target, $key), "\n" )
138             );
139             }
140            
141             my $flag;
142             for ( @modules ) {
143             $flag++ unless ref $href->rv && $href->rv->{$_}
144             }
145            
146             if( $href->ok and !$flag ) {
147             $o->view(loc("All modules %tense(%1,past) successfully", $target), "\n");
148             } else {
149             $o->view(loc("Problem %tense(%1,present) one or more modules", $target), "\n");
150             #td?: if error stack implemented
151             #$o->_warn(loc("*** You can view the complete error buffer by pressing '%1' ***\n", 'p'))
152             #unless $cpan->configure_object->get_conf('verbose');
153             }
154             }
155             sub listInstalled {
156             my ($o,@modules) = @_;
157             our $modtree = $cpan->module_tree();
158              
159             $o->_statusMessages ('Checking',@modules);
160             my $res = $cpan->installed(modules => @modules ? \@modules : undef );
161              
162             my @installed_modules = sort keys %{$res->rv};
163             $o->saveArray(@installed_modules) if ($o->Flag('menu'));
164             $o->view("Of the given modules, the following are installed:\n\n");
165             return @installed_modules;
166             }
167             sub listInstalledRegex {
168             my ($o,$regex) = @_;
169             if (@_ < 2) { $o->view("No regexp passed\n.")}
170             else {
171             my @modules = grep(/$regex/,sort $o->listInstalled);
172             $o->saveArray(@modules) if ($o->Flag('menu'));
173             return @modules;
174             }
175             }
176             sub listAuthorsRegex {
177             my ($o,$regex) = @_;
178             if (@_ < 2) { $o->view("No regexp passed\n.")}
179             else {
180             my @modules = grep(/$regex/,sort $o->authorList);
181             $o->saveArray(@modules) if ($o->Flag('menu'));
182             return @modules;
183             }
184             }
185             #?:worth implementing
186             #sub listModulesRegex {
187             sub listModulesToUpdate {
188             my ($o,@modules) = @_;
189             our $modtree = $cpan->module_tree();
190              
191             $o->_statusMessages ('Checking',@modules);
192              
193             #default behavior changed: no input is allowed
194             #if ("@modules" eq '') { $o->view(loc("No modules to check."), "\n"); return; }
195              
196             my @cache = $o->modulesToUpdate(@modules);
197             return if (! defined @cache);
198              
199             #view
200             if (@cache == 0) { $o->view( loc("All module(s) up to date."), "\n") }
201             else {
202              
203             my $output;
204             ### pretty print some information about the search
205             for (0 .. scalar(@cache)-1) {
206              
207             my ($module,$oldversion,$version, $author) = @{$cache[$_]}{qw/module old_version version author/};
208              
209             my $have = $o->_format_version( version => $oldversion); #$res->{$module}->{version} );
210             my $can = $o->_format_version( version => $version );
211              
212             my $local_format = "%5s %10s %10s %-40s %-10s\n";
213              
214             $output .= sprintf $local_format, ($_ +1), ($have, $can, $module, $author);
215             }
216              
217             $o->view($output);
218             }
219             $o->saveArray(@cache);
220             }
221             sub downloadModules {
222             my ($o,@modules) = @_;
223             $o->_statusMessages('Fetching',@modules);
224              
225             ### get the result of our fetch... we store the modules in whatever
226             ### dir the shell was invoked in.
227             my $href = $cpan->fetch(
228             fetchdir => $cpan->configure_object->_get_build('startdir'),
229             modules => [ @modules],
230             );
231             my $status = $href->rv;
232              
233             #view
234             my $output;
235             for my $key ( sort keys %$status ) {
236             $output .= ($status->{$key})
237             ? (loc("Successfully fetched %1", $key). "\n")
238             : (loc("Error fetching %1", $key). "\n")
239             ;
240             }
241              
242             $output .= ($href->ok)
243             ? (loc("All files downloaded successfully"). "\n")
244             : (loc("Problem downloading one or more files"). "\n")
245             ;
246             $o->view($output);
247             }
248             sub checkReports {
249             my ($o,@modules) = @_;
250              
251             ### get the result of our listing...
252             my $res = $cpan->reports(modules => [ @modules] )->rv;
253              
254             #view
255             my $output;
256             foreach my $name (@modules) {
257             my $dist = $cpan->pathname(to => $name);
258             my $url;
259              
260             foreach my $href ($res->{$name} || $res->{$dist}) {
261             $output .= "[$dist]\n";
262              
263             unless ($href) {
264             $output .= loc("No reports available for this distribution."), "\n";
265             next;
266             }
267              
268             foreach my $rv (@{$href}) {
269             $output .= sprintf "%8s %s%s\n", @{$rv}{'grade', 'platform'},
270             ($rv->{details} ? ' (*)' : '');
271             $url ||= $rv->{details} if $rv->{details};
272             }
273             }
274              
275             if ($url) {
276             $url =~ s/#.*//;
277             $output .= "==> $url\n\n";
278             }
279             else { $output .= "\n" }
280             }
281             $o->view($output);
282             }
283             sub moduleDetails {
284             my ($o,@modules) = @_;
285             my $href = $cpan->details(modules => [ @modules ] );
286             my $res = $href->rv;
287              
288             #view
289             my $output;
290             for my $mod ( sort keys %$res ) {
291             unless ( $res->{$mod} ) {
292             $output .= loc("No details for %1 - it's probably outdated.", $mod). "\n";
293             next;
294             }
295              
296             $output .= loc("Details for %1", $mod). "\n";
297             for my $item ( sort keys %{$res->{$mod}} ) {
298             $output.= sprintf "%-30s %-30s\n", $item, $res->{$mod}->{$item}
299             }
300             $output .= "\n";
301             }
302             $o->view($output);
303             }
304             sub authorDistros {
305             my ($o,@modules) = @_;
306             my $cache =[];
307             my $href = $cpan->distributions( authors => [ @modules] );
308             my $res = $href->rv;
309              
310             unless ( $res and keys %$res ) {
311             $o->view(loc("No authors found for your query"), "\n");
312             return;
313             }
314              
315             #view
316             my $output;
317             for my $auth ( sort keys %$res ) {
318             next unless $res->{$auth};
319              
320             my $path = '/'.substr($auth, 0, 1).'/'.substr($auth, 0, 2).'/'.$auth;
321              
322             my $i;
323             for my $dist ( sort keys %{$res->{$auth}} ) {
324             $i++;
325             push @{$cache}, "$path/$dist"; # full path to dist
326              
327             ### pretty print some information about the search
328             $output .= sprintf $basic_format, $i, $dist, $res->{$auth}->{$dist}->{size}, $auth;
329             }
330             }
331             $o->view($output);
332             }
333             sub readMe {
334             my ($o,@modules) = @_;
335             ### also takes multiple arguments, so:
336             ### r POE DBI #works just fine
337             ### alltho you probably shouldn't do that
338              
339             my $href = $cpan->readme( modules => [ @modules ] );
340             my $res = $href->rv;
341              
342             unless ( $res ) { $o->view(loc("No README found for your query"), "\n"); return; }
343              
344             #view
345             my $output;
346             for my $mod ( sort keys %$res ) {
347              
348             unless ($res->{$mod}) {
349             $output .= loc("No README found for %1", $mod). "\n";
350             } else {
351             $output .= $res->{$mod};
352             }
353              
354             $output .= "\n";
355             }
356             $o->view($output);
357             }
358             sub uninstallModules {
359             my ($o,@modules) = @_;
360             $o->_statusMessages('Uninstalling',@modules);
361              
362             my $href = $cpan->uninstall(modules => [ @modules] );
363             my $res = $href->rv;
364              
365             #view
366             my $output;
367             for my $mod ( sort keys %$res ) {
368             $output .= ($res->{$mod})
369             ? (loc("Uninstalled %1 successfully", $mod). "\n")
370             : (loc("Uninstalling %1 failed", $mod). "\n");
371             }
372              
373             $output .= $href->ok
374             ? (loc("All modules uninstalled successfully"). "\n")
375             : (loc("Problem uninstalling one or more modules"). "\n");
376             $o->view($output);
377             }
378             sub expandINC {
379             my ($o,@input) = @_;
380             my $input = "@input";
381             ### e Expands your @INC during runtime...
382             ### e /foo/bar "c:\program files"
383              
384             ### need to fix this so dirs with spaces are allowed ###
385             ### I thought this *was* the fix? -jmb
386             my $rv = $o->_expand_inc(
387             lib => [ $input =~ m/\s*("[^"]+"|'[^']+'|[^\s]+)/g ]
388             );
389             }
390             sub promptInModule {
391             my ($o,@modules) = @_;
392             $o->_statusMessages(loc('Opening shell for module'),@modules);
393              
394             my $conf = $cpan->configure_object;
395             my $shell = $conf->_get_build('shell');
396              
397             unless($shell) {
398             $o->view(loc("Your config does not specify a subshell!"), "\n",
399             loc("Perhaps you need to re-run your setup?"), "\n");
400             return;
401             }
402              
403             my $cwd = cwd();
404              
405             my $output;
406             for my $mod (@modules) {
407             my $answer = $cpan->parse_module(modules => [$mod]);
408             $answer->ok or next;
409              
410             my $mods = $answer->rv;
411             my ($name, $obj) = each %$mods;
412              
413             my $dir = $obj->status->extract;
414              
415             unless( defined $dir ) {
416             $obj->fetch;
417             $dir = $obj->extract();
418             }
419              
420             unless( defined $dir ) {
421             $output .= ("Could not determine where %1 was extracted to", $mod), "\n";
422             next;
423             }
424              
425             unless( chdir $dir ) {
426             $output .= loc("Could not chdir from %1 to %2: %3", $cwd, $dir, $!), "\n";
427             next;
428             }
429              
430             if( system($shell) and $! ) {
431             $output .= loc("Error executing your subshell: %1", $!), "\n";
432             next;
433             }
434              
435             unless( chdir $cwd ) {
436             $output .= loc("Could not chdir back to %1 from %2: %3", $cwd, $dir, $!), "\n";
437             }
438             $o->view($output);
439             }
440             }
441             sub searchModulesbyAuthor { shift->searchModules('author',@_) }
442             sub searchModulesbyModule { shift->searchModules('module',@_) }
443             sub changeOrSaveConfiguration {
444             my ($o,$name) = @_;
445              
446             ### redo setup configuration?
447             if ($name =~ m/^conf/i) { $o->setupConfig; return }
448             elsif ($name =~ m/^save/i) {
449             $cpan->configure_object->save;
450             $o->view(loc("Your CPAN++ configuration info has been saved!"), "\n\n");
451             return;
452             }
453             }
454             sub setConfigOptions {
455             my ($o,$name,$value) = @_;
456             ### perhaps we should go with FULL conf names,
457             ### rather than expanding shortcuts -kane
458              
459             ### allow lazy config options... not smart but possible ###
460             my $conf = $cpan->configure_object;
461             my @options = sort $conf->subtypes('conf');
462             my $realname;
463             for my $option (@options) {
464             if (defined $name and $option =~ m/^$name/) {
465             $realname = $option;
466             last;
467             }
468             }
469              
470             if ($realname) {
471             $o->_set_config(
472             key => $realname,
473             value => $value,
474             method => 'set_conf',
475             );
476             } else {
477             my $output;
478             local $Data::Dumper::Indent = 0;
479             $output .= loc("'%1' is not a valid configuration option!", $name). "\n" if defined $name;
480             $output .= loc("Available options and their current values are:"). "\n";
481              
482             my $local_format = " %-".(sort{$b<=>$a}(map(length, @options)))[0]."s %s\n";
483              
484             foreach my $key (@options) {
485             my $val = $conf->get_conf($key);
486             ($val) = ref($val)
487             ? (Data::Dumper::Dumper($val) =~ /= (.*);$/)
488             : "'$val'";
489             $output .= sprintf $local_format, $key, $val;
490             }
491             $o->view($output);
492             }
493             }
494             sub printErrorStack {
495             my ($o,$file) = @_;
496             my $stack = $cpan->error_object->summarize();
497             $o->_print_stack( stack => $stack, file =>$file);
498             }
499             #tests and completion
500             sub t_module { return 1 }
501             sub t_author { return 1 }
502             sub t_dir { return 1 }
503             sub t_cplus_option { return 1}
504             sub cmpl_cplus_option { return sort $cpan->configure_object->subtypes('conf') }
505             sub cmpl_author { return sort $_[0]->authorList }
506             sub cmpl_module { return sort $_[0]->moduleList }
507             ##Internals
508             sub moduleList {
509             (defined @module_list) ? print "yay\n" : print "doh\n";
510             return (defined @module_list) ? @module_list : keys %{$cpan->module_tree()}
511             }
512             sub authorList {
513             return (@author_list) ? @author_list : keys %{$cpan->author_tree()}
514             }
515             sub searchModules {
516             my ($o,$type,@input) = @_;
517              
518             ### build regexes.. this will break in anything pre 5.005_XX
519             ### we add the /i flag here for case insensitive searches
520             my @regexps = map { "(?i:$_)" } @input;
521              
522             my $res = $cpan->search( type =>$type, list => [ @regexps ]);
523              
524             ### if we got a result back....
525             if ( $res and keys %{$res} ) {
526             ### forget old searches...
527             my $cache = [];
528              
529             ### store them in our $cache; it's the storage for searches
530             ### in Shell.pm
531             for my $k ( sort keys %{$res} ) {
532             push @{$cache}, $res->{$k};
533             }
534              
535             #view
536             my $output;
537             ### pretty print some information about the search
538             for (0 .. scalar(@$cache) -1 ) {
539             my ($module, $version, $author) =
540             @{$cache->[$_]}{qw/module version author/};
541              
542             my $fmt_version = $o->_format_version( version => $version );
543              
544             $output .= sprintf $basic_format, ($_+1), ($module, $fmt_version, $author);
545             }
546              
547             $o->saveArray(@$cache);
548             $o->view($output);
549             } else {
550             $o->view(loc("Your search generated no results"), "\n");
551             return;
552             }
553             }
554             #for &_set_config
555             sub backend {return $cpan }
556             sub setupConfig {
557             my $o = shift;
558             CPANPLUS::Configure::Setup->init(
559             conf => $cpan->configure_object,
560             #term => $self->term,
561             backend => $cpan,
562             );
563             }
564             sub modulesToUpdate {
565             #d: returns slightly modified module object with old_version attribute
566             my ($o,@input) = @_;
567             my $long;# = 1;
568             our $modtree;
569              
570             my $inst = $cpan->installed(modules => @input ? \@input : undef );
571              
572             if(! $inst->rv or (!$inst->ok && defined @input) ) {
573             $o->view(loc("Could not find installation files for all the modules"), "\n");
574             return undef;
575             }
576             my $href = $cpan->uptodate( modules => [sort keys %{$inst->rv}] );
577              
578             my $res = $href->rv;
579             my $cache = [];
580              
581             ### keep a cache by default ###
582             my $seen = {};
583              
584             for my $name ( sort keys %$res ) {
585             next unless $res->{$name}->{uptodate} eq '0';
586              
587             ### dont list more than one module belonging to a package
588             ### blame H. Merijn Brand... -kane
589             my $pkg = $modtree->{$name}->package;
590              
591             if ( $long or !$seen->{$pkg}++ ) {
592             push @{$cache}, $modtree->{$name};
593             #return slightly modified author object
594             $cache->[-1]{old_version} = $res->{$name}{version}
595             }
596             }
597             return @$cache;
598             }
599             #simple replacement for &_select_modules which only
600             #prints command status messages
601             sub _statusMessages {
602             my ($o,$prompt,@input) = @_;
603             my $output;
604              
605             for (@input) { $output .= "$prompt: $_\n" }
606             $o->view($output);
607             }
608             sub _format_version {
609             my $self = shift;
610             my %hash = @_;
611              
612             my $tmpl = {
613             version => { default => 0 }
614             };
615              
616             my $args = check( $tmpl, \%hash ) or return undef;
617             my $version = $args->{version};
618              
619             ### fudge $version into the 'optimal' format
620             $version = sprintf('%3.4f', $version);
621             $version = '' if $version == '0.00';
622              
623             ### do we have to use $&? speed hit all over the module =/ --kane
624             $version =~ s/(00?)$/' ' x (length $&)/e;
625              
626             return $version;
627             }
628             ### add dirs to the @INC at runtime ###
629             sub _expand_inc {
630             my $o = shift;
631             my %args = @_;
632             #my $err = $self->{_error};
633              
634             for my $lib ( @{$args{'lib'}} ) {
635             push @INC, $lib;
636             $o->view( qq[Added $lib to your \@INC\n]);
637             }
638             return 1;
639             }
640             sub printHelpAttr {
641             my ($o,@cmds) = @_;
642             my $format = "%5s %-15s ... # %-40s\n";
643             my $output;
644             for (@cmds) {
645             my $cmd = $o->cmdObj($_) || next;
646             $output .= sprintf $format,@{$cmd}{qw/a arg d/} ;
647             }
648             return $output;
649             }
650              
651             ### dumps a message stack
652             sub _print_stack {
653             my $o = shift;
654             my %hash = @_;
655              
656             my $tmpl = {
657             stack => { required => 1 },
658             file => { default => '' },
659             };
660              
661             my $args = check( $tmpl, \%hash ) or return undef;
662              
663             my $stack = $args->{'stack'};
664             my $file = $args->{'file'};
665              
666             if ($file) {
667             $o->View->file($file);
668             } else { $o->view(join "\n", @$stack); }
669              
670             $o->view("\n", loc("Stack printed successfully"), "\n");
671             return 1;
672             }
673              
674             1;
675              
676             __END__