Applied patch in RT43255
[gitmo/MooseX-Getopt.git] / lib / MooseX / Getopt.pm
CommitLineData
5dac17c3 1
2package MooseX::Getopt;
3use Moose::Role;
4
8034a232 5use MooseX::Getopt::OptionTypeMap;
5dac17c3 6use MooseX::Getopt::Meta::Attribute;
0f8232b6 7use MooseX::Getopt::Meta::Attribute::NoGetopt;
5dac17c3 8
b56c8123 9use Carp ();
10
75a6449b 11use Getopt::Long (); # GLD uses it anyway, doesn't hurt
12use constant HAVE_GLD => not not eval { require Getopt::Long::Descriptive };
13
105e05aa 14our $VERSION = '0.18';
8034a232 15our $AUTHORITY = 'cpan:STEVAN';
16
8f95e21d 17has ARGV => (is => 'rw', isa => 'ArrayRef', metaclass => "NoGetopt");
18has extra_argv => (is => 'rw', isa => 'ArrayRef', metaclass => "NoGetopt");
3899e5df 19
5dac17c3 20sub new_with_options {
bff3807b 21 my ($class, @params) = @_;
5dac17c3 22
4e086633 23 my $config_from_file;
b4a79051 24 if($class->meta->does_role('MooseX::ConfigFromFile')) {
4e086633 25 local @ARGV = @ARGV;
26
b4a79051 27 my $configfile;
4e086633 28 my $opt_parser = Getopt::Long::Parser->new( config => [ qw( pass_through ) ] );
29 $opt_parser->getoptions( "configfile=s" => \$configfile );
b4a79051 30
4e086633 31 if(!defined $configfile) {
9f1ec7c0 32 my $cfmeta = $class->meta->find_attribute_by_name('configfile');
b4a79051 33 $configfile = $cfmeta->default if $cfmeta->has_default;
34 }
35
36 if(defined $configfile) {
4e086633 37 $config_from_file = $class->get_config_from_file($configfile);
b4a79051 38 }
ee69c4ba 39 }
40
b56c8123 41 my $constructor_params = ( @params == 1 ? $params[0] : {@params} );
42
43 Carp::croak("Single parameters to new_with_options() must be a HASH ref")
44 unless ref($constructor_params) eq 'HASH';
45
4e086633 46 my %processed = $class->_parse_argv(
47 options => [
48 $class->_attrs_to_options( $config_from_file )
b56c8123 49 ],
50 params => $constructor_params,
4e086633 51 );
52
53 my $params = $config_from_file ? { %$config_from_file, %{$processed{params}} } : $processed{params};
54
93374bbc 55 # did the user request usage information?
56 if ( $processed{usage} && ($params->{'?'} or $params->{help} or $params->{usage}) )
57 {
58 $processed{usage}->die();
59 }
60
ee211848 61 $class->new(
62 ARGV => $processed{argv_copy},
63 extra_argv => $processed{argv},
44b487bc 64 %$constructor_params, # explicit params to ->new
ee69c4ba 65 %$params, # params from CLI
ee211848 66 );
67}
68
69sub _parse_argv {
0e715336 70 my ( $class, %params ) = @_;
6bb4cb66 71
0e715336 72 local @ARGV = @{ $params{argv} || \@ARGV };
73
75a6449b 74 my ( $opt_spec, $name_to_init_arg ) = ( HAVE_GLD ? $class->_gld_spec(%params) : $class->_traditional_spec(%params) );
ee211848 75
76 # Get a clean copy of the original @ARGV
77 my $argv_copy = [ @ARGV ];
78
6bb4cb66 79 my @err;
80
81 my ( $parsed_options, $usage ) = eval {
82 local $SIG{__WARN__} = sub { push @err, @_ };
75a6449b 83
84 if ( HAVE_GLD ) {
85 return Getopt::Long::Descriptive::describe_options($class->_usage_format(%params), @$opt_spec);
86 } else {
87 my %options;
88 Getopt::Long::GetOptions(\%options, @$opt_spec);
3cc60720 89 return ( \%options, undef );
75a6449b 90 }
6bb4cb66 91 };
92
93 die join "", grep { defined } @err, $@ if @err or $@;
ee211848 94
95 # Get a copy of the Getopt::Long-mangled @ARGV
96 my $argv_mangled = [ @ARGV ];
97
0e715336 98 my %constructor_args = (
4e086633 99 map {
100 $name_to_init_arg->{$_} => $parsed_options->{$_}
101 } keys %$parsed_options,
ee211848 102 );
0e715336 103
913e7020 104 return (
105 params => \%constructor_args,
106 argv_copy => $argv_copy,
107 argv => $argv_mangled,
3cc60720 108 ( defined($usage) ? ( usage => $usage ) : () ),
913e7020 109 );
0e715336 110}
111
9287e912 112sub _usage_format {
113 return "usage: %c %o";
114}
115
75a6449b 116sub _traditional_spec {
117 my ( $class, %params ) = @_;
4e086633 118
75a6449b 119 my ( @options, %name_to_init_arg, %options );
120
121 foreach my $opt ( @{ $params{options} } ) {
122 push @options, $opt->{opt_string};
41d0ab09 123
124 my $identifier = $opt->{name};
125 $identifier =~ s/\W/_/g; # Getopt::Long does this to all option names
126
127 $name_to_init_arg{$identifier} = $opt->{init_arg};
75a6449b 128 }
129
130 return ( \@options, \%name_to_init_arg );
131}
132
0e715336 133sub _gld_spec {
134 my ( $class, %params ) = @_;
135
136 my ( @options, %name_to_init_arg );
137
b56c8123 138 my $constructor_params = $params{params};
139
0e715336 140 foreach my $opt ( @{ $params{options} } ) {
141 push @options, [
142 $opt->{opt_string},
143 $opt->{doc} || ' ', # FIXME new GLD shouldn't need this hack
144 {
b56c8123 145 ( ( $opt->{required} && !exists($constructor_params->{$opt->{init_arg}}) ) ? (required => $opt->{required}) : () ),
630657d5 146 # NOTE:
147 # remove this 'feature' because it didn't work
148 # all the time, and so is better to not bother
149 # since Moose will handle the defaults just
150 # fine anyway.
151 # - SL
152 #( exists $opt->{default} ? (default => $opt->{default}) : () ),
0e715336 153 },
154 ];
155
41d0ab09 156 my $identifier = $opt->{name};
157 $identifier =~ s/\W/_/g; # Getopt::Long does this to all option names
158
159 $name_to_init_arg{$identifier} = $opt->{init_arg};
0e715336 160 }
161
162 return ( \@options, \%name_to_init_arg );
ee211848 163}
164
bff3807b 165sub _compute_getopt_attrs {
166 my $class = shift;
bff3807b 167 grep {
adbe3e57 168 $_->does("MooseX::Getopt::Meta::Attribute::Trait")
bff3807b 169 or
170 $_->name !~ /^_/
adbe3e57 171 } grep {
172 !$_->does('MooseX::Getopt::Meta::Attribute::Trait::NoGetopt')
e2dcd71d 173 } $class->meta->get_all_attributes
bff3807b 174}
175
4ad81caf 176sub _get_cmd_flags_for_attr {
177 my ( $class, $attr ) = @_;
178
179 my $flag = $attr->name;
180
181 my @aliases;
182
adbe3e57 183 if ($attr->does('MooseX::Getopt::Meta::Attribute::Trait')) {
4ad81caf 184 $flag = $attr->cmd_flag if $attr->has_cmd_flag;
185 @aliases = @{ $attr->cmd_aliases } if $attr->has_cmd_aliases;
186 }
187
188 return ( $flag, @aliases );
189}
190
ee211848 191sub _attrs_to_options {
192 my $class = shift;
4e086633 193 my $config_from_file = shift || {};
ee211848 194
195 my @options;
196
bff3807b 197 foreach my $attr ($class->_compute_getopt_attrs) {
4ad81caf 198 my ( $flag, @aliases ) = $class->_get_cmd_flags_for_attr($attr);
ee211848 199
4ad81caf 200 my $opt_string = join(q{|}, $flag, @aliases);
de75868f 201
5dac17c3 202 if ($attr->has_type_constraint) {
365e5784 203 my $type = $attr->type_constraint;
204 if (MooseX::Getopt::OptionTypeMap->has_option_type($type)) {
205 $opt_string .= MooseX::Getopt::OptionTypeMap->get_option_type($type)
5dac17c3 206 }
207 }
f63e6310 208
ee211848 209 push @options, {
4ad81caf 210 name => $flag,
ee211848 211 init_arg => $attr->init_arg,
212 opt_string => $opt_string,
4e086633 213 required => $attr->is_required && !$attr->has_default && !$attr->has_builder && !exists $config_from_file->{$attr->name},
630657d5 214 # NOTE:
215 # this "feature" was breaking because
216 # Getopt::Long::Descriptive would return
217 # the default value as if it was a command
218 # line flag, which would then override the
219 # one passed into a constructor.
220 # See 100_gld_default_bug.t for an example
221 # - SL
222 #( ( $attr->has_default && ( $attr->is_default_a_coderef xor $attr->is_lazy ) ) ? ( default => $attr->default({}) ) : () ),
ee211848 223 ( $attr->has_documentation ? ( doc => $attr->documentation ) : () ),
224 }
f63e6310 225 }
226
ee211848 227 return @options;
5dac17c3 228}
229
8034a232 230no Moose::Role; 1;
5dac17c3 231
232__END__
233
234=pod
235
236=head1 NAME
237
8034a232 238MooseX::Getopt - A Moose role for processing command line options
5dac17c3 239
240=head1 SYNOPSIS
241
4e086633 242 ## In your class
5dac17c3 243 package My::App;
244 use Moose;
4e086633 245
5dac17c3 246 with 'MooseX::Getopt';
4e086633 247
5dac17c3 248 has 'out' => (is => 'rw', isa => 'Str', required => 1);
249 has 'in' => (is => 'rw', isa => 'Str', required => 1);
4e086633 250
5dac17c3 251 # ... rest of the class here
4e086633 252
5dac17c3 253 ## in your script
254 #!/usr/bin/perl
4e086633 255
5dac17c3 256 use My::App;
4e086633 257
5dac17c3 258 my $app = My::App->new_with_options();
259 # ... rest of the script here
4e086633 260
5dac17c3 261 ## on the command line
262 % perl my_app_script.pl -in file.input -out file.dump
263
264=head1 DESCRIPTION
265
4e086633 266This is a role which provides an alternate constructor for creating
267objects using parameters passed in from the command line.
8034a232 268
4e086633 269This module attempts to DWIM as much as possible with the command line
270params by introspecting your class's attributes. It will use the name
271of your attribute as the command line option, and if there is a type
8034a232 272constraint defined, it will configure Getopt::Long to handle the option
3899e5df 273accordingly.
274
2814de27 275You can use the trait L<MooseX::Getopt::Meta::Attribute::Trait> or the
276attribute metaclass L<MooseX::Getopt::Meta::Attribute> to get non-default
277commandline option names and aliases.
3899e5df 278
2814de27 279You can use the trait L<MooseX::Getopt::Meta::Attribute::Trait::NoGetopt>
280or the attribute metaclass L<MooseX::Getopt::Meta::Attribute::NoGetopt>
0f8232b6 281to have C<MooseX::Getopt> ignore your attribute in the commandline options.
282
3899e5df 283By default, attributes which start with an underscore are not given
284commandline argument support, unless the attribute's metaclass is set
3d9a716d 285to L<MooseX::Getopt::Meta::Attribute>. If you don't want you accessors
286to have the leading underscore in thier name, you can do this:
287
288 # for read/write attributes
289 has '_foo' => (accessor => 'foo', ...);
4e086633 290
3d9a716d 291 # or for read-only attributes
4e086633 292 has '_bar' => (reader => 'bar', ...);
3d9a716d 293
4e086633 294This will mean that Getopt will not handle a --foo param, but your
295code can still call the C<foo> method.
8034a232 296
ee69c4ba 297If your class also uses a configfile-loading role based on
298L<MooseX::ConfigFromFile>, such as L<MooseX::SimpleConfig>,
299L<MooseX::Getopt>'s C<new_with_options> will load the configfile
b4a79051 300specified by the C<--configfile> option (or the default you've
301given for the configfile attribute) for you.
302
303Options specified in multiple places follow the following
304precendence order: commandline overrides configfile, which
305overrides explicit new_with_options parameters.
ee69c4ba 306
8034a232 307=head2 Supported Type Constraints
308
309=over 4
310
311=item I<Bool>
312
4e086633 313A I<Bool> type constraint is set up as a boolean option with
8034a232 314Getopt::Long. So that this attribute description:
315
316 has 'verbose' => (is => 'rw', isa => 'Bool');
317
4e086633 318would translate into C<verbose!> as a Getopt::Long option descriptor,
8034a232 319which would enable the following command line options:
320
321 % my_script.pl --verbose
4e086633 322 % my_script.pl --noverbose
323
8034a232 324=item I<Int>, I<Float>, I<Str>
325
4e086633 326These type constraints are set up as properly typed options with
8034a232 327Getopt::Long, using the C<=i>, C<=f> and C<=s> modifiers as appropriate.
328
329=item I<ArrayRef>
330
331An I<ArrayRef> type constraint is set up as a multiple value option
332in Getopt::Long. So that this attribute description:
333
334 has 'include' => (
4e086633 335 is => 'rw',
336 isa => 'ArrayRef',
8034a232 337 default => sub { [] }
338 );
339
4e086633 340would translate into C<includes=s@> as a Getopt::Long option descriptor,
8034a232 341which would enable the following command line options:
342
343 % my_script.pl --include /usr/lib --include /usr/local/lib
344
345=item I<HashRef>
346
347A I<HashRef> type constraint is set up as a hash value option
348in Getopt::Long. So that this attribute description:
349
350 has 'define' => (
4e086633 351 is => 'rw',
352 isa => 'HashRef',
8034a232 353 default => sub { {} }
354 );
355
4e086633 356would translate into C<define=s%> as a Getopt::Long option descriptor,
8034a232 357which would enable the following command line options:
358
359 % my_script.pl --define os=linux --define vendor=debian
360
361=back
362
363=head2 Custom Type Constraints
364
4e086633 365It is possible to create custom type constraint to option spec
8034a232 366mappings if you need them. The process is fairly simple (but a
4e086633 367little verbose maybe). First you create a custom subtype, like
8034a232 368so:
369
370 subtype 'ArrayOfInts'
371 => as 'ArrayRef'
372 => where { scalar (grep { looks_like_number($_) } @$_) };
373
374Then you register the mapping, like so:
375
376 MooseX::Getopt::OptionTypeMap->add_option_type_to_map(
377 'ArrayOfInts' => '=i@'
378 );
379
4e086633 380Now any attribute declarations using this type constraint will
8034a232 381get the custom option spec. So that, this:
382
383 has 'nums' => (
384 is => 'ro',
385 isa => 'ArrayOfInts',
386 default => sub { [0] }
387 );
388
389Will translate to the following on the command line:
390
391 % my_script.pl --nums 5 --nums 88 --nums 199
392
4e086633 393This example is fairly trivial, but more complex validations are
8034a232 394easily possible with a little creativity. The trick is balancing
395the type constraint validations with the Getopt::Long validations.
396
397Better examples are certainly welcome :)
398
f63e6310 399=head2 Inferred Type Constraints
400
401If you define a custom subtype which is a subtype of one of the
402standard L</Supported Type Constraints> above, and do not explicitly
403provide custom support as in L</Custom Type Constraints> above,
404MooseX::Getopt will treat it like the parent type for Getopt
405purposes.
406
407For example, if you had the same custom C<ArrayOfInts> subtype
408from the examples above, but did not add a new custom option
409type for it to the C<OptionTypeMap>, it would be treated just
410like a normal C<ArrayRef> type for Getopt purposes (that is,
411C<=s@>).
412
5dac17c3 413=head1 METHODS
414
415=over 4
416
417=item B<new_with_options (%params)>
418
4e086633 419This method will take a set of default C<%params> and then collect
8034a232 420params from the command line (possibly overriding those in C<%params>)
421and then return a newly constructed object.
422
f63e6310 423If L<Getopt::Long/GetOptions> fails (due to invalid arguments),
424C<new_with_options> will throw an exception.
425
93374bbc 426If L<Getopt::Long::Descriptive> is installed and any of the following
427command line params are passed, the program will exit with usage
428information. You can add descriptions for each option by including a
429B<documentation> option for each attribute to document.
430
431 --?
432 --help
433 --usage
434
fad5da09 435If you have L<Getopt::Long::Descriptive> a the C<usage> param is also passed to
436C<new>.
437
3899e5df 438=item B<ARGV>
439
440This accessor contains a reference to a copy of the C<@ARGV> array
f63e6310 441as it originally existed at the time of C<new_with_options>.
442
443=item B<extra_argv>
444
445This accessor contains an arrayref of leftover C<@ARGV> elements that
446L<Getopt::Long> did not parse. Note that the real C<@ARGV> is left
447un-mangled.
3899e5df 448
5dac17c3 449=item B<meta>
450
8034a232 451This returns the role meta object.
452
5dac17c3 453=back
454
455=head1 BUGS
456
4e086633 457All complex software has bugs lurking in it, and this module is no
5dac17c3 458exception. If you find a bug please either email me, or add the bug
459to cpan-RT.
460
461=head1 AUTHOR
462
463Stevan Little E<lt>stevan@iinteractive.comE<gt>
464
e2911e34 465Brandon L. Black, E<lt>blblack@gmail.comE<gt>
466
630657d5 467Yuval Kogman, E<lt>nothingmuch@woobling.orgE<gt>
468
78a71ae5 469=head1 CONTRIBUTORS
470
471Ryan D Johnson, E<lt>ryan@innerfence.comE<gt>
472
93374bbc 473Drew Taylor, E<lt>drew@drewtaylor.comE<gt>
474
5dac17c3 475=head1 COPYRIGHT AND LICENSE
476
adbe3e57 477Copyright 2007-2008 by Infinity Interactive, Inc.
5dac17c3 478
479L<http://www.iinteractive.com>
480
481This library is free software; you can redistribute it and/or modify
482it under the same terms as Perl itself.
483
484=cut