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