File Coverage

blib/lib/MooX/Cmd/Role.pm
Criterion Covered Total %
statement 104 105 99.0
branch 34 38 89.4
condition n/a
subroutine 25 26 96.1
pod 2 2 100.0
total 165 171 96.4


line stmt bran cond sub pod time code
1             package MooX::Cmd::Role;
2              
3 3     3   31450 use strict;
  3         9  
  3         160  
4 3     3   20 use warnings;
  3         6  
  3         190  
5              
6             our $VERSION = "0.015";
7              
8 3     3   21 use Moo::Role;
  3         17  
  3         23  
9              
10 3     3   1191 use Carp;
  3         6  
  3         345  
11 3     3   17 use Module::Runtime qw/ use_module /;
  3         4  
  3         27  
12 3     3   2470 use Regexp::Common;
  3         11662  
  3         15  
13 3     3   200996 use Text::ParseWords 'shellwords';
  3         4899  
  3         284  
14 3     3   2101 use Module::Pluggable::Object;
  3         25413  
  3         169  
15              
16 3     3   2492 use List::MoreUtils qw/first_index first_result/;
  3         39726  
  3         95  
17 3     3   2952 use Scalar::Util qw/blessed/;
  3         8  
  3         425  
18 3     3   2691 use Params::Util qw/_ARRAY/;
  3         9824  
  3         5180  
19              
20             =head1 NAME
21              
22             MooX::Cmd::Role - MooX cli app commands do this
23              
24             =head1 SYNOPSIS
25              
26             =head2 using role and want behavior as MooX::Cmd
27              
28             package MyFoo;
29            
30             with MooX::Cmd::Role;
31            
32             sub _build_command_execute_from_new { 1 }
33              
34             package main;
35              
36             my $cmd = MyFoo->new_with_cmd;
37              
38             =head2 using role and don't execute immediately
39              
40             package MyFoo;
41              
42             with MooX::Cmd::Role;
43             use List::MoreUtils qw/ first_idx /;
44              
45             sub _build_command_base { "MyFoo::Command" }
46              
47             sub _build_command_execute_from_new { 0 }
48              
49             sub execute {
50             my $self = shift;
51             my $chain_idx = first_idx { $self == $_ } @{$self->command_chain};
52             my $next_cmd = $self->command_chain->{$chain_idx+1};
53             $next_cmd->owner($self);
54             $next_cmd->execute;
55             }
56              
57             package main;
58              
59             my $cmd = MyFoo->new_with_cmd;
60             $cmd->command_chain->[-1]->run();
61              
62             =head2 explicit expression of some implicit stuff
63              
64             package MyFoo;
65              
66             with MooX::Cmd::Role;
67              
68             sub _build_command_base { "MyFoo::Command" }
69              
70             sub _build_command_execute_method_name { "run" }
71              
72             sub _build_command_execute_from_new { 0 }
73              
74             package main;
75              
76             my $cmd = MyFoo->new_with_cmd;
77             $cmd->command_chain->[-1]->run();
78              
79             =head1 DESCRIPTION
80              
81             MooX::Cmd::Role is made for modern, flexible Moo style to tailor cli commands.
82              
83             =head1 ATTRIBUTES
84              
85             =head2 command_args
86              
87             ARRAY-REF of args on command line
88              
89             =cut
90              
91             has 'command_args' => ( is => "ro" );
92              
93             =head2 command_chain
94              
95             ARRAY-REF of commands lead to this instance
96              
97             =cut
98              
99             has 'command_chain' => ( is => "ro" );
100              
101             =head2 command_chain_end
102              
103             COMMAND accesses the finally detected command in chain
104              
105             =cut
106              
107             has 'command_chain_end' => ( is => "lazy" );
108              
109 16     16   2996 sub _build_command_chain_end { $_[0]->command_chain->[-1] }
110              
111             =head2 command_name
112              
113             ARRAY-REF the name of the command lead to this command
114              
115             =cut
116              
117             has 'command_name' => ( is => "ro" );
118              
119             =head2 command_commands
120              
121             HASH-REF names of other commands
122              
123             =cut
124              
125             has 'command_commands' => ( is => "lazy" );
126              
127             sub _build_command_commands
128             {
129 28     28   88 my ( $class, $params ) = @_;
130 28 100       237 defined $params->{command_base} or $params->{command_base} = $class->_build_command_base($params);
131 28         115 my $base = $params->{command_base};
132              
133             # i have no clue why 'only' and 'except' seems to not fulfill what i need or are bugged in M::P - Getty
134 43         58257 my @cmd_plugins = grep {
135 28         611 my $plug_class = $_;
136 43         514 $plug_class =~ s/${base}:://;
137 43         217 $plug_class !~ /:/;
138             } Module::Pluggable::Object->new(
139             search_path => $base,
140             require => 0,
141             )->plugins;
142              
143 28         3195 my %cmds;
144              
145 28         109 for my $cmd_plugin (@cmd_plugins)
146             {
147 38         242 $cmds{ _mkcommand( $cmd_plugin, $base ) } = $cmd_plugin;
148             }
149              
150 28         238 \%cmds;
151             }
152              
153             =head2 command_base
154              
155             STRING base of command plugins
156              
157             =cut
158              
159             has command_base => ( is => "lazy" );
160              
161 26     26   178 sub _build_command_base { $_[0] . '::Cmd'; }
162              
163             =head2 command_execute_method_name
164              
165             STRING name of the method to invoke to execute a command, default "execute"
166              
167             =cut
168              
169             has command_execute_method_name => ( is => "lazy" );
170              
171 7     7   39 sub _build_command_execute_method_name { "execute" }
172              
173             =head2 command_execute_return_method_name
174              
175             STRING I have no clue what that is good for ...
176              
177             =cut
178              
179             has command_execute_return_method_name => ( is => "lazy" );
180              
181 13     13   1089 sub _build_command_execute_return_method_name { "execute_return" }
182              
183             =head2 command_creation_method_name
184              
185             STRING name of constructor
186              
187             =cut
188              
189             has command_creation_method_name => ( is => "lazy" );
190              
191 2     2   10 sub _build_command_creation_method_name { "new_with_cmd" }
192              
193             =head2 command_creation_chain_methods
194              
195             ARRAY-REF names of methods to chain for creating object (from L)
196              
197             =cut
198              
199             has command_creation_chain_methods => ( is => "lazy" );
200              
201 23     23   111 sub _build_command_creation_chain_methods { [ 'new_with_options', 'new' ] }
202              
203             =head2 command_execute_from_new
204              
205             BOOL true when constructor shall invoke L, false otherwise
206              
207             =cut
208              
209             has command_execute_from_new => ( is => "lazy" );
210              
211 0     0   0 sub _build_command_execute_from_new { 0 }
212              
213             =head1 METHODS
214              
215             =head2 new_with_cmd
216              
217             initializes by searching command line args for commands and invoke them
218              
219             =cut
220              
221 2     2 1 19 sub new_with_cmd { goto &_initialize_from_cmd; }
222              
223             sub _mkcommand
224             {
225 38     38   82 my ( $package, $base ) = @_;
226 38         643 $package =~ s/^${base}:://g;
227 38         222 lc($package);
228             }
229              
230             my @private_init_params =
231             qw(command_base command_execute_method_name command_execute_return_method_name command_creation_chain_methods command_execute_method_name);
232              
233             my $required_method = sub {
234             my ( $tgt, $method ) = @_;
235             $tgt->can($method) or croak( "You need an '$method' in " . ( blessed $tgt || $tgt ) );
236             };
237              
238             my $call_required_method = sub {
239             my ( $tgt, $method, @args ) = @_;
240             my $m = $required_method->( $tgt, $method );
241             return $m->( $tgt, @args );
242             };
243              
244             my $call_optional_method = sub {
245             my ( $tgt, $method, @args ) = @_;
246             my $m = $tgt->can($method) or return;
247             return $m->( $tgt, @args );
248             };
249              
250             my $call_indirect_method = sub {
251             my ( $tgt, $name_getter, @args ) = @_;
252             my $g = $call_required_method->( $tgt, $name_getter );
253             my $m = $required_method->( $tgt, $g );
254             return $m->( $tgt, @args );
255             };
256              
257             sub _initialize_from_cmd
258             {
259 29     29   196 my ( $class, %params ) = @_;
260              
261 29         236 my @args = shellwords( join ' ', map { quotemeta } @ARGV );
  26         276  
262              
263 29         2819 my ( @used_args, $cmd, $cmd_name, $cmd_name_index );
264              
265 29         152 my %cmd_create_params = %params;
266 29         248 delete @cmd_create_params{ qw(command_commands), @private_init_params };
267              
268 29 100       349 defined $params{command_commands} or $params{command_commands} = $class->_build_command_commands( \%params );
269 29 100   19   466 if ( ( $cmd_name_index = first_index { $cmd = $params{command_commands}->{$_} } @args ) >= 0 )
  19         2893  
270             {
271 16         70 @used_args = splice @args, 0, $cmd_name_index;
272 16         53 $cmd_name = shift @args; # be careful about relics
273              
274 16         125 use_module($cmd);
275 16 50       16647 defined $cmd_create_params{command_execute_method_name}
276             or $cmd_create_params{command_execute_method_name} =
277             $call_optional_method->( $cmd, "_build_command_execute_method_name", \%cmd_create_params );
278 16 100       181 defined $cmd_create_params{command_execute_method_name}
279             or $cmd_create_params{command_execute_method_name} = "execute";
280 16         73 $required_method->( $cmd, $cmd_create_params{command_execute_method_name} );
281             }
282             else
283             {
284 13         38 @used_args = @args;
285 13         40 @args = ();
286             }
287              
288 28 100       497 defined $params{command_creation_chain_methods}
289             or $params{command_creation_chain_methods} = $class->_build_command_creation_chain_methods( \%params );
290 23         124 my @creation_chain =
291             _ARRAY( $params{command_creation_chain_methods} )
292 28 100       646 ? @{ $params{command_creation_chain_methods} }
293             : ( $params{command_creation_chain_methods} );
294 28 100   51   353 ( my $creation_method = first_result { defined $_ and $class->can($_) } @creation_chain )
  51 100       3109  
295             or croak "Can't find a creation method on $class";
296              
297 25         132 @ARGV = @used_args;
298 25         136 $params{command_args} = [@args];
299 25         89 $params{command_name} = $cmd_name;
300 25 100       120 defined $params{command_chain} or $params{command_chain} = [];
301 25         760 my $self = $creation_method->( $class, %params );
302 25         21940 push @{ $self->command_chain }, $self;
  25         195  
303              
304 25 100       105 if ($cmd)
305             {
306 15         44 @ARGV = @args;
307 15         31 my ( $creation_method, $creation_method_name, $cmd_plugin );
308 15 100       430 $cmd->can("_build_command_creation_method_name")
309             and $creation_method_name = $cmd->_build_command_creation_method_name( \%params );
310 15 100       108 $creation_method_name and $creation_method = $cmd->can($creation_method_name);
311 15 100       43 if ($creation_method)
312             {
313 9         41 @cmd_create_params{qw(command_chain)} = @$self{qw(command_chain)};
314 9         48 $cmd_plugin = $creation_method->( $cmd, %cmd_create_params );
315 7         23 $self->{ $self->command_execute_return_method_name } =
316 7         17 [ @{ $call_indirect_method->( $cmd_plugin, "command_execute_return_method_name" ) } ];
317             }
318             else
319             {
320 6 50   12   65 ( $creation_method = first_result { defined $_ and $cmd->can($_) } @creation_chain )
  12 50       155  
321             or croak "Can't find a creation method on " . $cmd;
322 6         63 $cmd_plugin = $creation_method->($cmd);
323 6         153 push @{ $self->command_chain }, $cmd_plugin;
  6         34  
324              
325 6         36 my $cemn = $cmd_plugin->can("command_execute_method_name");
326 6 50       45 my $exec_fun = $cemn ? $cemn->() : $self->command_execute_method_name();
327 6 100       500 $self->command_execute_from_new
328             and $self->{ $self->command_execute_return_method_name } =
329             [ $call_required_method->( $cmd_plugin, $exec_fun, \@ARGV, $self->command_chain ) ];
330             }
331             }
332             else
333             {
334 10 100       64 $self->command_execute_from_new
335             and $self->{ $self->command_execute_return_method_name } =
336             [ $call_indirect_method->( $self, "command_execute_method_name", \@ARGV, $self->command_chain ) ];
337             }
338              
339 23         308 return $self;
340             }
341              
342             =head2 execute_return
343              
344             returns the content of $self->{execute_return}
345              
346             =cut
347              
348             # XXX should be an r/w attribute - can be renamed on loading ...
349 22     22 1 143 sub execute_return { $_[0]->{execute_return} }
350              
351             =head1 LICENSE AND COPYRIGHT
352              
353             Copyright 2012-2013 Torsten Raudssus, Copyright 2013-2015 Jens Rehsack.
354              
355             This program is free software; you can redistribute it and/or modify it
356             under the terms of either: the GNU General Public License as published
357             by the Free Software Foundation; or the Artistic License.
358              
359             See L for more information.
360              
361             =cut
362              
363             1;