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