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