line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# ============================================================================« |
2
|
|
|
|
|
|
|
package MooseX::App; |
3
|
|
|
|
|
|
|
# ============================================================================« |
4
|
|
|
|
|
|
|
|
5
|
10
|
|
|
10
|
|
3169278
|
use 5.010; |
|
10
|
|
|
|
|
115
|
|
6
|
10
|
|
|
10
|
|
6436
|
use utf8; |
|
10
|
|
|
|
|
155
|
|
|
10
|
|
|
|
|
59
|
|
7
|
10
|
|
|
10
|
|
369
|
use strict; |
|
10
|
|
|
|
|
20
|
|
|
10
|
|
|
|
|
208
|
|
8
|
10
|
|
|
10
|
|
53
|
use warnings; |
|
10
|
|
|
|
|
20
|
|
|
10
|
|
|
|
|
582
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:MAROS'; |
11
|
|
|
|
|
|
|
our $VERSION = '1.42'; |
12
|
|
|
|
|
|
|
|
13
|
10
|
|
|
10
|
|
5658
|
use MooseX::App::Meta::Role::Attribute::Option; |
|
10
|
|
|
|
|
46
|
|
|
10
|
|
|
|
|
705
|
|
14
|
10
|
|
|
10
|
|
6111
|
use MooseX::App::Exporter qw(app_usage app_description app_base app_fuzzy app_strict app_prefer_commandline app_permute option parameter); |
|
10
|
|
|
|
|
40
|
|
|
10
|
|
|
|
|
107
|
|
15
|
10
|
|
|
10
|
|
6604
|
use MooseX::App::Message::Envelope; |
|
10
|
|
|
|
|
52
|
|
|
10
|
|
|
|
|
455
|
|
16
|
10
|
|
|
10
|
|
93
|
use Moose::Exporter; |
|
10
|
|
|
|
|
22
|
|
|
10
|
|
|
|
|
70
|
|
17
|
10
|
|
|
10
|
|
545
|
use Scalar::Util qw(blessed); |
|
10
|
|
|
|
|
22
|
|
|
10
|
|
|
|
|
10391
|
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
my ($IMPORT,$UNIMPORT,$INIT_META) = Moose::Exporter->build_import_methods( |
20
|
|
|
|
|
|
|
with_meta => [ qw(app_usage app_description app_namespace app_exclude app_base app_fuzzy app_command_name app_command_register app_strict app_prefer_commandline option parameter app_permute) ], |
21
|
|
|
|
|
|
|
also => [ 'Moose' ], |
22
|
|
|
|
|
|
|
as_is => [ 'new_with_command' ], |
23
|
|
|
|
|
|
|
install => [ 'unimport','init_meta' ], |
24
|
|
|
|
|
|
|
); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
sub import { |
27
|
11
|
|
|
11
|
|
652
|
my ( $class, @plugins ) = @_; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# Get caller |
30
|
11
|
|
|
|
|
51
|
my ($caller_class) = caller(); |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
# Process plugins |
33
|
11
|
|
|
|
|
314
|
MooseX::App::Exporter->process_plugins($caller_class,@plugins); |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# Call Moose-Exporter generated importer |
36
|
11
|
|
|
|
|
62
|
return $class->$IMPORT( { into => $caller_class } ); |
37
|
|
|
|
|
|
|
} |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
sub init_meta { |
40
|
11
|
|
|
11
|
0
|
1285
|
my ($class,%args) = @_; |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# Get required roles and metaroles |
43
|
11
|
|
|
|
|
46
|
$args{roles} = ['MooseX::App::Role::Base']; |
44
|
|
|
|
|
|
|
$args{metaroles} = { |
45
|
11
|
|
|
|
|
67
|
class => [ |
46
|
|
|
|
|
|
|
'MooseX::App::Meta::Role::Class::Base', |
47
|
|
|
|
|
|
|
'MooseX::App::Meta::Role::Class::Documentation' |
48
|
|
|
|
|
|
|
], |
49
|
|
|
|
|
|
|
attribute => [ |
50
|
|
|
|
|
|
|
'MooseX::App::Meta::Role::Attribute::Option' |
51
|
|
|
|
|
|
|
], |
52
|
|
|
|
|
|
|
}; |
53
|
|
|
|
|
|
|
|
54
|
11
|
|
|
|
|
112
|
return MooseX::App::Exporter->process_init_meta(%args); |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
sub app_command_name(&) { |
58
|
3
|
|
|
3
|
1
|
61
|
my ( $meta, $namesub ) = @_; |
59
|
3
|
|
|
|
|
112
|
return $meta->app_command_name($namesub); |
60
|
|
|
|
|
|
|
} |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub app_command_register(%) { |
63
|
0
|
|
|
0
|
1
|
0
|
my ( $meta, %commands ) = @_; |
64
|
|
|
|
|
|
|
|
65
|
0
|
|
|
|
|
0
|
foreach my $command (keys %commands) { |
66
|
0
|
|
|
|
|
0
|
$meta->command_register($command,$commands{$command}); |
67
|
|
|
|
|
|
|
} |
68
|
0
|
|
|
|
|
0
|
return; |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
sub app_namespace(@) { |
72
|
0
|
|
|
0
|
1
|
0
|
my ( $meta, @namespaces ) = @_; |
73
|
0
|
|
|
|
|
0
|
return $meta->app_namespace( \@namespaces ); |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
sub app_exclude(@) { |
77
|
3
|
|
|
3
|
1
|
51
|
my ( $meta, @namespaces ) = @_; |
78
|
3
|
|
|
|
|
109
|
return $meta->app_exclude( \@namespaces ); |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
sub new_with_command { |
82
|
70
|
|
|
70
|
1
|
64939
|
my ($class,@args) = @_; |
83
|
|
|
|
|
|
|
|
84
|
70
|
100
|
66
|
|
|
569
|
Moose->throw_error('new_with_command is a class method') |
85
|
|
|
|
|
|
|
if ! defined $class || blessed($class); |
86
|
|
|
|
|
|
|
|
87
|
69
|
|
|
|
|
490
|
my $meta = $class->meta; |
88
|
69
|
|
|
|
|
2373
|
my $metameta = $meta->meta; |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
# Sanity check |
91
|
69
|
100
|
66
|
|
|
1574
|
Moose->throw_error('new_with_command may only be called from the application base package:'.$class) |
92
|
|
|
|
|
|
|
if $metameta->does_role('MooseX::App::Meta::Role::Class::Command') |
93
|
|
|
|
|
|
|
|| ! $metameta->does_role('MooseX::App::Meta::Role::Class::Base'); |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
$meta->command_check() |
96
|
68
|
50
|
33
|
|
|
55909
|
if $ENV{APP_DEVELOPER} || $ENV{HARNESS_ACTIVE}; |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# Extra args |
99
|
68
|
|
|
|
|
143
|
my %args; |
100
|
68
|
50
|
33
|
|
|
488
|
if (scalar @args == 1 |
|
|
100
|
|
|
|
|
|
101
|
|
|
|
|
|
|
&& ref($args[0]) eq 'HASH' ) { |
102
|
0
|
|
|
|
|
0
|
%args = %{$args[0]}; |
|
0
|
|
|
|
|
0
|
|
103
|
|
|
|
|
|
|
} elsif (scalar @args % 2 == 0) { |
104
|
67
|
|
|
|
|
235
|
%args = @args; |
105
|
|
|
|
|
|
|
} else { |
106
|
1
|
|
|
|
|
6
|
Moose->throw_error('new_with_command got invalid extra arguments'); |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# Get ARGV |
110
|
67
|
|
|
|
|
272
|
my $argv = delete $args{ARGV}; |
111
|
67
|
|
|
|
|
129
|
my $parsed_argv; |
112
|
67
|
100
|
|
|
|
181
|
if (defined $argv) { |
113
|
7
|
|
|
|
|
245
|
$parsed_argv = MooseX::App::ParsedArgv->new( argv => $argv ); |
114
|
|
|
|
|
|
|
} else { |
115
|
60
|
|
|
|
|
324
|
$parsed_argv = MooseX::App::ParsedArgv->instance(); |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
67
|
|
|
|
|
287
|
my $first_argv = $parsed_argv->first_argv; |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
# Requested help |
121
|
67
|
100
|
100
|
|
|
1240
|
if (defined $first_argv |
|
|
100
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
122
|
|
|
|
|
|
|
&& lc($first_argv) =~ m/^(help|h|\?|usage|-h|--help|-\?|--usage)$/) { |
123
|
2
|
|
|
|
|
46
|
return MooseX::App::Message::Envelope->new( |
124
|
|
|
|
|
|
|
$meta->command_usage_global(), |
125
|
|
|
|
|
|
|
); |
126
|
|
|
|
|
|
|
# No args |
127
|
|
|
|
|
|
|
} elsif (! defined $first_argv |
128
|
|
|
|
|
|
|
|| $first_argv =~ m/^\s*$/ |
129
|
|
|
|
|
|
|
|| $first_argv =~ m/^-{1,2}\w/) { |
130
|
2
|
|
|
|
|
18
|
return MooseX::App::Message::Envelope->new( |
131
|
|
|
|
|
|
|
$meta->command_message( |
132
|
|
|
|
|
|
|
header => "Missing command", # LOCALIZE |
133
|
|
|
|
|
|
|
type => "error", |
134
|
|
|
|
|
|
|
), |
135
|
|
|
|
|
|
|
$meta->command_usage_global(), |
136
|
|
|
|
|
|
|
127, # exitcode |
137
|
|
|
|
|
|
|
); |
138
|
|
|
|
|
|
|
# Looks like a command |
139
|
|
|
|
|
|
|
} else { |
140
|
63
|
|
|
|
|
368
|
my $return = $meta->command_find(); |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
# Nothing found |
143
|
63
|
100
|
66
|
|
|
383
|
if (blessed $return |
144
|
|
|
|
|
|
|
&& $return->isa('MooseX::App::Message::Block')) { |
145
|
2
|
|
|
|
|
12
|
return MooseX::App::Message::Envelope->new( |
146
|
|
|
|
|
|
|
$return, |
147
|
|
|
|
|
|
|
$meta->command_usage_global(), |
148
|
|
|
|
|
|
|
127, # exitcode |
149
|
|
|
|
|
|
|
); |
150
|
|
|
|
|
|
|
# One command found |
151
|
|
|
|
|
|
|
} else { |
152
|
61
|
|
|
|
|
2706
|
my $command_class = $meta->command_get($return); |
153
|
61
|
|
|
|
|
442
|
return $class->initialize_command_class($command_class,%args); |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
|
159
|
10
|
|
|
10
|
|
106
|
no Moose; |
|
10
|
|
|
|
|
30
|
|
|
10
|
|
|
|
|
87
|
|
160
|
|
|
|
|
|
|
1; |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
__END__ |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=encoding utf8 |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=head1 NAME |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
MooseX::App - Write user-friendly command line apps with even less suffering |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=head1 SYNOPSIS |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
In your base class: |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
package MyApp; |
175
|
|
|
|
|
|
|
use MooseX::App qw(Color); |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
option 'global_option' => ( |
178
|
|
|
|
|
|
|
is => 'rw', |
179
|
|
|
|
|
|
|
isa => 'Bool', |
180
|
|
|
|
|
|
|
documentation => q[Enable this to do fancy stuff], |
181
|
|
|
|
|
|
|
); # Global option |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
has 'private' => ( |
184
|
|
|
|
|
|
|
is => 'rw', |
185
|
|
|
|
|
|
|
); # not exposed |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
Write multiple command classes (If you have only a single command class |
188
|
|
|
|
|
|
|
you should use L<MooseX::App::Simple> instead). Packackes in the namespace may be |
189
|
|
|
|
|
|
|
deeply nested. |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
package MyApp::SomeCommand; |
192
|
|
|
|
|
|
|
use MooseX::App::Command; # important (also imports Moose) |
193
|
|
|
|
|
|
|
extends qw(MyApp); # optional, only if you want to use global options from base class |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# Positional parameter |
196
|
|
|
|
|
|
|
parameter 'some_parameter' => ( |
197
|
|
|
|
|
|
|
is => 'rw', |
198
|
|
|
|
|
|
|
isa => 'Str', |
199
|
|
|
|
|
|
|
required => 1, |
200
|
|
|
|
|
|
|
documentation => q[Some parameter that you need to supply], |
201
|
|
|
|
|
|
|
); |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
option 'some_option' => ( |
204
|
|
|
|
|
|
|
is => 'rw', |
205
|
|
|
|
|
|
|
isa => 'Int', |
206
|
|
|
|
|
|
|
required => 1, |
207
|
|
|
|
|
|
|
documentation => q[Very important option!], |
208
|
|
|
|
|
|
|
); # Option |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
sub run { |
211
|
|
|
|
|
|
|
my ($self) = @_; |
212
|
|
|
|
|
|
|
# Do something |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
And then you need a simple wrapper script (called eg. myapp): |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
#!/usr/bin/env perl |
218
|
|
|
|
|
|
|
use MyApp; |
219
|
|
|
|
|
|
|
MyApp->new_with_command->run; |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
On the command line: |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
bash$ myapp help |
224
|
|
|
|
|
|
|
usage: |
225
|
|
|
|
|
|
|
myapp <command> [long options...] |
226
|
|
|
|
|
|
|
myapp help |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
global options: |
229
|
|
|
|
|
|
|
--global_option Enable this to do fancy stuff [Flag] |
230
|
|
|
|
|
|
|
--help --usage -? Prints this usage information. [Flag] |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
available commands: |
233
|
|
|
|
|
|
|
some_command Description of some command |
234
|
|
|
|
|
|
|
another_command Description of another command |
235
|
|
|
|
|
|
|
help Prints this usage information |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
or |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
bash$ myapp some_command --help |
240
|
|
|
|
|
|
|
usage: |
241
|
|
|
|
|
|
|
myapp some_command <SOME_PARAMETER> [long options...] |
242
|
|
|
|
|
|
|
myapp help |
243
|
|
|
|
|
|
|
myapp some_command --help |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
parameters: |
246
|
|
|
|
|
|
|
some_parameter Some parameter that you need to supply [Required] |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
options: |
249
|
|
|
|
|
|
|
--global_option Enable this to do fancy stuff [Flag] |
250
|
|
|
|
|
|
|
--some_option Very important option! [Int,Required] |
251
|
|
|
|
|
|
|
--help --usage -? Prints this usage information. [Flag] |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
=head1 DESCRIPTION |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
MooseX-App is a highly customisable helper to write user-friendly |
256
|
|
|
|
|
|
|
command line applications without having to worry about most of the annoying |
257
|
|
|
|
|
|
|
things usually involved. Just take any existing L<Moose> class, add a single |
258
|
|
|
|
|
|
|
line (C<use MooseX-App qw(PluginA PluginB ...);>) and create one class |
259
|
|
|
|
|
|
|
for each command in an underlying namespace. Options and positional parameters |
260
|
|
|
|
|
|
|
can be defined as simple L<Moose> accessors using the C<option> and |
261
|
|
|
|
|
|
|
C<parameter> keywords respectively. |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
MooseX-App will then |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
=over |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
=item * Find, load and initialise the command classes (see |
268
|
|
|
|
|
|
|
L<MooseX::App::Simple> for single class/command applications) |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
=item * Create automated help and documentation from modules POD as well as |
271
|
|
|
|
|
|
|
attributes metadata and type constraints |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
=item * Read, encode and validate the command line options and positional |
274
|
|
|
|
|
|
|
parameters entered by the user from @ARGV and %ENV (and possibly prompt |
275
|
|
|
|
|
|
|
the user for additional parameters see L<MooseX::App::Plugin::Term>) |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=item * Provide helpful error messages if user input cannot be validated |
278
|
|
|
|
|
|
|
(either missing or wrong attributes or Moose type constraints not satisfied) |
279
|
|
|
|
|
|
|
or if the user requests help. |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
=back |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
Commandline options are defined using the 'option' keyword which accepts |
284
|
|
|
|
|
|
|
the same attributes as Moose' 'has' keyword. |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
option 'some_option' => ( |
287
|
|
|
|
|
|
|
is => 'rw', |
288
|
|
|
|
|
|
|
isa => 'Str', |
289
|
|
|
|
|
|
|
); |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
This is equivalent to |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
has 'some_option' => ( |
294
|
|
|
|
|
|
|
is => 'rw', |
295
|
|
|
|
|
|
|
isa => 'Str', |
296
|
|
|
|
|
|
|
traits => ['AppOption'], # Load extra metaclass |
297
|
|
|
|
|
|
|
cmd_type => 'option', # Set attribute type |
298
|
|
|
|
|
|
|
); |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
Single letter options are treated as flags and may be combined with each other. |
301
|
|
|
|
|
|
|
However such options must have a Boolean type constraint. |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
option 'verbose' => ( |
304
|
|
|
|
|
|
|
is => 'rw', |
305
|
|
|
|
|
|
|
isa => 'Bool', |
306
|
|
|
|
|
|
|
cmd_flag => 'v', |
307
|
|
|
|
|
|
|
); |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
Positional parameters are defined with the 'parameter' keyword |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
parameter 'some_option' => ( |
312
|
|
|
|
|
|
|
is => 'rw', |
313
|
|
|
|
|
|
|
isa => 'Str', |
314
|
|
|
|
|
|
|
); |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
This is equivalent to |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
has 'some_option' => ( |
319
|
|
|
|
|
|
|
is => 'rw', |
320
|
|
|
|
|
|
|
isa => 'Str', |
321
|
|
|
|
|
|
|
traits => ['AppOption'], |
322
|
|
|
|
|
|
|
cmd_type => 'parameter', |
323
|
|
|
|
|
|
|
); |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
All keywords are imported by L<Moosex::App> (in the app base class) and |
326
|
|
|
|
|
|
|
L<MooseX::App::Command> (in the command class) or L<MooseX::App::Simple> |
327
|
|
|
|
|
|
|
(single class application). |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
Furthermore, all options and parameters can also be supplied via %ENV |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
option 'some_option' => ( |
332
|
|
|
|
|
|
|
is => 'rw', |
333
|
|
|
|
|
|
|
isa => 'Str', |
334
|
|
|
|
|
|
|
cmd_env => 'SOME_OPTION', # sets the env key |
335
|
|
|
|
|
|
|
); |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
Moose type constraints help MooseX::App to construct helpful error messages |
338
|
|
|
|
|
|
|
and parse @ARGV in a meaningful way. The following type constraints are |
339
|
|
|
|
|
|
|
supported: |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
=over |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
=item * ArrayRef: Specify multiple values ('--opt value1 --opt value2', |
344
|
|
|
|
|
|
|
also see L<app_permute> and L<cmd_split>) |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
=item * HashRef: Specify multiple key value pairs ('--opt key=value --opt |
347
|
|
|
|
|
|
|
key2=value2', also see L<app_permute>) |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
=item * Enum: Display all possibilities |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
=item * Bool: Flags that do not require values |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=item * Int, Num: Used for proper error messages |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
=back |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
Read the L<Tutorial|MooseX::App::Tutorial> for getting started with a simple |
358
|
|
|
|
|
|
|
MooseX::App command line application. |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
=head1 METHODS |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
=head2 new_with_command |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
my $myapp_command = MyApp->new_with_command(); |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
This constructor reads the command line arguments and tries to create a |
367
|
|
|
|
|
|
|
command class instance. If it fails it returns a |
368
|
|
|
|
|
|
|
L<MooseX::App::Message::Envelope> object holding an error message. |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
You can pass a hash of default/fallback params to new_with_command |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
my $obj = MyApp->new_with_command(%default); |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
Optionally you can pass a custom ARGV to this constructor |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
my $obj = MyApp->new_with_command( ARGV => \@myARGV ); |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
However, if you do so you must take care of propper @ARGV encoding yourself. |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
=head2 initialize_command_class |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
my $obj = MyApp->initialize_command_class($command_name,%default); |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
Helper method to instantiate the command class for the given command. |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
=head1 GLOBAL OPTIONS |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
These options may be used to alter the default behaviour of MooseX-App. |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
=head2 app_base |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
app_base 'my_script'; # Defaults to $0 |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
Usually MooseX::App will take the name of the calling wrapper script to |
395
|
|
|
|
|
|
|
construct the program name in various help messages. This name can |
396
|
|
|
|
|
|
|
be changed via the app_base function. |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
=head2 app_fuzzy |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
app_fuzzy 1; # default |
401
|
|
|
|
|
|
|
OR |
402
|
|
|
|
|
|
|
app_fuzzy 0; |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
Enables fuzzy matching of commands and attributes. Is turned on by default. |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
=head2 app_strict |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
app_strict 0; # default |
409
|
|
|
|
|
|
|
OR |
410
|
|
|
|
|
|
|
app_strict 1; |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
If strict is enabled the program will terminate with an error message if |
413
|
|
|
|
|
|
|
superfluous/unknown positional parameters are supplied. If disabled all |
414
|
|
|
|
|
|
|
extra parameters will be copied to the L<extra_argv> attribute. Unknown |
415
|
|
|
|
|
|
|
options (with leading dashes) will always yield an error message. |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
The command_strict config in the command classes allows one to set this option |
418
|
|
|
|
|
|
|
individually for each command in the respective command class. |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
=head2 app_prefer_commandline |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
app_prefer_commandline 0; # default |
423
|
|
|
|
|
|
|
or |
424
|
|
|
|
|
|
|
app_prefer_commandline 1; |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
Specifies if parameters/options supplied via @ARGV,%ENV should take precedence |
427
|
|
|
|
|
|
|
over arguments passed directly to new_with_command. |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
=head2 app_namespace |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
app_namespace 'MyApp::Commands', 'YourApp::MoreCommands'; |
432
|
|
|
|
|
|
|
OR |
433
|
|
|
|
|
|
|
app_namespace(); |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
Usually MooseX::App will take the package name of the base class as the |
436
|
|
|
|
|
|
|
namespace for commands. This namespace can be changed and you can add |
437
|
|
|
|
|
|
|
multiple extra namespaces. |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
If app_namespace is called with no arguments then autoloading of command |
440
|
|
|
|
|
|
|
classes will be disabled entirely. |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
=head2 app_exclude |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
app_exclude 'MyApp::Commands::Roles','MyApp::Commands::Utils'; |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
A sub namespace included via L<app_namespace> (or the default behaviour) can |
447
|
|
|
|
|
|
|
be excluded using app_exclude. |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
=head2 app_command_name |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
app_command_name { |
452
|
|
|
|
|
|
|
my ($package_short,$package_full) = @_; |
453
|
|
|
|
|
|
|
# munge package name; |
454
|
|
|
|
|
|
|
return $command_name; |
455
|
|
|
|
|
|
|
}; |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
This coderef can be used to control how autoloaded package names should be |
458
|
|
|
|
|
|
|
translated to command names. If this command returns nothing the respective |
459
|
|
|
|
|
|
|
command class will be skipped and not loaded. |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
=head2 app_command_register |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
app_command_register |
464
|
|
|
|
|
|
|
do => 'MyApp::Commands::DoSomething', |
465
|
|
|
|
|
|
|
undo => 'MyApp::Commands::UndoSomething'; |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
This keyword can be used to register additional commands. Especially |
468
|
|
|
|
|
|
|
useful in conjunction with L<app_namespace> and disabled autoloading. |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
=head2 app_description |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
app_description qq[Description text]; |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
Set the app description text. If not set this information will be taken from |
475
|
|
|
|
|
|
|
the Pod DESCRIPTION or OVERVIEW sections. (see command_description to set |
476
|
|
|
|
|
|
|
usage per command) |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
=head2 app_usage |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
app_usage qq[myapp --option ...]; |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
Set a custom usage text. If not set this will be taken from the Pod SYNOPSIS |
483
|
|
|
|
|
|
|
or USAGE section. If both sections are not available, the usage information |
484
|
|
|
|
|
|
|
will be autogenerated. (see command_usage to set usage per command) |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
=head2 app_permute |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
app_permute 0; # default |
489
|
|
|
|
|
|
|
OR |
490
|
|
|
|
|
|
|
app_permute 1; |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
Allows one to specify multiple values with one key. So instead of writing |
493
|
|
|
|
|
|
|
C<--list element1 --list element2 --list element3> one might write |
494
|
|
|
|
|
|
|
C<--list element1 element2 element3> for ArrayRef elements. HashRef elements |
495
|
|
|
|
|
|
|
may be expressed as C<--hash key=value key2=value2>. |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
=head1 GLOBAL ATTRIBUTES |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
All MooseX::App classes will have two extra attributes |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
=head2 extra_argv |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
Carries all parameters from @ARGV that were not consumed (only if app_strict |
504
|
|
|
|
|
|
|
is turned off, otherwise superfluous parameters will raise an exception). |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
=head2 help_flag |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
Help flag that is set when help was requested. |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
=head1 ATTRIBUTE OPTIONS |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
Options and parameters accept extra attributes for customisation: |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
=over |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
=item * cmd_tags - Extra tags (as used by the help) |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
=item * cmd_flag - Override option/parameter name |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
=item * cmd_aliases - Additional option/parameter name aliases |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
=item * cmd_split - Split values into ArrayRefs on this token or RegEx |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
=item * cmd_position - Specify option/parameter order in help |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=item * cmd_env - Read options/parameters from %ENV |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
=item * cmd_count - Value of option equals to number of occurrences in @ARGV |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=item * cmd_negate - Adds an option to negate boolean flags |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
=back |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
Refer to L<MooseX::App::Meta::Role::Attribute::Option> for detailed |
535
|
|
|
|
|
|
|
documentation. |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
=head1 METADATA |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
MooseX::App will use your class metadata and POD to construct the commands and |
540
|
|
|
|
|
|
|
helpful error- or usage-messages. These bits of information are utilised |
541
|
|
|
|
|
|
|
and should be provided if possible: |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
=over |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
=item * Package names |
546
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
=item * L<required|Moose/"required-=E<gt>-(1|0)"> options for Moose attributes |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
=item * L<documentation|Moose/"documentation =E<gt> $string"> options for Moose attributes |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
=item * Moose type constraints (Bool, ArrayRef, HashRef, Int, Num, and Enum) |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=item * Documentation set via app_description, app_usage, |
554
|
|
|
|
|
|
|
command_short_description, command_long_description and command_usage |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
=item * POD (NAME, ABSTRACT, DESCRIPTION, USAGE, SYNOPSIS, OVERVIEW, |
557
|
|
|
|
|
|
|
COPYRIGHT, LICENSE, COPYRIGHT AND LICENSE, AUTHOR and AUTHORS sections) |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
=item * L<Dist::Zilla> ABSTRACT tag if no POD is available yet |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
=back |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
=head1 PLUGINS |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
The behaviour of MooseX-App can be customised with plugins. To load a |
566
|
|
|
|
|
|
|
plugin just pass a list of plugin names after the C<use MooseX-App> statement. |
567
|
|
|
|
|
|
|
(Attention: order sometimes matters) |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
use MooseX::App qw(PluginA PluginB); |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
Currently the following plugins are shipped with MooseX::App |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
=over |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
=item * L<MooseX::App::Plugin::BashCompletion> |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
Adds a command that generates a bash completion script for your application. |
578
|
|
|
|
|
|
|
See third party L<MooseX::App::Plugin::ZshCompletion> for Z shell completion. |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
=item * L<MooseX::App::Plugin::Color> |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
Colorful output for your MooseX::App applications. |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
=item * L<MooseX::App::Plugin::Config> |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
Config files for MooseX::App applications. |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
=item * L<MooseX::App::Plugin::ConfigHome> |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
Try to find config files in users home directory. |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
=item * L<MooseX::App::Plugin::Term> |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
Prompt user for options and parameters that were not provided via options or |
595
|
|
|
|
|
|
|
params. Prompt offers basic editing capabilities and non-persistent history. |
596
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
=item * L<MooseX::App::Plugin::Typo> |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
Handle typos in command names and provide suggestions. |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
=item * L<MooseX::App::Plugin::Version> |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
Adds a command to display the version and license of your application. |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
=item * L<MooseX::App::Plugin::Man> |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
Display full manpage of application and commands. |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
=item * L<MooseX::App::Plugin::MutexGroup> |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
Allow for mutally exclusive options. |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
=item * L<MooseX::App::Plugin::Depends> |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
Adds dependent options. |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
=back |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
Refer to L<Writing MooseX-App Plugins|MooseX::App::WritingPlugins> |
620
|
|
|
|
|
|
|
for documentation on how to create your own plugins. |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
=head1 DEVELOPMENT |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
Make sure to invoke your script with APP_DEVELOPER=1 ser during development. This |
625
|
|
|
|
|
|
|
will come with a starup penalty but perform additional checks for detecting wrong |
626
|
|
|
|
|
|
|
attribute/type constraint combinations, name clashes, ... |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
=head1 CAVEATS & KNOWN BUGS |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
Startup time may be an issue - escpecially if you load many plugins. If you do |
631
|
|
|
|
|
|
|
not require the functionality of plugins and ability for fine grained |
632
|
|
|
|
|
|
|
customisation (or Moose for that matter) then you should probably |
633
|
|
|
|
|
|
|
use L<MooX::Options> or L<MooX::Cmd>. |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
In some cases - especially when using non-standard class inheritance - you may |
636
|
|
|
|
|
|
|
end up with command classes lacking the help attribute. In this case you need |
637
|
|
|
|
|
|
|
to include the following line in your base class or command classes. |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
with qw(MooseX::App::Role::Common); |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
When manually registering command classes (eg. via app_command_register) in |
642
|
|
|
|
|
|
|
multiple base classes with different sets of plugins (why would you ever want |
643
|
|
|
|
|
|
|
to do that?), then meta attributes may lack some attribute metaclasses. In |
644
|
|
|
|
|
|
|
this case you need to load the missing attribute traits explicitly: |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
option 'argument' => ( |
647
|
|
|
|
|
|
|
depends => 'otherargument', |
648
|
|
|
|
|
|
|
trait => ['MooseX::App::Plugin::Depends::Meta::Attribute'], # load trait |
649
|
|
|
|
|
|
|
); |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
=head1 SEE ALSO |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
Read the L<Tutorial|MooseX::App::Tutorial> for getting started with a simple |
654
|
|
|
|
|
|
|
MooseX::App command line application. |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
For alternatives you can check out |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
L<MooseX::App::Cmd>, L<MooseX::Getopt>, L<MooX::Options>, L<MooX::Cmd> and L<App::Cmd> |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
=head1 SUPPORT |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
Please report any bugs or feature requests via |
663
|
|
|
|
|
|
|
L<https://github.com/maros/MooseX-App/issues/new>. I will be notified, and |
664
|
|
|
|
|
|
|
then you'll automatically be notified of progress on your report as I make |
665
|
|
|
|
|
|
|
changes. |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
=head1 AUTHOR |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
Maroš Kollár |
670
|
|
|
|
|
|
|
CPAN ID: MAROS |
671
|
|
|
|
|
|
|
maros [at] k-1.com |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
http://www.k-1.com |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
=head1 CONTRIBUTORS |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
Special thanks to all contributors. |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
In no particular order: Andrew Jones, George Hartzell, Steve Nolte, |
680
|
|
|
|
|
|
|
Michael G, Thomas Klausner, Yanick Champoux, Edward Baudrez, David Golden, |
681
|
|
|
|
|
|
|
J.R. Mash, Thilo Fester, Gregor Herrmann, Sergey Romanov, Sawyer X, Roman F., |
682
|
|
|
|
|
|
|
Hunter McMillen, Maik Hentsche, Alexander Stoddard, Marc Logghe, Tina Müller, |
683
|
|
|
|
|
|
|
Lisa Hare, Jose Luis Martinez, Frank Schreiner |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
You are more than welcome to contribute to MooseX-App. Please have a look |
686
|
|
|
|
|
|
|
at the L<https://github.com/maros/MooseX-App/issues?q=is%3Aissue+is%3Aopen+label%3AWishlist> |
687
|
|
|
|
|
|
|
list of open wishlist issues for ideas. |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
=head1 COPYRIGHT |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
MooseX::App is Copyright (c) 2012-21 Maroš Kollár. |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
This library is free software and may be distributed under the same terms as |
694
|
|
|
|
|
|
|
perl itself. The full text of the licence can be found in the LICENCE file |
695
|
|
|
|
|
|
|
included with this module. |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
=cut |