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