File Coverage

blib/lib/MooX/Cmd.pm
Criterion Covered Total %
statement 43 43 100.0
branch 19 20 95.0
condition n/a
subroutine 9 9 100.0
pod n/a
total 71 72 98.6


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