Ask

 view release on metacpan or  search on metacpan

README  view on Meta::CPAN


       use Ask ':all';
   
       if (question("Are you happy?")
       and question("Do you know it?")
       and question("Really want to show it?")) {
          info("Then clap your hands!");
       }

DESCRIPTION
    The `Ask` suite is a set of modules for interacting with users; prompting
    them for information, displaying messages, warnings and errors, etc.

    There are already countless CPAN modules for doing this sort of thing, but
    what sets `Ask` apart from them is that `Ask` will detect how your script
    is being run (in a terminal, headless, etc) and choose an appropriate way
    to interact with the user.

  Class Methods
    `Ask->instance`
        Singleton pattern. Can also be passed an argument to use it as a

articles/ask-introduction.pod  view on Meta::CPAN

=head1 Ask not what your user can do for you...

In many scripts, we need to prompt the end user for information - this
could be a prompt for a file name, a selection from a list of options,
or an answer to a yes/no question.

The traditional approach to this sort of question is to print your
question to STDOUT, read a line from STDIN, and apply some sort of
parsing to the answer...

   use 5.010;
   use strict;
   use warnings;
   

articles/ask-introduction.pod  view on Meta::CPAN

      $answer = 0 if /^N/i;
   }
   
   say "Adding fries!" if $answer;

One issue with this approach is: what happens when your script is not
running in a terminal?

One attempt at solving this problem is L<IO::Prompt::Tiny> and its ilk.
This performs a simple test to determine if the script is running on an
interactive terminal and only prompts the user if the terminal is
interactive. When the script is being run non-interactively (or if the
C<PERL_MM_USE_DEFAULT> environment variable is set), then it returns a
default answer instead.

   use 5.010;
   use strict;
   use warnings;
   use IO::Prompt::Tiny qw(prompt);
   
   my $answer;
   until (defined $answer) {
      # In non-interactive mode, assume they want no fries...
      $_ = prompt("Would you like fries with that?", "No");
      $answer = 1 if /^Y/i;
      $answer = 0 if /^N/i;
   }
   
   say "Adding fries!" if $answer;

The problem with this is that it makes the assumption that when the
terminal is non-interactive, there is absolutely no other way to prompt
the user, and you should be happy with the default answer. This is not
always a good assumption.

=head2 Opening up a dialogue

On some operating systems, double-clicking a Perl file will launch it
without a terminal. In these cases, you can probably interact with the
user by launching a dialog box. But how to do that? Doesn't that
require complex programming in L<Tk> or L<Wx> (modules which are not
in core, and not always straightforward to build)?

lib/Ask.pm  view on Meta::CPAN

   use Ask ':all';
   
   if (question("Are you happy?")
   and question("Do you know it?")
   and question("Really want to show it?")) {
      info("Then clap your hands!");
   }

=head1 DESCRIPTION

The C<Ask> suite is a set of modules for interacting with users; prompting
them for information, displaying messages, warnings and errors, etc.

There are already countless CPAN modules for doing this sort of thing, but
what sets C<Ask> apart from them is that C<Ask> will detect how your script
is being run (in a terminal, headless, etc) and choose an appropriate way
to interact with the user.

=head2 Class Methods

=over

lib/Ask/Caroline.pm  view on Meta::CPAN

}

sub quality {
	my ( $self ) = ( shift );
	
	( ref( $self ) ? $self : 'Caroline'->new )->is_supported ? 90 : 30;
}

sub entry {
	my ( $self, %opts ) = ( shift, @_ );
	$opts{prompt} = 'entry> ' unless exists $opts{prompt};
	
	if ( exists $opts{completion} ) {
		$self->completion( $opts{completion} );
	}
	else {
		$self->completion(
			sub {
				return $opts{default};
			}
		);

lib/Ask/Caroline.pm  view on Meta::CPAN

	my ( $line, $tio );
	
	if (
		$opts{hide_text}
		and do { require POSIX; $tio = 'POSIX::Termios'->new }
		)
	{
		$tio->getattr( 0 );
		$tio->setlflag( $tio->getlflag & ~POSIX::ECHO() );
		$tio->setattr( 0 );
		print STDOUT $opts{prompt};    # no new line;
		STDOUT->flush;
		chomp( $line = <STDIN> );
		$tio->setlflag( $tio->getlflag | POSIX::ECHO() );
		$tio->setattr( 0 );
		print STDOUT "\r\n";
		STDOUT->flush;
	} #/ if ( $opts{hide_text} ...)
	else {
		chomp( $line = $self->caroline->readline( $opts{prompt} ) );
	}
	
	$self->clear_completion;
	
	return $line;
} #/ sub entry

sub question {
	my ( $self, %opts ) = ( shift, @_ );
	$opts{prompt} = 'y/n> ' unless exists $opts{prompt};
	
	my $response = $self->entry( %opts );
	my $lang     = $self->_lang_support( $opts{lang} );
	$lang->boolean( $response );
}

sub print_findings {
	my ( $self, $findings, $fh ) = ( shift, @_ );
	
	if ( my @copy = @$findings ) {

lib/Ask/Caroline.pm  view on Meta::CPAN

	} #/ if ( my @copy = @$findings)
	
	return;
} #/ sub print_findings

sub file_selection {
	my ( $self, %opts ) = ( shift, @_ );
	
	my $single = !$opts{multiple};
	
	$opts{prompt} = sprintf( '%s> ', $opts{directory} ? 'directory' : 'file' )
		unless exists $opts{prompt};
		
	unless ( $opts{text} ) {
		$opts{text} =
			$single
			? (
			$opts{directory} ? 'Please choose a directory.' : 'Please choose a file.' )
			: (
			$opts{directory}
			? 'Please choose some directories.'
			: 'Please choose some files.'

lib/Ask/Caroline.pm  view on Meta::CPAN

			
			$self->print_findings( \@printable, \*STDOUT );
			return map "$_", ( $got->is_dir ? $got : () ), @kids;
		}
	);
	
	my @chosen;
	
	CHOICE: while ( 1 ) {
	
		chomp( my $line = $self->caroline->readline( $opts{prompt} ) );
		
		if ( $line eq '' ) {
			$single ? next( CHOICE ) : last( CHOICE );
		}
		
		if ( $opts{existing} and not path( $line )->exists ) {
			$self->error(
				text => sprintf( 'Does not exist: %s. Please try again.', $line ),
			);
		}

lib/Ask/Caroline.pm  view on Meta::CPAN


sub single_choice {
	shift->multiple_choice( @_, _single => 1 );
}

sub multiple_choice {
	my ( $self, %opts ) = ( shift, @_ );
	
	my $single = $opts{_single};
	
	$opts{prompt} = 'choice> ' unless exists $opts{prompt};
	
	if ( exists $opts{text} ) {
		$self->info(
			text   => $opts{text},
			colour => $opts{colour} || 'bright_cyan',
		);
	}
	
	my %allowed;
	my @choices_list = map {

lib/Ask/Caroline.pm  view on Meta::CPAN

			my @found = grep /^$got/, map $_->[0], @{ $opts{choices} };
			$self->print_findings( \@found, \*STDOUT );
			return @found;
		}
	);
	
	my @chosen;
	
	CHOICE: while ( 1 ) {
	
		chomp( my $line = $self->caroline->readline( $opts{prompt} ) );
		
		if ( $line eq '' ) {
			$single ? next( CHOICE ) : last( CHOICE );
		}
		
		if ( $allowed{$line} ) {
			push @chosen, $line;
			last CHOICE if $single;
		}
		else {

lib/Ask/Question.pm  view on Meta::CPAN

The text of the question. If a coderef is given, that coderef will be
forwarded any of the arguments to C<< $question->(...) >>.

=item C<< backend >> I<< Object >>

A blessed object implementing L<Ask::API>. Defaults to the result of
C<< Ask->detect >>.

=item C<< title >> I<< Str >>

A title to use for question prompts, used by certain Ask backends.

=item C<< type >> I<< TypeTiny >>

A type constraint to check answers against. If the answer provided by the
user fails a type check (after coercion, if the type has a coercion), they
will be prompted to answer again.

=item C<< spec >> I<< HashRef >>

If this Ask::Question is being used as the default for an attrbute spec,
this can be used to hold the specification hash for the attribute, and
Ask::Question will attempt to find missing information like C<type> from
the spec hash.

=item C<< multiple >> I<< Bool >>



( run in 1.163 second using v1.01-cache-2.11-cpan-6aa56a78535 )