File Coverage

blib/lib/CLI/Gwrap.pm
Criterion Covered Total %
statement 45 67 67.1
branch 6 16 37.5
condition n/a
subroutine 15 21 71.4
pod 10 11 90.9
total 76 115 66.0


line stmt bran cond sub pod time code
1             #===============================================================================
2             #
3             # PODNAME: CLI::Gwrap.pm
4             # ABSTRACT: GUI wrapper for command line scripts and programs
5             #
6             # AUTHOR: Reid Augustin (REID),
7             # EMAIL: reid@LucidPort.com
8             # CREATED: 07/06/2013 07:38:28 PM
9             #===============================================================================
10              
11 1     1   41541 use 5.008;
  1         4  
  1         33  
12 1     1   5 use strict;
  1         2  
  1         27  
13 1     1   4 use warnings;
  1         1  
  1         39  
14              
15             package CLI::Gwrap;
16 1     1   804 use Moo;
  1         17407  
  1         7  
17 1     1   2981 use Types::Standard qw( Str Int Bool ArrayRef CodeRef InstanceOf );
  1         93477  
  1         15  
18 1     1   1509 use Carp;
  1         2  
  1         101  
19              
20             our $VERSION = '0.030'; # VERSION
21              
22 1     1   718 use CLI::Gwrap::Opt;
  1         3  
  1         35  
23 1     1   9 use Exporter 'import';
  1         2  
  1         681  
24             # export the widget builder functions
25             our @EXPORT_OK = qw(
26             check
27             radio
28             string
29             hash
30             integer
31             float
32             incremental
33             label
34             );
35              
36             has 'command' => (is => 'ro', trigger => sub {
37             my ($self, $new) = @_;
38             $self->{command} = _normalize_name($new);
39             } );
40             has 'main_opt' => (is => 'ro', isa => InstanceOf['CLI::Gwrap::Opt']);
41             has 'description' => (is => 'ro', isa => Str);
42             has 'gwrapper_name' => (is => 'ro', isa => Str, default => 'wxGrid');
43             has 'gwrapper' => (is => 'rw');
44             has 'columns' => (is => 'rw', isa => Int, default => 3);
45             has 'verbatim' => (is => 'ro', isa => Bool);
46             has 'help' => (is => 'ro', isa => Str);
47             has 'persist' => (is => 'ro', isa => Bool);
48             has 'opts' => (is => 'ro', isa => ArrayRef[InstanceOf['CLI::Gwrap::Opt']]);
49             has 'advanced' => (is => 'ro', isa => ArrayRef[InstanceOf['CLI::Gwrap::Opt']]);
50             has 'exec_callback' => (is => 'ro', isa => CodeRef);
51             has 'timeout' => (is => 'ro', isa => Int);
52              
53             sub BUILD {
54 1     1 0 75 my ($self, $params) = @_;
55              
56 1 50       9 croak "No command to Gwrap!\n" if(not $self->command);
57              
58 1         4 my $plugin = "CLI/Gwrapper/$self->{gwrapper_name}.pm";
59 1         650 require $plugin;
60 0         0 $plugin = "CLI::Gwrapper::$self->{gwrapper_name}";
61              
62 0         0 my %opts = (
63             title => $self->command->[0],
64             );
65 0         0 for my $opt ( qw(
66             command
67             main_opt
68             description
69             verbatim
70             help
71             persist
72             columns
73             opts
74             advanced
75             exec_callback
76             timeout
77             ) ) {
78 0 0       0 $opts{$opt} = $self->$opt if (defined $self->$opt);
79             }
80 0         0 $self->gwrapper( $plugin->new(%opts) );
81 0 0       0 if (not $self->gwrapper->DOES('CLI::Gwrapper')) {
82 0         0 die "$plugin doesn't fullfil the CLI::Gwrapper role\n"
83             }
84             }
85              
86             sub title {
87 0     0 1 0 my ($self, $new) = @_;
88              
89 0 0       0 if (@_ > 1) {
90 0         0 $self->gwrapper->title($new);
91             }
92 0         0 return $self->gwrapper->title;
93             }
94              
95             sub run {
96 0     0 1 0 my ($self) = @_;
97              
98 0         0 $self->gwrapper->run;
99             }
100              
101             #
102             # Functions (not methods!) to create specific CLI program option types
103             #
104             BEGIN {
105              
106             # convert NAME into [ 'name', 'alias' ] form for simplicity
107             sub _normalize_name {
108 8     8   12 my ($name) = @_;
109              
110 8 50       24 $name = $_[1] if (ref $name eq __PACKAGE__);
111 8 100       18 if (ref $name) {
112 2 50       37 return $name if (ref $name eq 'ARRAY');
113 0         0 carp "NAME must be a string or a ref to an array with two elements, not $name\n";
114             }
115 6         129 return [$name, $name]; # name and alias the same
116             }
117              
118             # create the CLI::Gwrap::Opt generators:
119 1     1   4 for my $func (qw(
120             check
121             radio
122             string
123             hash
124             integer
125             float
126             incremental
127             label
128             )) {
129 8         16 my $func_string = qq{
130             sub $func {
131             my (\$name, \$description, \%opts) = \@_;
132              
133             return CLI::Gwrap::Opt->new(
134             type => '$func',
135             name => _normalize_name(\$name),
136             description => \$description,
137             %opts,
138             );
139             }
140             };
141 8     3 1 479 eval $func_string; ## no critic
  3     0 1 141  
  3     1 1 9  
  0     0 1 0  
  0     0 1 0  
  1     0 1 52  
  1     1 1 5  
  0     2 1 0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         42  
  1         4  
  2         79  
  2         8  
142 8 50       127 die $@ if $@;
143             }
144             }
145              
146             1;
147              
148              
149              
150             =pod
151              
152             =head1 NAME
153              
154             CLI::Gwrap.pm - GUI wrapper for command line scripts and programs
155              
156             =head1 VERSION
157              
158             version 0.030
159              
160             =head1 SYNOPSIS
161              
162             use CLI::Gwrap;
163              
164             CLI::Grawp->new(
165             command => 'ls' # the CLI name
166             description => 'List info ...', # from the man page
167             ... # see Example below
168             );
169              
170             =head1 DESCRIPTION
171              
172             CLI::Gwrap builds a GUI wrapper around a Command Line Interface (CLI)
173             script or program. The GUI presents the CLI options to the user and then
174             runs the CLI program.
175              
176             The specific GUI is chosen with a plugin system. To write a new Gwrapper,
177             follow the default CLI::Gwrapper::wxGrid as an example.
178              
179             =head2 Methods
180              
181             =over
182              
183             =item my $gwrap = CLI::Gwrap->new( %hash )
184              
185             Creates a new GUI Wrapper. B<%hash> describes the particulars of how the
186             GUI should look and behave.
187              
188             CLI::Gwrap is intended to make traditionally difficult CLI programs more
189             easily accessible. To help, there are places where CLI::Gwrap accepts
190             either a simple name string, or a two-element array reference where the
191             first element is the 'actual' name (as used by the CLI program) and the
192             second element is a more descriptive alias. In the following
193             documentation, those places are denoted by 'NAME'.
194              
195             Most CLI programs have at least one option that is not explicitly named.
196             For example:
197              
198             ls foo*
199              
200             The "foo*" option is not 'named', but 'ls' expects it to be a file glob.
201             In CLI::Gwrap, this option should be denoted by B in the call
202             to CLI::Gwrap->new(). The NAME should be set to the empty string
203             (''), or more helpfully, aliased to an empty string:
204              
205             [ '', 'file or directory GLOB pattern' ]
206              
207             which shows the explanation in the GUI, but adds no explicit option on the
208             CLI program command line.
209              
210             B<%hash> keys available to B are:
211              
212             =over
213              
214             =item command => NAME
215              
216             The CLI program name.
217              
218             =item main_opt => OPTION
219              
220             Most CLI programs take one (or more) unnamed options. Specify that option
221             here. For a description of OPTION, see OPTION FUNCTIONS below.
222              
223             =item description => 'text'
224              
225             A description of the CLI program, possibly taken from the man page. This
226             is typically displayed by the GUI when the mouse hovers over the CLI
227             program name.
228              
229             =item gwrapper_name => 'Wrapper_Name'
230              
231             Select a different Graphic Wrappers pluggin. The default wrapper is
232             'wxGrid'. Wrappers are installed in the CLI/Gwrapper directory.
233              
234             =item verbatim => true
235              
236             Most CLI programs take options with either a single dash (single
237             letter) or double dash preceding them. CLI::Gwrap by default automatically
238             prepends a single dash to single letter options, and a double dash to
239             longer options. Setting a true B option suppresses this
240             behavior, options must then be entered exactly as the CLI program expects
241             them.
242              
243             =item help => 'help_option'
244              
245             Many CLI programs can be induced to print out a help message of some kind.
246             If the B key is used, the value should be the CLI program option that
247             produces the help message. CLI::Gwrap will add a 'Help' button to the GUI
248             which, when pressed, will create a text window of the output (from STDOUT
249             or STDERR).
250              
251             =item persist => true
252              
253             The GUI normally closes as soon as the 'Execute' button is pressed.
254             suppresses automatic closing - the user must press the 'Close'
255             button.
256              
257             =item opts => [ I ]
258              
259             =item advanced => [ I ]
260              
261             The CLI program options are listed in these array references. If
262             B is included, the CLI::Gwrap adds an 'Advanced' button which
263             exposes the less common options (which are normally hidden).
264              
265             The elements of these arrays are built by calling the OPTION FUNCTIONS (see
266             below).
267              
268             =item exec_callback => sub { ... }
269              
270             When the 'Execute' (or 'Help') button in the GUI is clicked, CLI::Gwrapper
271             by default calls a function that calls IPC::Run::run(...) to execute the
272             CLI script or program. You can set your own B function here
273             to override that behavior. This makes it easy to use CLI::Gwrap directly
274             in a script (as opposed to writing a wrapper for an existing script). The
275             callback function should be something like this:
276              
277             sub my_exec_callback {
278             my ($self, $cmd_ref) = @_; # $cmd_ref is an ArrayRef consisting
279             # of the command and all the options,
280             # as collected from the GUI
281              
282             my ($stdout, $stderr);
283             local (*STDOUT, *STDERR);
284             open(STDOUT, ">", \$stdout)
285             or die "failed to redirect STDOUT\n";
286             open(STDERR, ">", \$stderr)
287             or die "failed to redirect STDERR\n";
288              
289             ############################################
290             #
291             # Your script-specific execution here
292             #
293             ############################################
294              
295             return ($?, $stdout, $stderr);
296             }
297              
298             =item timeout => number
299              
300             The timeout, in seconds, used by IPC::Run::run(...) when calling the script
301             or program (when the 'Execute' or 'Help' button is clicked in the GUI).
302             The default is 10 (seconds).
303              
304             =back
305              
306             =item $gwrap->run
307              
308             Runs the CLI::Gwrap object by calling its B method (which is required
309             by the CLI::Gwrapper role). For (e.g.) the B Gwrapper, this calls
310             Wx::MainLoop.
311              
312             =item $gwrap->title( [ 'new title' ])
313              
314             Get or set the GUI window title. The title is normally set to the
315             unadorned command name when the GUI is first presented. When the 'Execute'
316             button is clicked, the title is changed to the command name followed by all
317             the options as specified by the user.
318              
319             =back
320              
321             =head1 OPTION FUNCTIONS
322              
323             Option functions are used in the B and B arguments to
324             B to populate the CLI program option fields in the GUI.
325              
326             Option functions all take a NAME as the first argument, and a 'description'
327             as the second. The description is typically displayed when the user hovers
328             the mouse over the option NAME or field. Additional named options may
329             follow. Named options include (see CLI::Gwrap::Opt for a full list):
330              
331             =over
332              
333             =item state => 'initial state'
334              
335             The initial state of the option, interpreted in the context of the option
336             (e.g: B is true/false, B is a string, B is one of the
337             choices).
338              
339             =item label => 'override label'
340              
341             A string to override the normal label derived from the NAME. This can be
342             set (e.g) to the empty string to disply no label.
343              
344             =item width => pixels
345              
346             The number of pixels for the width of a B, B, or
347             B widget.
348              
349             =item choices => [ qw( array of choices ) ]
350              
351             Passes a reference to an array of choices for a B option.
352              
353             =item joiner => 'string'
354              
355             The 'joiner' to use for the CLI command line between the option name and
356             the option value. The default is for single-letter options to use a single
357             space:
358              
359             ... -x 4 ...
360              
361             and for long options to use an equals sign:
362              
363             ... --long_opt=ABC
364              
365             =back
366              
367             The available option functions are:
368              
369             =over
370              
371             =item check( NAME, 'description', %opts )
372              
373             Create a checkbox in the GUI for 'flag' type options. B<$opt->{state}> is
374             interpreted as true or false.
375              
376             =item radio( NAME, 'description', %opts )
377              
378             Create a radio button or drop-down combo box. Set the choices with the
379             B named option:
380              
381             choices => [ qw( the array of choices ) ],
382              
383             The first element of the arrayref must be the CLI program's default.
384              
385             If the B named option is declared, it must be one of the choices.
386              
387             If the default is selected, this NAME will not be explicitly called out on
388             the command line.
389              
390             =item string( NAME, 'description', %opts )
391              
392             =item hash( NAME, 'description', %opts )
393              
394             Create a text-entry widget. If the B is specified, the initial
395             value in the text-entry widget is set.
396              
397             B is for single-use options.
398              
399             B is used for muliple-use options: the value is split into tokens (on
400             whitespace), and NAME is used once for each token. For example, if the
401             user enters "AAA bbb XyZ", the CLI program is called with:
402              
403             '... -x AAA -x bbb -x XyZ ...'
404              
405             or
406              
407             '... --long_opt=AAA --long_opt=bbb --long_opt=XyZ ...'
408              
409             =item integer( NAME, 'description', %opts )
410              
411             =item float( NAME, 'description', %opts )
412              
413             Create an integer or floating point number entry. allows normal
414             perl floating point representations.
415              
416             =item incremental( NAME, 'description', %opts )
417              
418             Create an input that looks like an B entry, but when the CLI
419             program is called, CLI::Gwrap will specify NAME the number of times set in
420             the spinner. For example, a multi-level verbose flag can be specified
421             with:
422              
423             incremental( [ 'v', 'verbose' ] )
424              
425             If the user sets the value to 3, the CLI program is called with:
426              
427             '... -v -v -v ...'
428              
429             =item label( NAME, 'description', %opts )
430              
431             This is not actually an option, and no option is passed to the CLI program from a B
432             used to create non-option tags and spacers in the GUI.
433              
434             =back
435              
436             =head1 EXAMPLE
437              
438             Here is part of the gwrap_ls.pl example, the entire listing can be found in
439             the /bin directory of the distribution package:
440              
441             use CLI::Gwrap qw(check radio string hash integer float incremental);
442              
443             my $gwrap = CLI::Gwrap->new(
444             command => 'ls',
445             description => 'list directory contents',
446             columns => 4,
447             help => '--help',
448             persist => 1,
449             main_opt => hash(
450             [
451             '', # this option has no name
452             'pattern(s)', # alias (description)
453             ],
454             # 'hover' help
455             qq[shell glob pattern(s) to match file or directory names],
456             ),
457             opts => [
458             check(
459             'all', # option name
460             'do not ignore entries starting with .',
461             ),
462              
463             check(
464             [
465             'C', # option name
466             'columns', # either a long name or help for a short name
467             ],
468             'list entries by columns'
469             ),
470             .
471             .
472             .
473             ],
474              
475             advanced => [
476             check(
477             'almost-all',
478             'do not list implied . and ..',
479             ),
480              
481             radio(
482             'color',
483             qq[colorize the output. Defaults to 'always' or can be 'never' or 'auto'],
484             choices => ['always', 'never', 'auto'], # the choices
485             ),
486             ]
487              
488             );
489              
490             =head1 AUTHOR
491              
492             Reid Augustin
493              
494             =head1 COPYRIGHT AND LICENSE
495              
496             This software is copyright (c) 2013 by Reid Augustin.
497              
498             This is free software; you can redistribute it and/or modify it under
499             the same terms as the Perl 5 programming language system itself.
500              
501             =cut
502              
503              
504             __END__