File Coverage

blib/lib/App/CLI/Command.pm
Criterion Covered Total %
statement 119 121 98.3
branch 22 28 78.5
condition 12 20 60.0
subroutine 23 23 100.0
pod 8 12 66.6
total 184 204 90.2


line stmt bran cond sub pod time code
1             package App::CLI::Command;
2 2     2   135823 use strict;
  2         15  
  2         77  
3 2     2   14 use warnings;
  2         5  
  2         92  
4 2     2   1135 use Locale::Maketext::Simple;
  2         4017  
  2         16  
5 2     2   533 use Carp ();
  2         5  
  2         44  
6 2     2   542 use App::CLI::Helper;
  2         6  
  2         16  
7 2     2   504 use Class::Load qw( load_class );
  2         21989  
  2         151  
8 2     2   49 use Scalar::Util qw( weaken );
  2         8  
  2         140  
9              
10             =head1 NAME
11              
12             App::CLI::Command - Base class for App::CLI commands
13              
14             =head1 SYNOPSIS
15              
16             package MyApp::List;
17             use base qw(App::CLI::Command);
18              
19             use constant options => (
20             'verbose' => 'verbose',
21             'n|name=s' => 'name',
22             );
23              
24             sub run {
25             my ( $self, $arg ) = @_;
26              
27             print "verbose" if $self->{verbose};
28              
29             my $name = $self->{name}; # get arg following long option --name
30              
31             # anything you want this command do
32             }
33              
34             # See App::CLI for information of how to invoke (sub)command.
35              
36             =head1 DESCRIPTION
37              
38              
39             =cut
40              
41 2     2   15 use constant subcommands => ();
  2         5  
  2         133  
42 2     2   15 use constant options => ();
  2         4  
  2         789  
43              
44             sub new {
45 15     15 0 1550 my $class = shift;
46 15         71 bless {@_}, $class;
47             }
48              
49             sub command_options {
50 20     20 0 141 ( ( map { $_ => $_ } $_[0]->subcommands ), $_[0]->options );
  12         69  
51             }
52              
53             sub run_command {
54 20     20 0 48 my $self = shift;
55 20         80 $self->run(@_);
56             }
57              
58             sub run {
59 1     1 0 7 my $class = shift;
60 1         192 Carp::croak ref($class) . " does not implement mandatory method 'run'\n";
61             }
62              
63             =head3 subcommand()
64              
65             return old genre subcommand of $self;
66              
67             =cut
68              
69             sub subcommand {
70 20     20 1 54 my $self = shift;
71 20         97 my @cmd = $self->subcommands;
72 20 50 66     99 @cmd = values %{ { $self->options } } if @cmd && $cmd[0] eq '*';
  0         0  
73 20         52 my $subcmd = undef;
74 20         62 for ( grep { $self->{$_} } @cmd ) {
  12         38  
75 2     2   21 no strict 'refs';
  2         5  
  2         605  
76 1 50       3 if ( exists ${ ref($self) . '::' }{ $_ . '::' } ) {
  1         9  
77 1         2 my %data = %{$self};
  1         9  
78 1         8 $subcmd = bless( {%data}, ( ref($self) . "::$_" ) );
79 1         5 last;
80             }
81             }
82 20 100       92 $subcmd ? $subcmd : $self;
83             }
84              
85             =head3 cascading()
86              
87             Return instance of cascading subcommand invoked if it was listed in your
88             constant subcommands.
89              
90             =cut
91              
92             sub cascading {
93 6     6 1 14 my $self = shift;
94 6 50       15 if ( my $subcmd = $self->cascadable ) {
95 6         13 shift @ARGV;
96 6         13 my %data = %{$self};
  6         32  
97 6         64 return bless {%data}, $subcmd;
98             }
99             else {
100 0         0 die $self->error_cmd( $ARGV[0] );
101             }
102             }
103              
104             =head3 cascadable()
105              
106             Return package name of subcommand if the subcommand invoked is in your
107             constant subcommands, otherwise, return C.
108              
109             =cut
110              
111             sub cascadable {
112 32     32 1 65 my $self = shift;
113 32   33     99 my $class = ref $self || $self;
114 32         173 for ( $self->subcommands ) {
115 2     2   25 no strict 'refs';
  2         6  
  2         2458  
116 30         89 my $package_name = $class . '::' . $_;
117 30         158 load_class $package_name;
118 30 100 100     5489 if ( $ARGV[0]
      66        
119             && (ucfirst( $ARGV[0] ) eq $_)
120 12         66 && exists ${ $class . '::' }{ $_ . '::' } )
121             {
122 12         84 return $package_name;
123             }
124             }
125 20         123 return undef;
126             }
127              
128             =head3 app
129              
130             Return the object referring to the current app.
131              
132             =cut
133              
134             sub app {
135 70     70 1 169 my $self = shift;
136              
137 70 100       251 if (@_) {
138 44         136 $self->{app} = shift;
139 44         178 weaken( $self->{app} );
140             }
141              
142 70         403 return $self->{app};
143             }
144              
145             =head3 brief_usage ($file)
146              
147             Display a one-line brief usage of the command object. Optionally, a file
148             could be given to extract the usage from the POD.
149              
150             =cut
151              
152             sub brief_usage {
153 6     6 1 52 my ( $self, $file ) = @_;
154 6 50 66     205 open my ($podfh), '<', ( $file || $self->filename ) or return;
155 6         49 local $/ = undef;
156 6         165 my $buf = <$podfh>;
157 6         38 my $base = ref $self->app;
158 6         18 my $indent = " ";
159 6 100       126 if ( $buf =~ /^=head1\s+NAME\s*\Q$base\E::(\w+)( - .+)$/m ) {
160 4         43 print $indent, loc( lc($1) . $2 ), "\n";
161             }
162             else {
163 2   33     12 my $cmd = $file || $self->filename;
164 2         21 $cmd =~ s/^(?:.*)\/(.*?).pm$/$1/;
165 2         12 print $indent, lc($cmd), " - ", loc("undocumented") . "\n";
166             }
167 6         527 close $podfh;
168             }
169              
170             =head3 usage ($want_detail)
171              
172             Display usage. If C<$want_detail> is true, the C
173             section is displayed as well.
174              
175             =cut
176              
177             sub usage {
178 3     3 1 42 my ( $self, $want_detail ) = @_;
179 3         17 my $fname = $self->filename;
180 3         29 my ($cmd) = $fname =~ m{\W(\w+)\.pm$};
181 3         31 require Pod::Simple::Text;
182 3         33 my $parser = Pod::Simple::Text->new;
183 3         409 my $buf;
184 3         24 $parser->output_string( \$buf );
185 3         1471 $parser->parse_file($fname);
186              
187 3         7714 my $base = ref $self->app;
188 3         85 $buf =~ s/\Q$base\E::(\w+)/\l$1/g;
189 3         19 $buf =~ s/^AUTHORS.*//sm;
190 3 100       14 $buf =~ s/^DESCRIPTION.*//sm unless $want_detail;
191 3         36 print $self->loc_text($buf);
192             }
193              
194             =head3 loc_text $text
195              
196             Localizes the body of (formatted) text in C<$text> and returns the
197             localized version.
198              
199             =cut
200              
201             sub loc_text {
202 4     4 1 13 my $self = shift;
203 4         9 my $buf = shift;
204              
205 4         12 my $out = "";
206 4         35 foreach my $line ( split( /\n\n+/, $buf, -1 ) ) {
207 11 100       215 if ( my @lines = $line =~ /^( {4}\s+.+\s*)$/mg ) {
    100          
    100          
208 1         3 foreach my $chunk (@lines) {
209 1 50       16 $chunk =~ /^(\s*)(.+?)( *)(: .+?)?(\s*)$/ or next;
210 1         6 my $spaces = $3;
211 1   50     11 my $loc = $1 . loc( $2 . ( $4 || '' ) ) . $5;
212 1 50       19 $loc =~ s/: /$spaces: / if $spaces;
213 1         6 $out .= $loc . "\n";
214             }
215 1         3 $out .= "\n";
216             }
217             elsif ( $line =~ /^(\s+)(\w+ - .*)$/ ) {
218 2         9 $out .= $1 . loc($2) . "\n\n";
219             }
220             elsif ( length $line ) {
221 5         25 $out .= loc($line) . "\n\n";
222             }
223             }
224 4         242 return $out;
225             }
226              
227             =head3 filename
228              
229             Return the filename for the command module.
230              
231             =cut
232              
233             sub filename {
234 7     7 1 19 my $self = shift;
235 7         20 my $fname = ref($self);
236 7         24 $fname =~ s{::[a-z]+$}{}; # subcommand
237 7         22 $fname =~ s{::}{/}g;
238 7         117 return $INC{"$fname.pm"};
239             }
240              
241             =head1 SEE ALSO
242              
243             L, L
244              
245             =head1 AUTHORS
246              
247             Chia-liang Kao Eclkao@clkao.orgE
248              
249             Cornelius Lin Ecornelius.howl@gmail.comE
250              
251             Shelling Enavyblueshellingford@gmail.comE
252              
253             Paul Cochrane Epaul@liekut.deE (current maintainer)
254              
255             =head1 COPYRIGHT
256              
257             Copyright 2005-2006 by Chia-liang Kao Eclkao@clkao.orgE.
258              
259             This program is free software; you can redistribute it and/or modify it
260             under the same terms as Perl itself.
261              
262             See L
263              
264             =cut
265              
266             1;