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