File Coverage

lib/Term/UI.pm
Criterion Covered Total %
statement 24 142 16.9
branch 0 94 0.0
condition 0 45 0.0
subroutine 8 13 61.5
pod 4 4 100.0
total 36 298 12.0


line stmt bran cond sub pod time code
1             package Term::UI;
2             $Term::UI::VERSION = '0.50';
3              
4 2     2   2798 use if $] > 5.017, 'deprecate';
  2         28  
  2         12  
5              
6 2     2   2445 use strict;
  2         3  
  2         39  
7 2     2   10 use warnings;
  2         4  
  2         45  
8              
9 2     2   9 use Carp;
  2         4  
  2         118  
10 2     2   1116 use Params::Check qw[check allow];
  2         8316  
  2         131  
11 2     2   600 use Term::ReadLine;
  2         2794  
  2         69  
12 2     2   12 use Locale::Maketext::Simple Style => 'gettext';
  2         2  
  2         10  
13 2     2   1195 use Term::UI::History;
  2         5  
  2         4997  
14              
15             our $AUTOREPLY;
16             our $INVALID = loc( 'Invalid selection, please try again: ' );
17             our $VERBOSE = 1;
18              
19             push @Term::ReadLine::Stub::ISA, __PACKAGE__
20             unless grep { $_ eq __PACKAGE__ } @Term::ReadLine::Stub::ISA;
21              
22              
23             =pod
24              
25             =head1 NAME
26              
27             Term::UI - Term::ReadLine UI made easy
28              
29             =head1 SYNOPSIS
30              
31             use Term::UI;
32             use Term::ReadLine;
33              
34             my $term = Term::ReadLine->new('brand');
35              
36             my $reply = $term->get_reply(
37             prompt => 'What is your favourite colour?',
38             choices => [qw|blue red green|],
39             default => 'blue',
40             );
41              
42             my $bool = $term->ask_yn(
43             prompt => 'Do you like cookies?',
44             default => 'y',
45             );
46              
47              
48             my $string = q[some_command -option --no-foo --quux='this thing'];
49              
50             my ($options,$munged_input) = $term->parse_options($string);
51              
52              
53             ### don't have Term::UI issue warnings -- default is '1'
54             $Term::UI::VERBOSE = 0;
55              
56             ### always pick the default (good for non-interactive terms)
57             ### -- default is '0'
58             $Term::UI::AUTOREPLY = 1;
59              
60             ### Retrieve the entire session as a printable string:
61             $hist = Term::UI::History->history_as_string;
62             $hist = $term->history_as_string;
63              
64             =head1 DESCRIPTION
65              
66             C is a transparent way of eliminating the overhead of having
67             to format a question and then validate the reply, informing the user
68             if the answer was not proper and re-issuing the question.
69              
70             Simply give it the question you want to ask, optionally with choices
71             the user can pick from and a default and C will DWYM.
72              
73             For asking a yes or no question, there's even a shortcut.
74              
75             =head1 HOW IT WORKS
76              
77             C places itself at the back of the C
78             C<@ISA> array, so you can call its functions through your term object.
79              
80             C uses C to record all interactions
81             with the commandline. You can retrieve this history, or alter
82             the filehandle the interaction is printed to. See the
83             C manpage or the C for details.
84              
85             =head1 METHODS
86              
87             =head2 $reply = $term->get_reply( prompt => 'question?', [choices => \@list, default => $list[0], preput => "text to put as default user input", multi => BOOL, print_me => "extra text to print & record", allow => $ref] );
88              
89             C asks a user a question, and then returns the reply to the
90             caller. If the answer is invalid (more on that below), the question will
91             be reposed, until a satisfactory answer has been entered.
92              
93             You have the option of providing a list of choices the user can pick from
94             using the C argument. If the answer is not in the list of choices
95             presented, the question will be reposed.
96              
97             If you provide a C answer, this will be returned when either
98             C<$AUTOREPLY> is set to true, (see the C section further
99             below), or when the user just hits C.
100              
101             The C argument allows to specify a text that will be inserted to
102             the prompt line as the initial input which may be edited, deleted or
103             accepted by the user. If you supply the empty string as the C
104             argument then the C value will be preputted. It will only work if
105             the underlying readline module provide support for it (now it is supported
106             only by the C).
107              
108             You can indicate that the user is allowed to enter multiple answers by
109             toggling the C flag. Note that a list of answers will then be
110             returned to you, rather than a simple string.
111              
112             By specifying an C handler, you can yourself validate the answer
113             a user gives. This can be any of the types that the Params::Check C
114             function allows, so please refer to that manpage for details.
115              
116             Finally, you have the option of adding a C argument, which is
117             simply printed before the prompt. It's printed to the same file handle
118             as the rest of the questions, so you can use this to keep track of a
119             full session of Q&A with the user, and retrieve it later using the
120             C<< Term::UI->history_as_string >> function.
121              
122             See the C section for samples of how to use this function.
123              
124             =cut
125              
126             sub get_reply {
127 0     0 1   my $term = shift;
128 0           my %hash = @_;
129              
130 0           my $tmpl = {
131             default => { default => undef, strict_type => 0 },
132             preput => { default => '', strict_type => 0 },
133             prompt => { default => '', strict_type => 1, required => 1 },
134             choices => { default => [], strict_type => 1 },
135             multi => { default => 0, allow => [0, 1] },
136             allow => { default => qr/.*/ },
137             print_me => { default => '', strict_type => 1 },
138             };
139              
140 0 0         my $args = check( $tmpl, \%hash, $VERBOSE )
141             or ( carp( loc(q[Could not parse arguments]) ), return );
142              
143             # Check for legacy default on multi=1
144 0 0 0       if ($args->{multi} and defined $args->{default} and ref($args->{default}) ne "ARRAY") {
      0        
145 0           $args->{default} = [ $args->{default} ];
146             }
147              
148             ### add this to the prompt to indicate the default
149             ### answer to the question if there is one.
150 0           my $prompt_add;
151              
152             ### if you supplied several choices to pick from,
153             ### we'll print them separately before the prompt
154 0 0         if( @{$args->{choices}} ) {
  0 0          
155             # clean up 'default' of items not in 'choices'
156 0 0         if ( $args->{ 'default' } ) {
157 0 0         if ( $args->{ 'multi' } ) {
158 0           @{ $args->{ 'default' } } =
159             grep {
160 0           my $default = $_;
161 0           grep $default eq $_, @{ $args->{ 'choices' } };
  0            
162             }
163 0           @{ $args->{ 'default' } };
  0            
164             } else {
165             delete $args->{ 'default' }
166 0 0         unless grep $_ eq $args->{ 'default' }, @{ $args->{ 'choices' } }
  0            
167             }
168             }
169              
170 0           my $i;
171 0           my $choices_width = length( sprintf( "%d", scalar @{ $args->{ 'choices' } } ) );
  0            
172              
173 0           for my $choice ( @{$args->{choices}} ) {
  0            
174 0           $i++; # the answer counter -- but humans start counting
175             # at 1 :D
176              
177             ### so this choice is the default? add it to 'prompt_add'
178             ### so we can construct a "foo? [DIGIT]" type prompt
179 0 0         if (defined $args->{default}) {
180 0 0         if ($args->{multi}) {
181 0 0         push @$prompt_add, $i if grep { $_ eq $choice } @{ $args->{ 'default' } };
  0            
  0            
182             }
183             else {
184 0 0         $prompt_add = $i if ($choice eq $args->{default});
185             }
186             }
187              
188             ### create a "DIGIT> choice" type line
189 0           $args->{print_me} .= sprintf "\n%*s> %-s", $choices_width, $i, $choice;
190             }
191              
192 0 0 0       $prompt_add = join(" ", @$prompt_add) if ( $prompt_add && $args->{multi} );
193              
194             ### we listed some choices -- add another newline for
195             ### pretty printing
196 0 0         $args->{print_me} .= "\n" if $i;
197              
198             ### allowable answers are now equal to the choices listed
199 0           $args->{allow} = $args->{choices};
200              
201             ### no choices, but a default? set 'prompt_add' to the default
202             ### to construct a 'foo? [DEFAULT]' type prompt
203             } elsif ( defined $args->{default} ) {
204 0 0 0       if ($args->{multi} and ref($args->{default}) eq "ARRAY") {
205 0           $prompt_add = join(" ", @{$args->{default}});
  0            
206             }
207             else {
208 0           $prompt_add = $args->{default};
209             }
210             }
211              
212             ### we set up the defaults, prompts etc, dispatch to the readline call
213 0           return $term->_tt_readline( %$args, prompt_add => $prompt_add );
214              
215             }
216              
217             =head2 $bool = $term->ask_yn( prompt => "your question", [default => (y|1,n|0), print_me => "extra text to print & record"] )
218              
219             Asks a simple C or C question to the user, returning a boolean
220             indicating C or C to the caller.
221              
222             The C answer will automatically returned, if the user hits
223             C or if C<$AUTOREPLY> is set to true. See the C
224             section further below.
225              
226             Also, you have the option of adding a C argument, which is
227             simply printed before the prompt. It's printed to the same file handle
228             as the rest of the questions, so you can use this to keep track of a
229             full session of Q&A with the user, and retrieve it later using the
230             C<< Term::UI->history_as_string >> function.
231              
232              
233             See the C section for samples of how to use this function.
234              
235             =cut
236              
237             sub ask_yn {
238 0     0 1   my $term = shift;
239 0           my %hash = @_;
240              
241 0           my $tmpl = {
242             default => { default => undef, allow => [qw|0 1 y n|],
243             strict_type => 1 },
244             prompt => { default => '', required => 1, strict_type => 1 },
245             print_me => { default => '', strict_type => 1 },
246             multi => { default => 0, no_override => 1 },
247             choices => { default => [qw|y n|], no_override => 1 },
248             allow => { default => [qr/^y(?:es)?$/i, qr/^n(?:o)?$/i],
249             no_override => 1
250             },
251             };
252              
253 0 0         my $args = check( $tmpl, \%hash, $VERBOSE ) or return undef;
254              
255             ### uppercase the default choice, if there is one, to be added
256             ### to the prompt in a 'foo? [Y/n]' type style.
257 0           my $prompt_add;
258 0           { my @list = @{$args->{choices}};
  0            
  0            
259 0 0         if( defined $args->{default} ) {
260              
261             ### if you supplied the default as a boolean, rather than y/n
262             ### transform it to a y/n now
263             $args->{default} = $args->{default} =~ /\d/
264             ? { 0 => 'n', 1 => 'y' }->{ $args->{default} }
265 0 0         : $args->{default};
266              
267 0           @list = map { lc $args->{default} eq lc $_
268             ? uc $args->{default}
269 0 0         : $_
270             } @list;
271             }
272              
273 0           $prompt_add .= join("/", @list);
274             }
275              
276 0           my $rv = $term->_tt_readline( %$args, prompt_add => $prompt_add );
277              
278 0 0         return $rv =~ /^y/i ? 1 : 0;
279             }
280              
281              
282              
283             sub _tt_readline {
284 0     0     my $term = shift;
285 0           my %hash = @_;
286              
287 0           local $Params::Check::VERBOSE = 0; # why is this?
288 0           local $| = 1; # print ASAP
289              
290 0           my ($default, $preput, $prompt, $choices, $multi, $allow, $prompt_add, $print_me);
291 0           my $tmpl = {
292             default => { default => undef, strict_type => 0,
293             store => \$default },
294             preput => { default => undef, strict_type => 0, store => \$preput},
295             prompt => { default => '', strict_type => 1, required => 1,
296             store => \$prompt },
297             choices => { default => [], strict_type => 1,
298             store => \$choices },
299             multi => { default => 0, allow => [0, 1], store => \$multi },
300             allow => { default => qr/.*/, store => \$allow, },
301             prompt_add => { default => '', store => \$prompt_add, strict_type => 1 },
302             print_me => { default => '', store => \$print_me },
303             };
304              
305 0 0         check( $tmpl, \%hash, $VERBOSE ) or return;
306              
307             ### prompts for Term::ReadLine can't be longer than one line, or
308             ### it can display wonky on some terminals.
309 0 0         history( $print_me ) if $print_me;
310              
311 0 0         my $preput_is_supported =
312             $term->ReadLine eq "Term::ReadLine::Gnu" ? 1 : undef;
313              
314 0 0         $preput = undef unless $preput_is_supported;
315              
316             ### If we are using Term::ReadLine:Gnu we can preput default value
317 0 0 0       if (defined $preput and $preput eq '') {
318             # if preput is the empty string we preput default
319 0           $preput = $prompt_add;
320             # We don't need to double information in the prompt in that case
321 0           $prompt_add = undef;
322             }
323              
324 0 0         if ($prompt_add) {
325             ### we might have to add a default value to the prompt, to
326             ### show the user what will be picked by default:
327 0           $prompt .= " [$prompt_add]: " ;
328             }
329             else {
330 0           $prompt .= " : ";
331             }
332              
333              
334             ### are we in autoreply mode?
335 0 0         if ($AUTOREPLY) {
336              
337             ### you used autoreply, but didn't provide a default!
338 0 0 0       carp loc(
339             q[You have '%1' set to true, but did not provide a default!],
340             '$AUTOREPLY'
341             ) if( !defined $default && $VERBOSE);
342              
343             ### print it out for visual feedback
344 0 0 0       if ($multi and defined($default)) {
345 0           history( join ' ', grep { defined } $prompt, @$default );
  0            
346             ### and return the default
347 0           return @$default;
348             }
349             else {
350 0           history( join ' ', grep { defined } $prompt, $default );
  0            
351             ### and return the default
352 0           return $default;
353             }
354              
355             }
356              
357 0 0 0       if ($multi and defined($default)) {
358 0           $default = join(' ', @$default);
359             }
360              
361             ### so, no AUTOREPLY, let's see what the user will answer
362             LOOP: {
363              
364             ### annoying bug in T::R::Perl that mucks up lines with a \n
365             ### in them; So split by \n, save the last line as the prompt
366             ### and just print the rest
367 0           { my @lines = split "\n", $prompt;
  0            
  0            
368 0           $prompt = pop @lines;
369              
370 0           history( "$_\n" ) for @lines;
371             }
372              
373             ### pose the question
374 0 0         my $answer = defined $preput
375             ? $term->readline($prompt, $preput)
376             : $term->readline($prompt);
377              
378 0 0         $answer = $default unless length $answer;
379              
380 0 0         $term->addhistory( $answer ) if length $answer;
381              
382             ### add both prompt and answer to the history
383 0 0         history( defined $answer ? "$prompt $answer" : "$prompt", 0 );
384              
385             ### if we're allowed to give multiple answers, split
386             ### the answer on whitespace
387 0 0         my @answers = grep defined, $multi ? split(/\s+/, $answer) : ( $answer );
388              
389             ### the return value list
390 0           my @rv;
391              
392 0 0         if( @$choices ) {
393              
394 0           for my $answer (@answers) {
395              
396             ### a digit implies a multiple choice question,
397             ### a non-digit is an open answer
398 0 0 0       if ( $answer =~ /\D/
      0        
399             || ( $answer =~ /^\d+$/
400             && @$choices < $answer
401             )
402             ) {
403 0 0         push @rv, $answer if allow( $answer, $allow );
404             } else {
405              
406             ### remember, the answer digits are +1 compared to
407             ### the choices, because humans want to start counting
408             ### at 1, not at 0
409 0 0 0       push @rv, $choices->[ $answer - 1 ]
410             if $answer > 0 && defined $choices->[ $answer - 1 ];
411             }
412             }
413              
414             ### no fixed list of choices.. just check if the answers
415             ### (or otherwise the default!) pass the allow handler
416             } else {
417 0           push @rv, grep { allow( $_, $allow ) } @answers;
  0            
418             }
419              
420             ### if not all the answers made it to the return value list,
421             ### at least one of them was an invalid answer -- make the
422             ### user do it again
423 0 0 0       if( (@rv != @answers) or
      0        
424             (scalar(@$choices) and not scalar(@answers))
425             ) {
426 0           $prompt = $INVALID;
427 0 0         $prompt .= "[$prompt_add] " if $prompt_add;
428 0           redo LOOP;
429              
430             ### otherwise just return the answer, or answers, depending
431             ### on the multi setting
432             } else {
433 0 0         return $multi ? @rv : $rv[0];
434             }
435             }
436             }
437              
438             =head2 ($opts, $munged) = $term->parse_options( STRING );
439              
440             C will convert all options given from an input string
441             to a hash reference. If called in list context it will also return
442             the part of the input string that it found no options in.
443              
444             Consider this example:
445              
446             my $str = q[command --no-foo --baz --bar=0 --quux=bleh ] .
447             q[--option="some'thing" -one-dash -single=blah' arg];
448              
449             my ($options,$munged) = $term->parse_options($str);
450              
451             ### $options would contain: ###
452             $options = {
453             'foo' => 0,
454             'bar' => 0,
455             'one-dash' => 1,
456             'baz' => 1,
457             'quux' => 'bleh',
458             'single' => 'blah\'',
459             'option' => 'some\'thing'
460             };
461              
462             ### and this is the munged version of the input string,
463             ### ie what's left of the input minus the options
464             $munged = 'command arg';
465              
466             As you can see, you can either use a single or a double C<-> to
467             indicate an option.
468             If you prefix an option with C and do not give it a value, it
469             will be set to 0.
470             If it has no prefix and no value, it will be set to 1.
471             Otherwise, it will be set to its value. Note also that it can deal
472             fine with single/double quoting issues.
473              
474             =cut
475              
476             sub parse_options {
477 0     0 1   my $term = shift;
478 0           my $input = shift;
479              
480 0           my $return = {};
481              
482             ### there's probably a more elegant way to do this... ###
483 0   0       while ( $input =~ s/(?:^|\s+)--?([-\w]+=("|').+?\2)(?=\Z|\s+)// or
      0        
484             $input =~ s/(?:^|\s+)--?([-\w]+=\S+)(?=\Z|\s+)// or
485             $input =~ s/(?:^|\s+)--?([-\w]+)(?=\Z|\s+)//
486             ) {
487 0           my $match = $1;
488              
489 0 0         if( $match =~ /^([-\w]+)=("|')(.+?)\2$/ ) {
    0          
    0          
    0          
490 0           $return->{$1} = $3;
491              
492             } elsif( $match =~ /^([-\w]+)=(\S+)$/ ) {
493 0           $return->{$1} = $2;
494              
495             } elsif( $match =~ /^no-?([-\w]+)$/i ) {
496 0           $return->{$1} = 0;
497              
498             } elsif ( $match =~ /^([-\w]+)$/ ) {
499 0           $return->{$1} = 1;
500              
501             } else {
502 0 0         carp(loc(q[I do not understand option "%1"\n], $match)) if $VERBOSE;
503             }
504             }
505              
506 0 0         return wantarray ? ($return,$input) : $return;
507             }
508              
509             =head2 $str = $term->history_as_string
510              
511             Convenience wrapper around C<< Term::UI::History->history_as_string >>.
512              
513             Consult the C man page for details.
514              
515             =cut
516              
517 0     0 1   sub history_as_string { return Term::UI::History->history_as_string };
518              
519             1;
520              
521             =head1 GLOBAL VARIABLES
522              
523             The behaviour of Term::UI can be altered by changing the following
524             global variables:
525              
526             =head2 $Term::UI::VERBOSE
527              
528             This controls whether Term::UI will issue warnings and explanations
529             as to why certain things may have failed. If you set it to 0,
530             Term::UI will not output any warnings.
531             The default is 1;
532              
533             =head2 $Term::UI::AUTOREPLY
534              
535             This will make every question be answered by the default, and warn if
536             there was no default provided. This is particularly useful if your
537             program is run in non-interactive mode.
538             The default is 0;
539              
540             =head2 $Term::UI::INVALID
541              
542             This holds the string that will be printed when the user makes an
543             invalid choice.
544             You can override this string from your program if you, for example,
545             wish to do localization.
546             The default is C
547              
548             =head2 $Term::UI::History::HISTORY_FH
549              
550             This is the filehandle all the print statements from this module
551             are being sent to. Please consult the C manpage
552             for details.
553              
554             This defaults to C<*STDOUT>.
555              
556             =head1 EXAMPLES
557              
558             =head2 Basic get_reply sample
559              
560             ### ask a user (with an open question) for their favourite colour
561             $reply = $term->get_reply( prompt => 'Your favourite colour? );
562              
563             which would look like:
564              
565             Your favourite colour?
566              
567             and C<$reply> would hold the text the user typed.
568              
569             =head2 get_reply with choices
570              
571             ### now provide a list of choices, so the user has to pick one
572             $reply = $term->get_reply(
573             prompt => 'Your favourite colour?',
574             choices => [qw|red green blue|] );
575              
576             which would look like:
577              
578             1> red
579             2> green
580             3> blue
581              
582             Your favourite colour?
583              
584             C<$reply> will hold one of the choices presented. C will repose
585             the question if the user attempts to enter an answer that's not in the
586             list of choices. The string presented is held in the C<$Term::UI::INVALID>
587             variable (see the C section for details.
588              
589             =head2 get_reply with choices and default
590              
591             ### provide a sensible default option -- everyone loves blue!
592             $reply = $term->get_reply(
593             prompt => 'Your favourite colour?',
594             choices => [qw|red green blue|],
595             default => 'blue' );
596              
597             which would look like:
598              
599             1> red
600             2> green
601             3> blue
602              
603             Your favourite colour? [3]:
604              
605             Note the default answer after the prompt. A user can now just hit C
606             (or set C<$Term::UI::AUTOREPLY> -- see the C section) and
607             the sensible answer 'blue' will be returned.
608              
609             =head2 get_reply using print_me & multi
610              
611             ### allow the user to pick more than one colour and add an
612             ### introduction text
613             @reply = $term->get_reply(
614             print_me => 'Tell us what colours you like',
615             prompt => 'Your favourite colours?',
616             choices => [qw|red green blue|],
617             multi => 1 );
618              
619             which would look like:
620              
621             Tell us what colours you like
622             1> red
623             2> green
624             3> blue
625              
626             Your favourite colours?
627              
628             An answer of C<3 2 1> would fill C<@reply> with C
629              
630             =head2 get_reply & allow
631              
632             ### pose an open question, but do a custom verification on
633             ### the answer, which will only exit the question loop, if
634             ### the answer matches the allow handler.
635             $reply = $term->get_reply(
636             prompt => "What is the magic number?",
637             allow => 42 );
638              
639             Unless the user now enters C<42>, the question will be reposed over
640             and over again. You can use more sophisticated C handlers (even
641             subroutines can be used). The C handler is implemented using
642             C's C function. Check its manpage for details.
643              
644             =head2 an elaborate ask_yn sample
645              
646             ### ask a user if he likes cookies. Default to a sensible 'yes'
647             ### and inform him first what cookies are.
648             $bool = $term->ask_yn( prompt => 'Do you like cookies?',
649             default => 'y',
650             print_me => 'Cookies are LOVELY!!!' );
651              
652             would print:
653              
654             Cookies are LOVELY!!!
655             Do you like cookies? [Y/n]:
656              
657             If a user then simply hits C, agreeing with the default,
658             C<$bool> would be set to C. (Simply hitting 'y' would also
659             return C. Hitting 'n' would return C)
660              
661             We could later retrieve this interaction by printing out the Q&A
662             history as follows:
663              
664             print $term->history_as_string;
665              
666             which would then print:
667              
668             Cookies are LOVELY!!!
669             Do you like cookies? [Y/n]: y
670              
671             There's a chance we're doing this non-interactively, because a console
672             is missing, the user indicated he just wanted the defaults, etc.
673              
674             In this case, simply setting C<$Term::UI::AUTOREPLY> to true, will
675             return from every question with the default answer set for the question.
676             Do note that if C is true, and no default is set, C
677             will warn about this and return C.
678              
679             =head1 See Also
680              
681             C, C, C
682              
683             =head1 BUG REPORTS
684              
685             Please report bugs or other issues to Ebug-term-ui@rt.cpan.org.
686              
687             =head1 AUTHOR
688              
689             This module by Jos Boumans Ekane@cpan.orgE.
690              
691             =head1 COPYRIGHT
692              
693             This library is free software; you may redistribute and/or modify it
694             under the same terms as Perl itself.
695              
696             =cut