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