File Coverage

blib/lib/Command/Do.pm
Criterion Covered Total %
statement 63 79 79.7
branch 17 30 56.6
condition 5 10 50.0
subroutine 14 14 100.0
pod 3 3 100.0
total 102 136 75.0


line stmt bran cond sub pod time code
1             # ABSTRACT: Command-Line Applications Made Simple
2              
3             package Command::Do;
4              
5 2     2   53727 use 5.10.0;
  2         6  
  2         77  
6 2     2   1858 use utf8;
  2         16  
  2         11  
7 2     2   1822 use Validation::Class;
  2         468135  
  2         18  
8 2     2   4967 use Smart::Options;
  2         85282  
  2         128  
9 2     2   2207 use Docopt;
  2         62798  
  2         135  
10 2     2   33 use Carp 'croak';
  2         4  
  2         78  
11 2     2   9 use Scalar::Util 'blessed';
  2         3  
  2         80  
12 2     2   9 use parent 'Exporter::Tiny';
  2         4  
  2         6  
13              
14             our $VERSION = '0.120011'; # VERSION
15              
16             our @EXPORT = qw(
17             command
18             execute
19             usages
20             prototype
21             );
22              
23             our @EXPORT_OK = qw(
24             command
25             execute
26             usages
27             prototype
28             build
29             directive
30             document
31             field
32             filter
33             message
34             method
35             mixin
36             profile
37             );
38              
39             our %EXPORT_TAGS = (
40                 less => [qw(
41             command
42             execute
43             usages
44             prototype
45             field
46             mixin
47             )],
48                 more => [qw(
49             command
50             execute
51             usages
52             prototype
53             build
54             directive
55             field
56             filter
57             message
58             mixin
59             )],
60                 most => [qw(
61             command
62             execute
63             usages
64             prototype
65             build
66             directive
67             document
68             field
69             filter
70             message
71             mixin
72             profile
73             )]
74             );
75              
76              
77              
78             sub command {
79 7     7 1 3653     my ($code, $name) = (pop, pop);
80              
81 7 100 66     406     croak "Bad arguments to the command method" unless
82                     'CODE' eq ref $code && ! ref $name;
83              
84 4   100     16     $name //= 'default';
85              
86                 caller->prototype->configuration->builders->add(sub{
87 4     4   6715         my ($self) = @_;
88              
89 4 50       20         $self->stash("command.commands.$name" => $code)
90                         unless defined $self->stash("command.commands.$name");
91 4         21     });
92              
93 4         14552     return;
94             }
95              
96              
97             sub execute {
98 7     7 1 5356     my ($self, @args) = @_;
99              
100 7 100       42     $self = caller->new unless blessed $self;
101 7         1011     $self->stash('command.options' => Smart::Options->new);
102              
103 7         780     my $usage = $self->stash('command.usages');
104 7 100       116     unless ($usage) {
105 5         12         my $pkg = ref $self;
106 2     2   9282         my $dat = do { no strict 'refs'; \*{"$pkg\::DATA"} };
  2         5  
  2         1301  
  5         6  
  5         7  
  5         23  
107 5 50       18         unless (eof $dat) {
108 0         0             binmode $dat, ':raw';
109 0         0             $usage = join '', (<$dat>);
110 0         0             $self->stash('command.usages' => $usage);
111                     }
112                 }
113              
114 7         23     my $options = $self->stash("command.options")->parse(@args);
115 7   50     510     my $arguments = delete $options->{'_'} // [];
116              
117 7 100       21     my $mappings = eval {
118 2         10         docopt(doc => $usage, help => 0, version => 0)
119                 } if $usage;
120              
121 7 50       170     if ($mappings) {
122 0         0         my $selection = {};
123 0         0         while (my($key, $val) = each %{$mappings}) {
  0         0  
124 0 0       0             next unless defined $val;
125 0         0             $key =~ s/(<|>)//g;
126 0         0             $key =~ s/^-+//;
127 0 0 0     0             if (ref $val && blessed $val) {
128 0 0       0                 $selection->{$key} = $val->isa('boolean') ? 0 + $val : $val;
129                         }
130                         else {
131 0         0                 $selection->{$key} = $val;
132                         }
133                     }
134 0         0         $mappings = $selection;
135                 }
136              
137 7         24     $self->params->add($options);
138 7 50       5825     $self->params->add($mappings) if $mappings;
139 7         28     $self->prototype->normalize($self);
140              
141 7         2742     $options = $self->params->hash;
142              
143             # execute sub-command (if applicable)
144 7 50       117     if (defined $arguments->[0]) {
145 0         0         my $command = $arguments->[0];
146 0         0         my $code = $self->stash("command.commands.$command");
147              
148 0 0       0         return $code->($self, $options, $arguments)
149                         if 'CODE' eq ref $code;
150                 }
151              
152             # execute default command (if applicable)
153 7         27     my $code = $self->stash("command.commands.default");
154 7 100       176     return $code->($self, $options, $arguments, $usage)
155                     if 'CODE' eq ref $code;
156              
157 1         11     return;
158             }
159              
160              
161             sub usages {
162 2     2 1 693     my $text = pop;
163              
164 2 100       125     croak "Bad arguments to the usages method" unless defined $text;
165              
166                 caller->prototype->configuration->builders->add(sub{
167 3     3   9704         my ($self) = @_;
168 3         13         $self->stash('command.usages' => $text);
169 1         7     });
170              
171 1         5455     return;
172             }
173              
174             1;
175              
176             __END__
177            
178             =pod
179            
180             =head1 NAME
181            
182             Command::Do - Command-Line Applications Made Simple
183            
184             =head1 VERSION
185            
186             version 0.120011
187            
188             =head1 SYNOPSIS
189            
190             A simple script with option and argument parsing.
191            
192             use Command::Do;
193            
194             # default command (execute runs on-load)
195             execute command sub {
196             my ($self, $opts, $args) = @_;
197             printf "You sunk my %s\n", $opts->{vessel} || 'Battleship';
198             };
199            
200             # example usage
201             $ ./yourcmd
202            
203             A simple script with option/argument parsing and input validation.
204            
205             use Command::Do -less;
206            
207             field vessel => {
208             required => 1,
209             filters => ['trim','strip','titlecase'],
210             default => 'Battleship'
211             };
212            
213             # default command (execute runs on-load)
214             execute command sub {
215             my ($self, $opts, $args) = @_;
216             printf "You sunk my %s\n", $self->vessel;
217             };
218            
219             # example usage
220             $ ./yourcmd --vessel Yacht
221            
222             A simple script with option/argument parsing, input validation, and sub-commands.
223            
224             use Command::Do -less;
225            
226             field vessel => {
227             required => 1,
228             filters => ['trim','strip','titlecase'],
229             default => 'Battleship'
230             };
231            
232             command move => sub {
233             my ($self, $opts, $args) = @_;
234             printf "Relocating your %s\n", $self->vessel;
235             };
236            
237             command engage => sub {
238             my ($self, $opts, $args) = @_;
239             printf "Your %s has engaged enemy aircrafts\n", $self->vessel;
240             };
241            
242             # default command (execute runs on-load)
243             execute command sub {
244             my ($self, $opts, $args) = @_;
245             printf "You sunk my %s\n", $self->vessel;
246             };
247            
248             # example usage
249             $ ./yourcmd engage
250             $ ./yourcmd move --vessel 'Cruise Ship'
251             $ ./yourcmd --vessel=Battleship
252            
253             A simple script with option/argument parsing, validation, sub-commands and
254             documentation. Let your documentation determine which options and arguments your
255             program expects.
256            
257             package YourCmd;
258            
259             use Command::Do -less;
260            
261             field name => {
262             required => 1,
263             filters => ['trim', 'strip', 'titlecase'],
264             min_alpha => 4,
265             };
266            
267             field x => {
268             filters => ['trim', 'strip', 'numeric'],
269             default => 0
270             };
271            
272             field y => {
273             filters => ['trim', 'strip', 'numeric'],
274             default => 0
275             };
276            
277             command new => sub {
278             my ($self, $opts, $args) = @_;
279             $self->validate('name')
280             or $self->render_errors;
281            
282             # create new ship
283             };
284            
285             command evade => sub {
286             my ($self, $opts, $args) = @_;
287             $self->validate('name', 'y', 'x')
288             or $self->render_errors;
289            
290             # move ship to different coordinates
291             # e.g. using $opts->{speed} which defaults to 10
292             };
293            
294             command submerge => sub {
295             my ($self, $opts, $args) = @_;
296             $self->validate('name', 'x', 'y')
297             or $self->render_errors;
298            
299             # cause ship to be under water
300             };
301            
302             # roll your own output rendering
303             sub render_errors {
304             my ($self) = @_;
305             print STDERR $self->errors_to_string, "\n";
306             exit(1);
307             }
308            
309             1;
310            
311             # The DATA section will be render to STDOUT automatically unless the default
312             # command or a sub-command matched the execution
313            
314             __DATA__
315            
316             Battleship Script.
317            
318             Usage:
319             yourcmd new <name>
320             yourcmd evade <name> <x> <y> [--speed=<kn>]
321             yourcmd submerge <name> <x> <y>
322            
323             Options:
324             --speed=<kn> Speed in knots [default: 10].
325            
326             As depicted, you can opt in or out of most all features. Please see
327             L<Validation::Class> for more information on creating field definitions for
328             validation, and see L<Docopt> for more information on the usage-text format and
329             parser specification.
330            
331             =head1 DESCRIPTION
332            
333             Command::Do is a simple toolkit for building simple or sophisticated
334             command-line applications with ease. It includes very little magic, executes
335             quickly, and is useful when creating, validating, executing, and organizing
336             command-line applications and actions. Command::Do inherits most of its
337             functionality from L<Validation::Class> which allows you to focus on describing
338             your command-line arguments and how they should be validated. Command::Do also
339             uses L<Docopt> and L<Smart::Options> for parsing additional command-line options
340             and arguments. Command::Do is very unassuming as thus flexible. It does not
341             impose a particular application configuration and its dependencies are trivial
342             and easily fat-packed. Command::Do simply provides you with the tools to create
343             simple or sophisticated command-line interfaces, all wrapped-up in a nice DSL.
344            
345             The name Command::Do is meant to convey the idea, command-and-do, i.e., write a
346             command and do something! Leave the parsing, routing, validating, exception
347             handling and execution to the framework. Command::Do inherits the following
348             methods from L<Validation::Class>, (command, execute, usages, build, directive,
349             document, field, filter, message, method, mixin, profile and prototype) and
350             implements the following new ones.
351            
352             =head1 METHODS
353            
354             =head2 command
355            
356             The command function/method is used to register a coderef by name which may be
357             automatically invoked by the execute method if it's name matches the first
358             argument to the execute method. The command method can be passed a coderef, or a
359             name and coderef. The coderef, when executed will be passed an instance of the
360             current class, a hashref of command-line options, and an arrayref of extra
361             command-line arguments. If passed a coderef without an associated name, that
362             routine will be registered as the default routine to be executed by default
363             if/when no other named routines match.
364            
365             # sub-command to be execute when <name> matches the first argument
366             command name => sub {
367             my ($self, $options, $arguments) = @_;
368             ...
369             };
370            
371             # default command to be execute unless a sub-command matches the request
372             # the default command is passed an additional argument, the usages-text
373             # which can be print to the console
374             command name => sub {
375             my ($self, $options, $arguments, $usages_text) = @_;
376             ...
377             };
378            
379             =head2 execute
380            
381             The execute function/method is used to process the command-line request by
382             parsing the options and arguments and finding a matching pattern, action and/or
383             routine and executing it. The execute method can take a list of arguments but
384             defaults to using @ARGV. This method can also be used as a function to initiate
385             the parsing and execution process from within a script.
386            
387             # instantiate and execute from anywhere, using execute as a function
388             # will cause the code to execute whenever/wherever loaded
389             my $self = YourCmd->new;
390             $self->execute;
391            
392             =head2 usages
393            
394             The usages function/method is used to register the L<Docopt> compatible
395             command-line interface specification. This specification will be parsed for
396             instructions, e.g. default-values, constraints, execution patterns, options and
397             more.
398            
399             usages q{
400             yourcmd. does stuff.
401            
402             Usage:
403             run causes the console to run
404             jump causes the console to jump
405             play causes the console to play
406            
407             Options:
408             -h --hours [default: 8]
409             };
410            
411             If the usages text is not registered using this function, Command::Do will
412             examine the DATA section for instructions.
413            
414             __DATA__
415             yourcmd. does stuff.
416            
417             Usage:
418             run causes the console to run
419             jump causes the console to jump
420             play causes the console to play
421            
422             Options:
423             -h --hours [default: 8]
424            
425             =head1 AUTHOR
426            
427             Al Newkirk <anewkirk@ana.io>
428            
429             =head1 COPYRIGHT AND LICENSE
430            
431             This software is copyright (c) 2013 by Al Newkirk.
432            
433             This is free software; you can redistribute it and/or modify it under
434             the same terms as the Perl 5 programming language system itself.
435            
436             =cut
437