_usage_format method
[gitmo/MooseX-Getopt.git] / lib / MooseX / Getopt.pm
CommitLineData
5dac17c3 1
2package MooseX::Getopt;
3use Moose::Role;
4
6bb4cb66 5use Getopt::Long::Descriptive ();
5dac17c3 6
8034a232 7use MooseX::Getopt::OptionTypeMap;
5dac17c3 8use MooseX::Getopt::Meta::Attribute;
0f8232b6 9use MooseX::Getopt::Meta::Attribute::NoGetopt;
5dac17c3 10
384fb15d 11our $VERSION = '0.08';
8034a232 12our $AUTHORITY = 'cpan:STEVAN';
13
913e7020 14has ARGV => (is => 'rw', isa => 'ArrayRef', documentation => "hidden");
15has extra_argv => (is => 'rw', isa => 'ArrayRef', documentation => "hidden");
3899e5df 16
5dac17c3 17sub new_with_options {
bff3807b 18 my ($class, @params) = @_;
5dac17c3 19
26be7f7e 20 my %processed = $class->_parse_argv(
21 options => [
bff3807b 22 $class->_attrs_to_options( @params )
26be7f7e 23 ]
24 );
ee211848 25
ee69c4ba 26 my $params = $processed{params};
27
28 if($class->meta->does_role('MooseX::ConfigFromFile')
29 && defined $params->{configfile}) {
30 %$params = (
31 %{$class->get_config_from_file($params->{configfile})},
32 %$params,
33 );
34 }
35
ee211848 36 $class->new(
37 ARGV => $processed{argv_copy},
38 extra_argv => $processed{argv},
bff3807b 39 @params, # explicit params to ->new
ee69c4ba 40 %$params, # params from CLI
ee211848 41 );
42}
43
44sub _parse_argv {
0e715336 45 my ( $class, %params ) = @_;
6bb4cb66 46
0e715336 47 local @ARGV = @{ $params{argv} || \@ARGV };
48
49 my ( $opt_spec, $name_to_init_arg ) = $class->_gld_spec(%params);
ee211848 50
51 # Get a clean copy of the original @ARGV
52 my $argv_copy = [ @ARGV ];
53
6bb4cb66 54 my @err;
55
56 my ( $parsed_options, $usage ) = eval {
57 local $SIG{__WARN__} = sub { push @err, @_ };
9287e912 58 Getopt::Long::Descriptive::describe_options($class->_usage_format(%params), @$opt_spec)
6bb4cb66 59 };
60
61 die join "", grep { defined } @err, $@ if @err or $@;
ee211848 62
63 # Get a copy of the Getopt::Long-mangled @ARGV
64 my $argv_mangled = [ @ARGV ];
65
0e715336 66 my %constructor_args = (
67 map {
68 $name_to_init_arg->{$_} => $parsed_options->{$_}
69 } keys %$parsed_options,
ee211848 70 );
0e715336 71
913e7020 72 return (
73 params => \%constructor_args,
74 argv_copy => $argv_copy,
75 argv => $argv_mangled,
76 usage => $usage
77 );
0e715336 78}
79
9287e912 80sub _usage_format {
81 return "usage: %c %o";
82}
83
0e715336 84sub _gld_spec {
85 my ( $class, %params ) = @_;
86
87 my ( @options, %name_to_init_arg );
88
89 foreach my $opt ( @{ $params{options} } ) {
90 push @options, [
91 $opt->{opt_string},
92 $opt->{doc} || ' ', # FIXME new GLD shouldn't need this hack
93 {
94 ( $opt->{required} ? (required => $opt->{required}) : () ),
95 ( exists $opt->{default} ? (default => $opt->{default}) : () ),
96 },
97 ];
98
99 $name_to_init_arg{ $opt->{name} } = $opt->{init_arg};
100 }
101
102 return ( \@options, \%name_to_init_arg );
ee211848 103}
104
bff3807b 105sub _compute_getopt_attrs {
106 my $class = shift;
bff3807b 107 grep {
108 $_->isa("MooseX::Getopt::Meta::Attribute")
109 or
110 $_->name !~ /^_/
111 &&
0f8232b6 112 !$_->isa('MooseX::Getopt::Meta::Attribute::NoGetopt')
bff3807b 113 } $class->meta->compute_all_applicable_attributes
114}
115
ee211848 116sub _attrs_to_options {
117 my $class = shift;
118
119 my @options;
120
bff3807b 121 foreach my $attr ($class->_compute_getopt_attrs) {
5dac17c3 122 my $name = $attr->name;
3899e5df 123
de75868f 124 my $aliases;
125
126 if ($attr->isa('MooseX::Getopt::Meta::Attribute')) {
127 $name = $attr->cmd_flag if $attr->has_cmd_flag;
128 $aliases = $attr->cmd_aliases if $attr->has_cmd_aliases;
3899e5df 129 }
ee211848 130
de75868f 131 my $opt_string = $aliases
132 ? join(q{|}, $name, @$aliases)
133 : $name;
134
5dac17c3 135 if ($attr->has_type_constraint) {
136 my $type_name = $attr->type_constraint->name;
6bb4cb66 137 if (MooseX::Getopt::OptionTypeMap->has_option_type($type_name)) {
138 $opt_string .= MooseX::Getopt::OptionTypeMap->get_option_type($type_name)
5dac17c3 139 }
140 }
f63e6310 141
ee211848 142 push @options, {
143 name => $name,
144 init_arg => $attr->init_arg,
145 opt_string => $opt_string,
146 required => $attr->is_required,
6bb4cb66 147 ( ( $attr->has_default && ( $attr->is_default_a_coderef xor $attr->is_lazy ) ) ? ( default => $attr->default({}) ) : () ),
ee211848 148 ( $attr->has_documentation ? ( doc => $attr->documentation ) : () ),
149 }
f63e6310 150 }
151
ee211848 152 return @options;
5dac17c3 153}
154
8034a232 155no Moose::Role; 1;
5dac17c3 156
157__END__
158
159=pod
160
161=head1 NAME
162
8034a232 163MooseX::Getopt - A Moose role for processing command line options
5dac17c3 164
165=head1 SYNOPSIS
166
167 ## In your class
168 package My::App;
169 use Moose;
170
171 with 'MooseX::Getopt';
172
173 has 'out' => (is => 'rw', isa => 'Str', required => 1);
174 has 'in' => (is => 'rw', isa => 'Str', required => 1);
175
176 # ... rest of the class here
177
178 ## in your script
179 #!/usr/bin/perl
180
181 use My::App;
182
183 my $app = My::App->new_with_options();
184 # ... rest of the script here
185
186 ## on the command line
187 % perl my_app_script.pl -in file.input -out file.dump
188
189=head1 DESCRIPTION
190
8034a232 191This is a role which provides an alternate constructor for creating
192objects using parameters passed in from the command line.
193
194This module attempts to DWIM as much as possible with the command line
195params by introspecting your class's attributes. It will use the name
196of your attribute as the command line option, and if there is a type
197constraint defined, it will configure Getopt::Long to handle the option
3899e5df 198accordingly.
199
200You can use the attribute metaclass L<MooseX::Getopt::Meta::Attribute>
201to get non-default commandline option names and aliases.
202
0f8232b6 203You can use the attribute metaclass L<MooseX::Getopt::Meta::Attribute::NoGetOpt>
204to have C<MooseX::Getopt> ignore your attribute in the commandline options.
205
3899e5df 206By default, attributes which start with an underscore are not given
207commandline argument support, unless the attribute's metaclass is set
3d9a716d 208to L<MooseX::Getopt::Meta::Attribute>. If you don't want you accessors
209to have the leading underscore in thier name, you can do this:
210
211 # for read/write attributes
212 has '_foo' => (accessor => 'foo', ...);
213
214 # or for read-only attributes
215 has '_bar' => (reader => 'bar', ...);
216
217This will mean that Getopt will not handle a --foo param, but your
218code can still call the C<foo> method.
8034a232 219
ee69c4ba 220If your class also uses a configfile-loading role based on
221L<MooseX::ConfigFromFile>, such as L<MooseX::SimpleConfig>,
222L<MooseX::Getopt>'s C<new_with_options> will load the configfile
223specified by the C<--configfile> option for you.
224
8034a232 225=head2 Supported Type Constraints
226
227=over 4
228
229=item I<Bool>
230
231A I<Bool> type constraint is set up as a boolean option with
232Getopt::Long. So that this attribute description:
233
234 has 'verbose' => (is => 'rw', isa => 'Bool');
235
236would translate into C<verbose!> as a Getopt::Long option descriptor,
237which would enable the following command line options:
238
239 % my_script.pl --verbose
240 % my_script.pl --noverbose
241
242=item I<Int>, I<Float>, I<Str>
243
244These type constraints are set up as properly typed options with
245Getopt::Long, using the C<=i>, C<=f> and C<=s> modifiers as appropriate.
246
247=item I<ArrayRef>
248
249An I<ArrayRef> type constraint is set up as a multiple value option
250in Getopt::Long. So that this attribute description:
251
252 has 'include' => (
253 is => 'rw',
254 isa => 'ArrayRef',
255 default => sub { [] }
256 );
257
258would translate into C<includes=s@> as a Getopt::Long option descriptor,
259which would enable the following command line options:
260
261 % my_script.pl --include /usr/lib --include /usr/local/lib
262
263=item I<HashRef>
264
265A I<HashRef> type constraint is set up as a hash value option
266in Getopt::Long. So that this attribute description:
267
268 has 'define' => (
269 is => 'rw',
270 isa => 'HashRef',
271 default => sub { {} }
272 );
273
274would translate into C<define=s%> as a Getopt::Long option descriptor,
275which would enable the following command line options:
276
277 % my_script.pl --define os=linux --define vendor=debian
278
279=back
280
281=head2 Custom Type Constraints
282
283It is possible to create custom type constraint to option spec
284mappings if you need them. The process is fairly simple (but a
285little verbose maybe). First you create a custom subtype, like
286so:
287
288 subtype 'ArrayOfInts'
289 => as 'ArrayRef'
290 => where { scalar (grep { looks_like_number($_) } @$_) };
291
292Then you register the mapping, like so:
293
294 MooseX::Getopt::OptionTypeMap->add_option_type_to_map(
295 'ArrayOfInts' => '=i@'
296 );
297
298Now any attribute declarations using this type constraint will
299get the custom option spec. So that, this:
300
301 has 'nums' => (
302 is => 'ro',
303 isa => 'ArrayOfInts',
304 default => sub { [0] }
305 );
306
307Will translate to the following on the command line:
308
309 % my_script.pl --nums 5 --nums 88 --nums 199
310
311This example is fairly trivial, but more complex validations are
312easily possible with a little creativity. The trick is balancing
313the type constraint validations with the Getopt::Long validations.
314
315Better examples are certainly welcome :)
316
f63e6310 317=head2 Inferred Type Constraints
318
319If you define a custom subtype which is a subtype of one of the
320standard L</Supported Type Constraints> above, and do not explicitly
321provide custom support as in L</Custom Type Constraints> above,
322MooseX::Getopt will treat it like the parent type for Getopt
323purposes.
324
325For example, if you had the same custom C<ArrayOfInts> subtype
326from the examples above, but did not add a new custom option
327type for it to the C<OptionTypeMap>, it would be treated just
328like a normal C<ArrayRef> type for Getopt purposes (that is,
329C<=s@>).
330
5dac17c3 331=head1 METHODS
332
333=over 4
334
335=item B<new_with_options (%params)>
336
8034a232 337This method will take a set of default C<%params> and then collect
338params from the command line (possibly overriding those in C<%params>)
339and then return a newly constructed object.
340
f63e6310 341If L<Getopt::Long/GetOptions> fails (due to invalid arguments),
342C<new_with_options> will throw an exception.
343
3899e5df 344=item B<ARGV>
345
346This accessor contains a reference to a copy of the C<@ARGV> array
f63e6310 347as it originally existed at the time of C<new_with_options>.
348
349=item B<extra_argv>
350
351This accessor contains an arrayref of leftover C<@ARGV> elements that
352L<Getopt::Long> did not parse. Note that the real C<@ARGV> is left
353un-mangled.
3899e5df 354
5dac17c3 355=item B<meta>
356
8034a232 357This returns the role meta object.
358
5dac17c3 359=back
360
361=head1 BUGS
362
363All complex software has bugs lurking in it, and this module is no
364exception. If you find a bug please either email me, or add the bug
365to cpan-RT.
366
367=head1 AUTHOR
368
369Stevan Little E<lt>stevan@iinteractive.comE<gt>
370
e2911e34 371Brandon L. Black, E<lt>blblack@gmail.comE<gt>
372
5dac17c3 373=head1 COPYRIGHT AND LICENSE
374
375Copyright 2007 by Infinity Interactive, Inc.
376
377L<http://www.iinteractive.com>
378
379This library is free software; you can redistribute it and/or modify
380it under the same terms as Perl itself.
381
382=cut