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