File Coverage

blib/lib/App/Chained.pm
Criterion Covered Total %
statement 26 28 92.8
branch n/a
condition n/a
subroutine 10 10 100.0
pod n/a
total 36 38 94.7


line stmt bran cond sub pod time code
1              
2             package App::Chained ;
3              
4 1     1   37136 use strict;
  1         3  
  1         30  
5 1     1   6 use warnings ;
  1         2  
  1         118  
6 1     1   4 use Carp ;
  1         7  
  1         138  
7              
8             BEGIN
9             {
10 1         9 use Sub::Exporter -setup =>
11             {
12             exports => [ qw() ],
13             groups =>
14             {
15             all => [ qw() ],
16             }
17 1     1   924 };
  1         15911  
18            
19 1     1   440 use vars qw ($VERSION);
  1         2  
  1         52  
20 1     1   21 $VERSION = '0.02';
21             }
22              
23             #-------------------------------------------------------------------------------
24              
25 1     1   1199 use English qw( -no_match_vars ) ;
  1         13890  
  1         7  
26              
27 1     1   1348 use Readonly ;
  1         5222  
  1         87  
28             Readonly my $EMPTY_STRING => q{} ;
29             Readonly my $SCALAR => q{} ;
30              
31 1     1   20 use Carp qw(carp croak confess) ;
  1         3  
  1         61  
32 1     1   1011 use List::MoreUtils qw(any none first_index) ;
  0            
  0            
33             use Getopt::Long ;
34            
35              
36             #-------------------------------------------------------------------------------
37              
38             =head1 NAME
39              
40             App::Chained - Wrapper to sub applications in the Git fashion - No modification to your scripts, modules.
41              
42             =head1 SYNOPSIS
43              
44             A complete example can be found in I< test_wrapper.p test_application test_module.pm test_templatel> in the distribution.
45              
46             package App::Chained::Test ;
47             use parent 'App::Chained' ;
48             our $VERSION = '0.03' ;
49            
50             =head1 THIS WRAPPER DOCUMENTATION
51            
52             This will be automatically extracted as we set the B fields to B<\&App::Chained::get_help_from_pod>
53            
54             =cut
55              
56             sub run
57             {
58             my ($invocant, @setup_data) = @_ ;
59            
60             my $chained_app =
61             App::Chained->new
62             (
63             help => \&App::Chained::get_help_from_pod,
64             version => $VERSION,
65             apropos => undef,
66             faq => undef,
67             getopt_data => [] ;
68            
69             sub_apps =>
70             {
71             test_application =>
72             {
73             description => 'executable',
74             run =>
75             sub
76             {
77             my ($self, $command, $arguments) = @_ ;
78             system './test_application ' . join(' ', @{$arguments}) ;
79             },
80             ...
81             },
82             },
83            
84             @setup_data,
85             ) ;
86            
87             bless $chained_app, $class ;
88            
89             $chained_app->parse_command_line() ;
90             $chained_app->SUPER::run() ;
91             }
92            
93             #---------------------------------------------------------------------------------
94            
95             package main ;
96            
97             App::Chained::Test->run(command_line_arguments => \@ARGV) ;
98              
99             =head1 DESCRIPTION
100              
101             This module implements an application front end to other applications. As the B command is a front end
102             to many B sub commands
103              
104             =head1 DOCUMENTATION
105              
106             This module tries to provide the git like front end with the minimum work from you. Your sub commands can be implemented in
107             perl scripts, modules or even applications written in other languages. You will not have to derive your sub commands from a class I define
108             nor will you have to define specific soubrourines/methods in your sub commands. In a word I tried to keep this module as non-intruisive as
109             possible.
110              
111             Putting a front end to height sub applications took a total of 15 minutes plus another 15 minutes when I decided to have a more advanced command
112             completion. More on completion later.
113              
114             =head2 What you gain
115              
116             The Wrapper will handle the following options
117              
118             =over 2
119              
120             =item * --help
121              
122             =item * --apropos
123              
124             =item * --faq
125              
126             =item * --version
127              
128             =item * --generate_bash_completion
129              
130             =back
131              
132             =head3 Defining sub commands/applications
133              
134             sub_apps =>
135             {
136             check => # the name of the sub command, it can be an alias
137             {
138             description => 'does a check', # description
139             run =>
140             sub
141             {
142             # a subroutine reference called to run the sub command
143             # This is a simple wrapper. You don't have to change your modules or scripts
144             # or inherite from any class
145            
146             my ($self, $command, $arguments) = @_ ;
147             system 'your_executable ' . join(' ', @{$arguments}) ;
148             },
149            
150             help => sub {system "your_executable --help"}, # a sub to be run when help required
151             apropos => [qw(verify check error test)], # a list of words to match a user apropos query
152            
153             options => sub{ ...}, # See generate_bash_completion below
154             },
155             ...
156             }
157            
158             =head1 EXAMPLE
159              
160             L (from version 0.02) defines a front end application B to quite a few sub commands. Check the source
161             of the B script for a real life example with sub command completion script.
162              
163             =head1 THIS CLASS USES EXIT!
164              
165             Some of the default handling will result in this module using B to return from the application wrapper. I may remove the B in future
166             versions as I rather dislike the usage of B in module.
167              
168             =head1 SUBROUTINES/METHODS
169              
170             =cut
171              
172              
173             #-------------------------------------------------------------------------------
174              
175             Readonly my $NEW_ARGUMENTS => [qw(NAME INTERACTION help getopt_data sub_apps command_line_arguments version apropos faq usage)] ;
176              
177             sub new
178             {
179              
180             =head2 new(NAMED_ARGUMENT_LIST)
181              
182             Create a App::Chained object, refer to the synopsis for a complete example.
183              
184             I
185              
186             =over 2
187              
188             =item * INTERACTION - Lets you redefine how B displays information to thhe user
189              
190             =item * command_line_arguments - Array reference-
191              
192             =item * help - A sub reference -
193              
194             you can also \&App::Chained::get_help_from_pod if you want your help to be extracted from the pod present in your app. The pod will be displayed
195             by I if present in your system or converted by B.
196              
197             =item * version - A scalar or a Sub reference -
198              
199             =item * apropos - A sub reference -
200              
201             if it is not defined, The apropos fields in the sub commands entries are searched for a match
202              
203             =item * faq - A sub reference - called when the user
204              
205             =item * getopt_data - Ans array reference containing
206              
207             =over 2
208              
209             =item * A string - a Getopt specification
210              
211             =item * A scalar/array/hash/sub reference according to Getop
212              
213             =item * A string - short description
214              
215             =item * A string - long description
216              
217             =back
218              
219             ['an_option|o=s' => \my $option, 'description', 'long description'],
220              
221             =item * sub_apps - A Hash reference - contains a sub command/application definition
222              
223             {
224             check =>
225             {
226             description => 'does a check',
227             run =>
228             sub
229             {
230             my ($self, $command, $arguments) = @_ ;
231             system 'ra_check.pl ' . join(' ', @{$arguments}) ;
232             },
233            
234             help => sub {system "ra_check.pl --help"},
235             apropos => [qw(verify check error test)],
236             options => sub{ ...},
237             },
238             },
239              
240             =back
241              
242             I - An App::Chained object
243              
244             I - Dies if an invalid argument is passed
245              
246             =cut
247              
248             my ($invocant, @setup_data) = @_ ;
249              
250             my $class = ref($invocant) || $invocant ;
251             confess 'Error: Invalid constructor call!' unless defined $class ;
252              
253             my $object = {} ;
254              
255             my ($package, $file_name, $line) = caller() ;
256             bless $object, $class ;
257              
258             $object->Setup($package, $file_name, $line, @setup_data) ;
259              
260             return($object) ;
261             }
262              
263             #-------------------------------------------------------------------------------
264              
265             sub Setup
266             {
267              
268             =head2 [P]Setup
269              
270             Helper sub called by new.
271              
272             =cut
273              
274             my ($self, $package, $file_name, $line, @setup_data) = @_ ;
275              
276             croak "Error: Invalid number of argument '$file_name, $line'." if (@setup_data % 2) ;
277              
278             $self->{INTERACTION}{INFO} ||= sub {print @_} ;
279             $self->{INTERACTION}{WARN} ||= \&Carp::carp ;
280             $self->{INTERACTION}{DIE} ||= \&Carp::croak ;
281             $self->{NAME} = 'Anonymous';
282             $self->{FILE} = $file_name ;
283             $self->{LINE} = $line ;
284              
285             $self->CheckOptionNames($NEW_ARGUMENTS, @setup_data) ;
286              
287             %{$self} =
288             (
289             NAME => 'Anonymous',
290             FILE => $file_name,
291             LINE => $line,
292             @setup_data,
293             ) ;
294              
295             my $location = "$self->{FILE}:$self->{LINE}" ;
296              
297             $self->{INTERACTION}{INFO} ||= sub {print @_} ;
298             $self->{INTERACTION}{WARN} ||= \&Carp::carp ;
299             $self->{INTERACTION}{DIE} ||= \&Carp::confess ;
300              
301             if($self->{VERBOSE})
302             {
303             $self->{INTERACTION}{INFO}('Creating ' . ref($self) . " '$self->{NAME}' at $location.\n") ;
304             }
305              
306             return 1 ;
307             }
308              
309             #-------------------------------------------------------------------------------
310              
311             sub CheckOptionNames
312             {
313              
314             =head2 [P]CheckOptionNames
315              
316             Verifies the named options passed to the members of this class. Calls B<{INTERACTION}{DIE}> in case
317             of error.
318              
319             =cut
320              
321             my ($self, $valid_options, @options) = @_ ;
322              
323             $self->{INTERACTION}{DIE}->('Invalid number of argument!') if (@options % 2) ;
324              
325             if('HASH' eq ref $valid_options)
326             {
327             # OK
328             }
329             elsif('ARRAY' eq ref $valid_options)
330             {
331             $valid_options = {map{$_ => 1} @{$valid_options}} ;
332             }
333             else
334             {
335             $self->{INTERACTION}{DIE}->("Invalid argument '$valid_options'!") ;
336             }
337              
338             my %options = @options ;
339              
340             for my $option_name (keys %options)
341             {
342             unless(exists $valid_options->{$option_name})
343             {
344             $self->{INTERACTION}{DIE}->
345             (
346             "$self->{NAME}: Invalid Option '$option_name' at '$self->{FILE}:$self->{LINE}'\nValid options:\n\t"
347             . join("\n\t", sort keys %{$valid_options}) . "\n"
348             );
349             }
350             }
351              
352             if
353             (
354             (defined $options{FILE} && ! defined $options{LINE})
355             || (!defined $options{FILE} && defined $options{LINE})
356             )
357             {
358             $self->{INTERACTION}{DIE}->("$self->{NAME}: Incomplete option FILE::LINE!") ;
359             }
360              
361             return(1) ;
362             }
363              
364             #-------------------------------------------------------------------------------
365              
366             sub parse_command_line
367             {
368              
369             =head2 [P]parse_command_line()
370              
371             Parses the option passed in the throught the named argument B. It will also handle some
372             of the options directly, eg: --help, --apropos, ...
373              
374             I - None
375              
376             I - Nothing
377              
378             B<$self->{parsed_command}> is set to the command to run.
379              
380             B<$self->{command_options}> is set to the options that are to be passed to the command
381              
382             I -Dies if an invalid command is passed in the options, warns if the options seem incorrect
383              
384             =cut
385              
386             my ($self) = @_ ;
387              
388             my @command_line_arguments = @{$self->{command_line_arguments}} ;
389              
390             if(@command_line_arguments)
391             {
392             local @ARGV = @command_line_arguments ;
393            
394             my @option_definitions = $self->get_options_definitions() ;
395             GetOptions(@option_definitions);
396            
397             my @arguments_left_on_command_line = @ARGV ;
398            
399             my $command = shift @arguments_left_on_command_line ;
400             my $options_ok = defined $command ? $command !~ /^-/sxm : 0 ;
401            
402             if($options_ok)
403             {
404             $self->{parsed_command} = $command ;
405             $self->{command_options} = \@arguments_left_on_command_line ;
406             }
407            
408             # run help, faq apropos, ... even if the command line was wrong
409            
410            
411             if(${$self->{getopt_definitions}{h}} || ${$self->{getopt_definitions}{help}})
412             {
413             if(defined $command)
414             {
415             my $command_index = first_index {/$command/} @{$self->{command_line_arguments}} ;
416             my $help_index = first_index {/-(h|help)/} @{$self->{command_line_arguments}} ;
417            
418             if($command_index < $help_index)
419             {
420             # the --help comes after the command. let the command handle it
421             $self->run_help_command($command) ;
422             exit(0) ;
423             }
424             else
425             {
426             $self->display_help() ;
427             exit(0) ;
428             }
429             }
430             else
431             {
432             $self->display_help() ;
433             exit(0) ;
434             }
435             }
436            
437             if(${$self->{getopt_definitions}{version}})
438             {
439             $self->display_version() ;
440             exit(0) ;
441             }
442            
443             if(${$self->{getopt_definitions}{'apropos=s'}})
444             {
445             $self->display_apropos() ;
446             exit(0) ;
447             }
448            
449             if(${$self->{getopt_definitions}{'faq=s'}})
450             {
451             $self->display_faq() ;
452             exit(0) ;
453             }
454            
455             if($options_ok)
456             {
457             if($command eq 'help')
458             {
459             $self->run_help_command($self->{command_options}[0]) ;
460             exit(0) ;
461             }
462             else
463             {
464             my $sub_apps = $self->{sub_apps} ;
465            
466             if(defined $sub_apps)
467             {
468             unless(exists $sub_apps->{$command})
469             {
470             $self->{INTERACTION}{DIE}("Error: Unrecognized command '$command'\n\n" . $self->get_command_list() . "\n\n") ;
471             }
472             }
473             else
474             {
475             $self->{INTERACTION}{INFO}('No sub applications registred') ;
476             }
477             }
478             }
479             else
480             {
481             if(defined $command)
482             {
483             $self->{INTERACTION}{WARN}("Error: Invalid or incomplete command '$command'\n") ;
484             $self->display_help() ;
485             exit(1) ;
486             }
487             else
488             {
489             $self->display_usage() ;
490             $self->display_command_list() ;
491             }
492             }
493             }
494             else
495             {
496             $self->display_usage() ;
497             $self->display_command_list() ;
498             }
499              
500             return ;
501             }
502              
503             #-------------------------------------------------------------------------------
504              
505             sub get_options_definitions
506             {
507              
508             =head2 [P]get_options_definitions()
509              
510             Generated an option definition suitable for Getopt::Long. Adding default options is necessary. The added
511             option will be added in B<$self->{getopt_data}>.
512              
513             I - None
514              
515             I - a list of tuples
516              
517             =over 2
518              
519             =item * first element is a Getopt::Long option defintion
520              
521             =item * second element is a reference to a scalar (or other type) which will store the option value
522              
523             I - None
524              
525             =cut
526              
527             my ($self) = @_ ;
528              
529             my %option_definitions = ## no critic (BuiltinFunctions::ProhibitComplexMappings)
530             map
531             {
532             my($option_specification, $recipient) = @{$_} ;
533            
534             my ($type) = $option_specification =~ m/(=.)$/sxm ;
535             $type ||= $EMPTY_STRING ;
536            
537             $option_specification =~ s/(=.)$//sxm ;
538            
539             my @options ;
540            
541             for my $option (split /\|/sxm, $option_specification)
542             {
543             push @options, "$option$type" => $recipient ;
544             }
545            
546             @options ;
547             } @{$self->{getopt_data}} ;
548              
549              
550             # add help,version, apropos, faq, ... if necessary
551             for my $default_option
552             (
553             ['h', \my $help],
554             ['help', \my $help_long],
555             ['version', \my $version],
556             ['apropos=s', \my $apropos],
557             ['faq=s', \my $faq],
558             ['generate_bash_completion', sub {$self->generate_bash_completion()}],
559             ['bash', sub {$self->generate_bash_completion()}],
560             )
561             {
562             my ($option_specification, $recipient) = @{$default_option} ;
563            
564             unless (exists $option_definitions{$option_specification})
565             {
566             push @{$self->{getopt_data}}, [$option_specification, $recipient, "App::Chained generated '$option_specification' option", $EMPTY_STRING] ;
567             $option_definitions{$option_specification} = $recipient ;
568             }
569             }
570              
571             $self->{getopt_definitions} = \%option_definitions ;
572              
573             return map {@{$_}[0 .. 1]} @{$self->{getopt_data}} ;
574             }
575              
576             #-------------------------------------------------------------------------------
577              
578             sub display_help
579             {
580              
581             =head2 [P]display_help()
582              
583             Will use B<$self->{help}>, that you set during construction, or will inform you if you haven't set the B field.
584              
585             I - None
586              
587             I - Nothing
588              
589             I - None
590              
591             =cut
592              
593             my ($self) = @_ ;
594              
595             my $help = $self->{help} ;
596              
597             if(defined $help)
598             {
599             if('CODE' eq ref $help)
600             {
601             $help->($self) ;
602             }
603             else
604             {
605             if($SCALAR eq ref $help)
606             {
607             $self->{INTERACTION}{INFO}($help) ;
608             }
609             }
610             }
611             else
612             {
613             my $app = ref($self) ;
614             $self->{INTERACTION}{INFO}("No help defined. Please define one in '$app'.\n\n") ;
615             }
616              
617             return ;
618             }
619              
620             sub get_help_from_pod
621             {
622             use Pod::Text ;
623              
624             open my $fh, '<', $PROGRAM_NAME or die "Can't open '$PROGRAM_NAME': $!\n";
625             open my $out, '>', \my $textified_pod or die "Can't redirect to scalar output: $!\n";
626              
627             Pod::Text->new (alt => 1, sentence => 0, width => 78)->parse_from_filehandle($fh, $out) ;
628              
629             print $textified_pod ;
630              
631             exit(1) ;
632             }
633              
634             #-------------------------------------------------------------------------------
635              
636             sub display_usage
637             {
638              
639             =head2 [P]display_usage()
640              
641             Will use B<$self->{usage}>, that you set during construction, or will inform you if you haven't set the B field.
642              
643             I - None
644              
645             I - Nothing
646              
647             I - None
648              
649             =cut
650              
651             my ($self) = @_ ;
652              
653             my $usage = $self->{usage} ;
654              
655             if(defined $usage)
656             {
657             if('CODE' eq ref $usage)
658             {
659             $usage->($self) ;
660             }
661             else
662             {
663             if($SCALAR eq ref $usage)
664             {
665             $self->{INTERACTION}{INFO}($usage) ;
666             }
667             }
668             }
669             else
670             {
671             my $app = ref($self) ;
672             $self->{INTERACTION}{WARN}("No usage example. Please define one in '$app'.\n\n") ;
673             }
674              
675             return ;
676             }
677              
678             #-------------------------------------------------------------------------------
679              
680             sub display_command_list
681             {
682              
683             =head2 [P]display_command_list()
684              
685             Will display the list of the sub commands.
686              
687             I - None
688              
689             I - Nothing
690              
691             I - None
692              
693             =cut
694              
695             my ($self) = @_ ;
696              
697             my $commands = $self->get_command_list() ;
698              
699             $self->{INTERACTION}{INFO}($commands) ;
700              
701             return ;
702             }
703              
704              
705             sub get_command_list
706             {
707              
708             =head2 [P]get_command_list()
709              
710             I - None
711              
712             I - A string - the list of sub commands
713              
714             I - None
715              
716             =cut
717              
718             my ($self) = @_ ;
719              
720             my $sub_apps = $self->{sub_apps} ;
721              
722             my $commands = $EMPTY_STRING ;
723              
724             if(defined $sub_apps)
725             {
726             $commands = "Available commands are:\n" ;
727            
728             for my $sub_app_name (sort keys %{$sub_apps})
729             {
730             $commands .= sprintf ' %-25.25s ', $sub_app_name ;
731             $commands .= $sub_apps->{$sub_app_name}{description} || 'no description!.' ;
732             $commands .= "\n" ;
733             }
734             }
735             else
736             {
737             $commands = 'No commands registred' ;
738             }
739              
740             return $commands ;
741             }
742              
743             #-------------------------------------------------------------------------------
744              
745             sub run_help_command
746             {
747              
748             =head2 [P]run_help_command(NAMED_ARGUMENT_LIST)
749              
750             Handle the B command. It will display help for the sub command or for the application if none is given.
751              
752             $> my_app help sub_command
753              
754             I - None
755              
756             I - Nothing
757              
758             I Dies if a wrong sub command name is used or if the sub command doesn't define a B sub
759              
760             =cut
761              
762             my ($self, $command ) = @_ ;
763              
764             return unless defined $self->{parsed_command} ;
765              
766             if(defined $command)
767             {
768             my $sub_app = $self->{sub_apps}{$command} ;
769              
770             if(defined $sub_app)
771             {
772             if(exists $sub_app->{help})
773             {
774             if('CODE' eq ref($sub_app->{help}))
775             {
776             $sub_app->{help}($self, $sub_app) ;
777             }
778             else
779             {
780             $self->{INTERACTION}{DIE}->("Error: sub app '$self->{parsed_command}' help subroutine is not a code reference.") ;
781             }
782             }
783             else
784             {
785             $self->{INTERACTION}{DIE}->('Error: sub app does not defined a \'help\' subroutine.') ;
786             #~ run man page
787             }
788             }
789             else
790             {
791             $self->{INTERACTION}{DIE}->("Error: No such command '$command'." . $self->get_command_list() ) ;
792             }
793             }
794             else
795             {
796             $self->display_help() ;
797             exit(0) ;
798             }
799            
800             return ;
801             }
802              
803             #-------------------------------------------------------------------------------
804              
805             sub run
806             {
807              
808             =head2 [P]run()
809              
810             Runs the sub command parsed on the command line.
811              
812             I - None
813              
814             I - Nothing
815              
816             I Dies if the sub command B field is improperly set.
817              
818             =cut
819              
820             my ($self) = @_ ;
821              
822             return unless defined $self->{parsed_command} ;
823              
824             my $sub_app = $self->{sub_apps}{$self->{parsed_command}} ;
825              
826             if(defined $sub_app->{run})
827             {
828             if('CODE' eq ref($sub_app->{run}))
829             {
830             my @arguments ;
831             @arguments = map {"'$_'"} @{$self->{command_options}} if(defined $self->{command_options}) ;
832            
833             $sub_app->{run}($self, $sub_app, \@arguments) ;
834             }
835             else
836             {
837             $self->{INTERACTION}{DIE}->("Error: sub app '$self->{parsed_command}' run subroutine is not a code reference.") ;
838             }
839             }
840             else
841             {
842             $self->{INTERACTION}{DIE}->("Error: sub app '$self->{parsed_command}' run subroutine is not defined.") ;
843             }
844              
845             return ;
846             }
847              
848             #-------------------------------------------------------------------------------
849              
850             sub generate_bash_completion
851             {
852            
853             =head2 [P]generate_bash_completion()
854              
855             The generated completion is in two parts:
856              
857             A perl script used to generate the completion (output on stdout) and a shell script that you must source (output on stderr).
858              
859             $> my_app -bash 1> my_app_perl_completion.pl 2> my_app_regiter_completion
860              
861             Direction about how to use the completion scritp is contained in the generated script.
862              
863             The completion will work for the top application till a command is input on the command line after that the completion is for the command.
864              
865             =head3 command specific options
866              
867             Your sub commands can define an B field. The field should be set to a subroutine reference that returns a string of options the sub command
868             accepts. The format should be I<-option_name>. One option perl line.
869              
870             Here is an example of how I added completion to a set sub commands (8 of them). The sub commands do not have a completion script
871             and rely on the wrapper for completion.
872              
873             I first set the B field:
874              
875             {
876             description => ...
877             run => ...
878             ...
879            
880             options => sub {return `$name --dump_options`},
881             }
882              
883             I am using the sub command itself to generate the options. This way I don't have to maintain the list by hand (which is possible).
884              
885             Modifying the sub command itself was trivial and very quick. I modified the following code (example in one of thesub commands)
886              
887             die 'Error parsing options!'unless
888             GetOptions
889             (
890             'master_template_file=s' => \$master_template_file,
891             'h|help' => \&display_help,
892             ) ;
893            
894             to be
895              
896             die 'Error parsing options!'unless
897             GetOptions
898             (
899             'master_template_file=s' => \$master_template_file,
900             'h|help' => \&display_help,
901            
902             'dump_options' =>
903             sub
904             {
905             print join "\n", map {"-$_"}
906             qw(
907             master_template_file
908             help
909             ) ;
910             exit(0) ;
911             },
912             ) ;
913              
914             Modfying the height or so scripts took only a few minutes.
915              
916             Noiw I have command completion for all the sub command. Here is an example:
917              
918             nadim@naquadim Arch (master)$ ra show -[tab]
919             -format -include_loaded_from -master_categories_file
920             -help -include_not_found -master_template_file
921             -include_categories -include_statistics -remove_empty_requirement_field_in_categories
922             -include_description_data -include_type -requirement_fields_filter_file
923              
924             The I sub command is two order of magnitude easier to use with completion.
925              
926             I - None
927              
928             I - Nothing - exits with status code B<1> after emitting the completion script on stdout
929              
930             I - None - Exits the program.
931              
932             =cut
933              
934             my ($self) = @_ ;
935              
936             $self->get_options_definitions() ; # generates $self->{getopt_definitions}
937             my @options = map { s/=.$//sxm ; "\t-$_ => 0," } keys %{$self->{getopt_definitions}} ;
938              
939             my @command_options ;
940             my $sub_apps = $self->{sub_apps} ;
941              
942             if(defined $sub_apps)
943             {
944             while(my ($sub_app_name, $sub_app) = each %{$sub_apps})
945             {
946             my @sub_app_options ;
947            
948             if(exists $sub_app->{options} && 'CODE' eq ref($sub_app->{options}))
949             {
950             @sub_app_options= map {chomp ; $_} $sub_app->{options}($self, $sub_app, []) ;
951             }
952            
953             push @command_options, "\t$sub_app_name => [qw(@sub_app_options)]," ;
954             }
955             }
956              
957             use File::Basename ;
958             my ($basename, $path, $ext) = File::Basename::fileparse($PROGRAM_NAME, ('\..*')) ;
959             my $application_name = $basename . $ext ;
960              
961             local $| = 1 ;
962              
963             my $complete_script = <<"COMPLETION_SCRIPT" ;
964              
965             #The perl script has to be executable and somewhere in the path.
966             #This script was generated using used your application name
967              
968             #Add the following line in your I<~/.bashrc> or B them:
969              
970             _${application_name}_perl_completion()
971             {
972             local old_ifs="\${IFS}"
973             local IFS=\$'\\n';
974             COMPREPLY=( \$(${application_name}_perl_completion.pl \${COMP_CWORD} \${COMP_WORDS[\@]}) );
975             IFS="\${old_ifs}"
976              
977             return 1;
978             }
979              
980             complete -o default -F _${application_name}_perl_completion $application_name
981             COMPLETION_SCRIPT
982              
983             print {*STDERR} $complete_script ;
984              
985             print {*STDOUT} <<'COMPLETION_SCRIPT' ;
986             #! /usr/bin/perl
987              
988             =pod
989              
990             I received from bash:
991              
992             =over 2
993              
994             =item * $index - index of the command line argument to complete (starting at '1')
995              
996             =item * $command - a string containing the command name
997              
998             =item * \@argument_list - list of the arguments typed on the command line
999              
1000             =back
1001              
1002             You return possible completion you want separated by I<\n>. Return nothing if you
1003             want the default bash completion to be run which is possible because of the <-o defaul>
1004             passed to the B command.
1005              
1006             Note! You may have to re-run the B command after you modify your perl script.
1007              
1008             =cut
1009              
1010             use strict;
1011             use Tree::Trie;
1012              
1013             my ($argument_index, $command, @arguments) = @ARGV ;
1014              
1015             $argument_index-- ;
1016             my $word_to_complete = $arguments[$argument_index] ;
1017              
1018             my %top_level_completions = # name => takes a file 0/1
1019             (
1020             COMPLETION_SCRIPT
1021              
1022             print {*STDOUT} join("\n", @options) . "\n" ;
1023            
1024             print {*STDOUT} <<'COMPLETION_SCRIPT' ;
1025             ) ;
1026            
1027             my %commands_and_their_options =
1028             (
1029             COMPLETION_SCRIPT
1030              
1031             print {*STDOUT} join("\n", @command_options) . "\n" ;
1032              
1033             print {*STDOUT} <<'COMPLETION_SCRIPT' ;
1034             ) ;
1035            
1036             my @commands = (sort keys %commands_and_their_options) ;
1037             my %commands = map {$_ => 1} @commands ;
1038             my %top_level_completions_taking_file = map {$_ => 1} grep {$top_level_completions{$_}} keys %top_level_completions ;
1039              
1040             my $command_present = 0 ;
1041             for my $argument (@arguments)
1042             {
1043             if(exists $commands{$argument})
1044             {
1045             $command_present = $argument ;
1046             last ;
1047             }
1048             }
1049              
1050             my @completions ;
1051             if($command_present)
1052             {
1053             # complete differently depending on $command_present
1054             push @completions, @{$commands_and_their_options{$command_present}} ;
1055             }
1056             else
1057             {
1058             if(defined $word_to_complete)
1059             {
1060             @completions = (@commands, keys %top_level_completions) ;
1061             }
1062             else
1063             {
1064             @completions = @commands ;
1065             }
1066             }
1067              
1068             if(defined $word_to_complete)
1069             {
1070             my $trie = new Tree::Trie;
1071             $trie->add(@completions) ;
1072              
1073             print join("\n", $trie->lookup($word_to_complete) ) ;
1074             }
1075             else
1076             {
1077             my $last_argument = $arguments[-1] ;
1078            
1079             if(exists $top_level_completions_taking_file{$last_argument})
1080             {
1081             # use bash file completiong or we could pass the files ourselves
1082             #~ use File::Glob qw(bsd_glob) ;
1083             #~ print join "\n", bsd_glob('M*.*') ;
1084             }
1085             else
1086             {
1087             print join("\n", @completions) unless $command_present ;
1088             }
1089             }
1090              
1091             COMPLETION_SCRIPT
1092              
1093             exit(0) ;
1094             }
1095              
1096             #-------------------------------------------------------------------------------
1097              
1098             sub display_version
1099             {
1100              
1101             =head2 [P]display_version()
1102              
1103             Displays the version you set through B<$self->{version}>.
1104              
1105             I - None
1106              
1107             I - Nothing
1108              
1109             I None. Will warn if you forgot to set a version
1110              
1111             See C.
1112              
1113             =cut
1114              
1115             my ($self) = @_ ;
1116              
1117             my $version = $self->{version} ;
1118              
1119             if(defined $version)
1120             {
1121             if('CODE' eq ref $version)
1122             {
1123             $version->($self) ;
1124             }
1125             else
1126             {
1127             if($SCALAR eq ref $version)
1128             {
1129             $version .= "\n" unless $version =~ /\n$/sxm ;
1130            
1131             $self->{INTERACTION}{INFO}($version) ;
1132             }
1133             }
1134             }
1135             else
1136             {
1137             my $app = ref($self) ;
1138             $self->{INTERACTION}{WARN}("No version. Please define one in '$app'.\n\n") ;
1139             }
1140              
1141             return ;
1142             }
1143              
1144             #-------------------------------------------------------------------------------
1145              
1146             sub display_apropos
1147             {
1148              
1149             =head2 [P]display_apropos()
1150              
1151             Will display matches to the apropos query using B<$self->{apropos}>, that you set during construction, or will search in the
1152             B field of the sub commands.
1153              
1154             I - None - takes the search string from the I<--apropos> option.
1155              
1156             I - Nothing
1157              
1158             I - None
1159              
1160             =cut
1161              
1162             my ($self) = @_ ;
1163              
1164             my $apropos = $self->{apropos} ;
1165              
1166             my $apropos_option = ${$self->{getopt_definitions}{'apropos=s'}} ;
1167              
1168             if(defined $apropos)
1169             {
1170             if('CODE' eq ref $apropos)
1171             {
1172             $apropos->($self, $apropos_option) ;
1173             }
1174             else
1175             {
1176             if($SCALAR eq ref $apropos)
1177             {
1178             $apropos .= "\n" unless $apropos =~ /\n$/sxm ;
1179            
1180             $self->{INTERACTION}{INFO}($apropos) ;
1181             }
1182             }
1183             }
1184             else
1185             {
1186             my $sub_apps = $self->{sub_apps} ;
1187            
1188             if(defined $sub_apps)
1189             {
1190             my $command ;
1191            
1192             for my $sub_app_name (sort keys %{$sub_apps})
1193             {
1194             if(any {/\Q$apropos_option/sxm} @{$sub_apps->{$sub_app_name}{apropos}})
1195             {
1196             $command .= sprintf ' %-25.25s ', $sub_app_name ;
1197             $command .= $sub_apps->{$sub_app_name}{description} || 'no description!.' ;
1198             $command .= "\n" ;
1199             }
1200             }
1201            
1202             defined $command
1203             ? $self->{INTERACTION}{INFO}("Matching apropos search:\n$command")
1204             : $self->{INTERACTION}{INFO}("No match for apropos search.\n") ;
1205             }
1206             else
1207             {
1208             $self->{INTERACTION}{INFO}('No sub applications registred') ;
1209             }
1210             }
1211              
1212             return ;
1213             }
1214              
1215             #-------------------------------------------------------------------------------
1216              
1217             sub display_faq
1218             {
1219              
1220             =head2 [P]display_faq()
1221              
1222             Will display an answer to a a faq question using B<$self->{faq}>, that you set during construction, or will inform you if you haven't set the B field.
1223              
1224             I - None - takes the FAQ query from the I<--faq> option.
1225              
1226             I - Nothing
1227              
1228             I - None
1229              
1230             =cut
1231              
1232             my ($self, @argument) = @_ ;
1233              
1234             my $faq = $self->{faq} ;
1235              
1236             my $faq_option = ${$self->{getopt_definitions}{'apropos=s'}} ;
1237              
1238             if(defined $faq)
1239             {
1240             if('CODE' eq ref $faq)
1241             {
1242             $faq->($self, $faq_option) ;
1243             }
1244             else
1245             {
1246             if($SCALAR eq ref $faq)
1247             {
1248             $faq .= "\n" unless $faq =~ /\n$/sxm ;
1249            
1250             $self->{INTERACTION}{INFO}($faq) ;
1251             }
1252             }
1253             }
1254             else
1255             {
1256             my $app = ref($self) ;
1257             $self->{INTERACTION}{WARN}("No FAQ. Please define one in '$app'.\n\n") ;
1258             }
1259              
1260             return ;}
1261              
1262             #-------------------------------------------------------------------------------
1263              
1264             1 ;
1265              
1266             =head1 BUGS AND LIMITATIONS
1267              
1268             None so far.
1269              
1270             =head1 AUTHOR
1271              
1272             Nadim ibn hamouda el Khemir
1273             CPAN ID: NKH
1274             mailto: nadim@cpan.org
1275              
1276             =head1 COPYRIGHT AND LICENSE
1277              
1278             Copyright Nadim Khemir 2010.
1279              
1280             This program is free software; you can redistribute it and/or
1281             modify it under the terms of either:
1282              
1283             =over 4
1284              
1285             =item * the GNU General Public License as published by the Free
1286             Software Foundation; either version 1, or (at your option) any
1287             later version, or
1288              
1289             =item * the Artistic License version 2.0.
1290              
1291             =back
1292              
1293             =head1 SUPPORT
1294              
1295             You can find documentation for this module with the perldoc command.
1296              
1297             perldoc App::Chained
1298              
1299             You can also look for information at:
1300              
1301             =over 4
1302              
1303             =item * AnnoCPAN: Annotated CPAN documentation
1304              
1305             L
1306              
1307             =item * RT: CPAN's request tracker
1308              
1309             Please report any bugs or feature requests to L .
1310              
1311             We will be notified, and then you'll automatically be notified of progress on
1312             your bug as we make changes.
1313              
1314             =item * Search CPAN
1315              
1316             L
1317              
1318             =back
1319              
1320             =head1 SEE ALSO
1321              
1322              
1323             =cut