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