File Coverage

blib/lib/MooX/Cmd.pm
Criterion Covered Total %
statement 42 42 100.0
branch 15 18 83.3
condition n/a
subroutine 9 9 100.0
pod n/a
total 66 69 95.6


line stmt bran cond sub pod time code
1             package MooX::Cmd;
2              
3 3     3   92328 use strict;
  3         7  
  3         124  
4 3     3   18 use warnings;
  3         6  
  3         158  
5              
6             our $VERSION = "0.015";
7              
8 3     3   771 use Package::Stash;
  3         6865  
  3         205  
9              
10             sub import
11             {
12 12     12   22077 my ( undef, %import_options ) = @_;
13 12         40 my $caller = caller;
14 12         22 my @caller_isa;
15 3     3   22 { no strict 'refs'; @caller_isa = @{"${caller}::ISA"} };
  3         6  
  3         1326  
  12         21  
  12         21  
  12         72  
16              
17             #don't add this to a role
18             #ISA of a role is always empty !
19             ## no critic qw/ProhibitStringyEval/
20 12 50       54 @caller_isa or return;
21              
22 12         23 my $execute_return_method_name = $import_options{execute_return_method_name};
23              
24 12 100       58 exists $import_options{execute_from_new} or $import_options{execute_from_new} = 1; # set default until we want other way
25              
26 12         276 my $stash = Package::Stash->new($caller);
27             defined $import_options{execute_return_method_name}
28             and $stash->add_symbol( '&' . $import_options{execute_return_method_name},
29 12 100   1   94 sub { shift->{ $import_options{execute_return_method_name} } } );
  1         9  
30 12 100       135 defined $import_options{creation_method_name} or $import_options{creation_method_name} = "new_with_cmd";
31 12     27   270 $stash->add_symbol( '&' . $import_options{creation_method_name}, sub { shift->_initialize_from_cmd(@_); } );
  27         8245  
32              
33             my $apply_modifiers = sub {
34 12 100   12   194 $caller->can('_initialize_from_cmd') and return;
35 11         41 my $with = $caller->can('with');
36 11         42 $with->('MooX::Cmd::Role');
37             # XXX prove whether it can chained ...
38 11 50       29812 $import_options{with_config_from_file} and $with->('MooX::ConfigFromFile::Role');
39 11 50       223 $import_options{with_config_from_file} and $with->('MooX::Cmd::Role::ConfigFromFile');
40 12         74 };
41 12         33 $apply_modifiers->();
42              
43 12         170 my %default_modifiers = (
44             base => '_build_command_base',
45             execute_method_name => '_build_command_execute_method_name',
46             execute_return_method_name => '_build_command_execute_return_method_name',
47             creation_chain_methods => '_build_command_creation_chain_methods',
48             creation_method_name => '_build_command_creation_method_name',
49             execute_from_new => '_build_command_execute_from_new',
50             );
51              
52 12         21 my $around;
53 12         49 foreach my $opt_key ( keys %default_modifiers )
54             {
55 72 100       15882 exists $import_options{$opt_key} or next;
56 25 100       144 $around or $around = $caller->can('around');
57 25     22   177 $around->( $default_modifiers{$opt_key} => sub { $import_options{$opt_key} } );
  22         3120  
58             }
59              
60 12         1391 return;
61             }
62              
63             1;
64              
65             =encoding utf8
66              
67             =head1 NAME
68              
69             MooX::Cmd - Giving an easy Moo style way to make command organized CLI apps
70              
71             =head1 SYNOPSIS
72              
73             package MyApp;
74              
75             use Moo;
76             use MooX::Cmd;
77              
78             sub execute {
79             my ( $self, $args_ref, $chain_ref ) = @_;
80             my @extra_argv = @{$args_ref};
81             my @chain = @{$chain_ref} # in this case only ( $myapp )
82             # where $myapp == $self
83             }
84              
85             1;
86            
87             package MyApp::Cmd::Command;
88             # for "myapp command"
89              
90             use Moo;
91             use MooX::Cmd;
92              
93             # gets executed on "myapp command" but not on "myapp command command"
94             # there MyApp::Cmd::Command still gets instantiated and for the chain
95             sub execute {
96             my ( $self, $args_ref, $chain_ref ) = @_;
97             my @chain = @{$chain_ref} # in this case ( $myapp, $myapp_cmd_command )
98             # where $myapp_cmd_command == $self
99             }
100              
101             1;
102              
103             package MyApp::Cmd::Command::Cmd::Command;
104             # for "myapp command command"
105              
106             use Moo;
107             use MooX::Cmd;
108              
109             # gets executed on "myapp command command" and will not get instantiated
110             # on "myapp command" cause it doesnt appear in the chain there
111             sub execute {
112             my ( $self, $args_ref, $chain_ref ) = @_;
113             my @chain = @{$chain_ref} # in this case ( $myapp, $myapp_cmd_command,
114             # $myapp_cmd_command_cmd_command )
115             # where $myapp_cmd_command_cmd_command == $self
116             }
117              
118             package MyZapp;
119              
120             use Moo;
121             use MooX::Cmd execute_from_new => 0;
122              
123             sub execute {
124             my ( $self ) = @_;
125             my @extra_argv = @{$self->command_args};
126             my @chain = @{$self->command_chain} # in this case only ( $myzapp )
127             # where $myzapp == $self
128             }
129              
130             1;
131            
132             package MyZapp::Cmd::Command;
133             # for "myapp command"
134              
135             use Moo;
136             use MooX::Cmd execute_from_new => 0;
137              
138             # gets executed on "myapp command" but not on "myapp command command"
139             # there MyApp::Cmd::Command still gets instantiated and for the chain
140             sub execute {
141             my ( $self ) = @_;
142             my @extra_argv = @{$self->command_args};
143             my @chain = @{$self->command_chain} # in this case ( $myzapp, $myzapp_cmd_command )
144             # where $myzapp_cmd_command == $self
145             }
146              
147             1;
148             package main;
149              
150             use MyApp;
151              
152             MyZapp->new_with_cmd->execute();
153             MyApp->new_with_cmd;
154              
155             1;
156              
157             =head1 DESCRIPTION
158              
159             Eases the writing of command line utilities, accepting commands and
160             subcommands and so on. These commands can form a tree, which is
161             mirrored in the package structure. On invocation each command along
162             the path through the tree (starting from the toplevel command
163             through to the most specific one) is instanciated.
164              
165             Each command needs to have an C function, accepting three
166             parameters:
167              
168             =over
169              
170             =item C
171              
172             A reference to the specific L object that is executing.
173              
174             =item C
175              
176             An ArrayRef of arguments passed to C. This only encompasses
177             arguments of the most specific (read: right-most) command.
178              
179             =item C
180              
181             An ArrayRef of Cs along the tree path, as specified on
182             the command line.
183              
184             =back
185              
186             B
187              
188             =head3 L Attributes
189              
190             Each command has some attributes set by L during
191             initialization:
192              
193             =over
194              
195             =item C
196              
197             Same as C argument to C.
198              
199             =item C
200              
201             TODO
202              
203             =item C
204              
205             TODO
206              
207             =item C
208              
209             TODO
210              
211             =item C
212              
213             TODO
214              
215             =back
216              
217             =head2 Examples
218              
219             =head3 A Single Toplevel Command
220              
221             #!/usr/bin/env perl
222             package MyApp;
223             use Moo;
224             use MooX::Cmd;
225              
226             sub execute {
227             my ($self,$args,$chain) = @_;
228             printf("%s.execute(\$self,[%s],[%s])\n",
229             ref($self), # which command is executing?
230             join(", ", @$args ), # what where the arguments?
231             join(", ", map { ref } @$chain) # what's in the command chain?
232             );
233             }
234              
235             package main;
236             MyApp->new_with_cmd();
237              
238             Some sample invocations:
239              
240             $ ./MyApp.pl
241             MyApp.execute($self,[],[MyApp])
242              
243             $./MyApp.pl --opt1
244             MyApp.execute($self,[--opt1],[MyApp])
245              
246             $ ./MyApp.pl --opt1 arg
247             MyApp.execute($self,[--opt1, arg],[MyApp])
248              
249             =head3 Toplevel Command with Subcommand
250              
251             #!/usr/bin/env perl
252             # let's define a base class containing our generic execute
253             # function to save some typing...
254             package CmdBase;
255             use Moo;
256              
257             sub execute {
258             my ($self,$args,$chain) = @_;
259             printf("%s.execute(\$self,[%s],[%s])\n",
260             ref($self),
261             join(", ", @$args ),
262             join(", ", map { ref } @$chain)
263             );
264             }
265              
266             package MyApp;
267             # toplevel command/app
268             use Moo;
269             use MooX::Cmd;
270             extends 'CmdBase';
271              
272             package MyApp::Cmd::frobnicate;
273             # can be called via ./MyApp.pl frobnicate
274             use Moo;
275             use MooX::Cmd;
276             extends 'CmdBase';
277              
278             package main;
279             MyApp->new_with_cmd();
280              
281             And some sample invocations:
282              
283             $ ./MyApp.pl frobnicate
284             MyApp::Cmd::frobnicate.execute($self,[],[MyApp, MyApp::Cmd::frobnicate])
285              
286             As you can see the chain contains our toplevel command object and
287             then the specififc one.
288              
289             $ ./MyApp.pl frobnicate arg1
290             MyApp::Cmd::frobnicate.execute($self,[arg1],[MyApp, MyApp::Cmd::frobnicate])
291              
292             Arguments are passed via the C parameter.
293              
294             $ ./MyApp.pl some --stuff frobnicate arg1
295             MyApp::Cmd::frobnicate.execute($self,[arg1],[MyApp, MyApp::Cmd::frobnicate])
296              
297             Arguments to commands higher in the tree get ignored if they don't
298             match a command.
299              
300             =head3 Access Toplevel Attributes via Chain
301              
302             #!/usr/bin/env perl
303             package CmdBase;
304             use Moo;
305              
306             sub execute {
307             my ($self,$args,$chain) = @_;
308             printf("%s.execute(\$self,[%s],[%s])\n",
309             ref($self),
310             join(", ", @$args ),
311             join(", ", map { ref } @$chain)
312             );
313             }
314              
315             package MyApp;
316             use Moo;
317             use MooX::Cmd;
318             extends 'CmdBase';
319              
320             has somevar => ( is => 'ro', default => 'someval' );
321              
322             package MyApp::Cmd::frobnicate;
323             use Moo;
324             use MooX::Cmd;
325             extends 'CmdBase';
326              
327             around execute => sub {
328             my ($orig,$self,$args,$chain) = @_;
329             $self->$orig($args,$chain);
330             # we can access toplevel attributes via the chain...
331             printf("MyApp->somevar = '%s'\n", $chain->[0]->somevar);
332             };
333              
334             package main;
335             MyApp->new_with_cmd();
336              
337             A sample invocation
338              
339             $ ./MyApp.pl some --stuff frobnicate arg1
340             MyApp::Cmd::frobnicate.execute($self,[arg1],[MyApp, MyApp::Cmd::frobnicate])
341             MyApp->somevar = someval
342              
343              
344             =head2 L integration
345              
346             You can integrate L simply by using it and declaring
347             some options, like so:
348              
349             #!/usr/bin/env perl
350             package MyApp;
351             use Moo;
352             use MooX::Cmd;
353             use MooX::Options;
354              
355             option debug => ( is => 'ro' );
356              
357             sub execute {
358             my ($self,$args,$chain) = @_;
359             print "debugging enabled!\n" if $self->{debug};
360             }
361              
362             package main;
363             MyApp->new_with_cmd();
364              
365             A sample invocation
366              
367             $ ./MyApp-Options.pl --debug
368             debugging enabled!
369              
370             B, so options are
371             parsed for the specific context and used for the instantiation:
372              
373             $ ./MyApp.pl --argformyapp command --argformyappcmdcommand ...
374              
375             =head1 SUPPORT
376              
377             Repository
378              
379             http://github.com/Getty/p5-moox-cmd
380             Pull request and additional contributors are welcome
381              
382             Issue Tracker
383              
384             http://github.com/Getty/p5-moox-cmd/issues
385             http://rt.cpan.org/NoAuth/Bugs.html?Dist=MooX-Cmd
386             bug-moox-cmd at rt.cpan.org
387              
388             =head1 THANKS
389              
390             =over
391              
392             =item Lukas Mai (mauke), Toby Inkster (tobyink)
393              
394             Gave some helpful advice for solving difficult issues
395              
396             =item Celogeek San
397              
398             Integration into MooX::Options for better help messages and suit team play
399              
400             =item Torsten Raudssus (Getty)
401              
402             did the initial work and brought it to CPAN
403              
404             =back
405              
406             =head1 LICENSE AND COPYRIGHT
407              
408             Copyright 2012-2013 Torsten Raudssus, Copyright 2013-2015 Jens Rehsack.
409              
410             This program is free software; you can redistribute it and/or modify it
411             under the terms of either: the GNU General Public License as published
412             by the Free Software Foundation; or the Artistic License.
413              
414             See L for more information.
415              
416             =cut