0.08 version/changes
[gitmo/MooseX-Getopt.git] / lib / MooseX / Getopt.pm
CommitLineData
5dac17c3 1
2package MooseX::Getopt;
3use Moose::Role;
4
f63e6310 5use Getopt::Long ();
5dac17c3 6
8034a232 7use MooseX::Getopt::OptionTypeMap;
5dac17c3 8use MooseX::Getopt::Meta::Attribute;
0f8232b6 9use MooseX::Getopt::Meta::Attribute::NoGetopt;
5dac17c3 10
384fb15d 11our $VERSION = '0.08';
8034a232 12our $AUTHORITY = 'cpan:STEVAN';
13
f63e6310 14has ARGV => (is => 'rw', isa => 'ArrayRef');
15has extra_argv => (is => 'rw', isa => 'ArrayRef');
3899e5df 16
5dac17c3 17sub new_with_options {
bff3807b 18 my ($class, @params) = @_;
5dac17c3 19
26be7f7e 20 my %processed = $class->_parse_argv(
21 options => [
bff3807b 22 $class->_attrs_to_options( @params )
26be7f7e 23 ]
24 );
ee211848 25
ee69c4ba 26 my $params = $processed{params};
27
28 if($class->meta->does_role('MooseX::ConfigFromFile')
29 && defined $params->{configfile}) {
30 %$params = (
31 %{$class->get_config_from_file($params->{configfile})},
32 %$params,
33 );
34 }
35
ee211848 36 $class->new(
37 ARGV => $processed{argv_copy},
38 extra_argv => $processed{argv},
bff3807b 39 @params, # explicit params to ->new
ee69c4ba 40 %$params, # params from CLI
ee211848 41 );
42}
43
44sub _parse_argv {
45 my ( $class, %params ) = @_;
46
47 local @ARGV = @{ $params{argv} || \@ARGV };
48
49 my ( @options, %name_to_init_arg, %options );
50
51 foreach my $opt ( @{ $params{options} } ) {
52 push @options, $opt->{opt_string};
53 $name_to_init_arg{ $opt->{name} } = $opt->{init_arg};
54 }
55
56 # Get a clean copy of the original @ARGV
57 my $argv_copy = [ @ARGV ];
58
59 {
60 local $SIG{__WARN__} = sub { die $_[0] };
61 Getopt::Long::GetOptions(\%options, @options);
62 }
63
64 # Get a copy of the Getopt::Long-mangled @ARGV
65 my $argv_mangled = [ @ARGV ];
66
67 return (
68 argv_copy => $argv_copy,
69 argv => $argv_mangled,
70 params => {
71 map {
72 $name_to_init_arg{$_} => $options{$_}
73 } keys %options,
74 }
75 );
76}
77
bff3807b 78sub _compute_getopt_attrs {
79 my $class = shift;
bff3807b 80 grep {
81 $_->isa("MooseX::Getopt::Meta::Attribute")
82 or
83 $_->name !~ /^_/
84 &&
0f8232b6 85 !$_->isa('MooseX::Getopt::Meta::Attribute::NoGetopt')
bff3807b 86 } $class->meta->compute_all_applicable_attributes
87}
88
ee211848 89sub _attrs_to_options {
90 my $class = shift;
91
92 my @options;
93
bff3807b 94 foreach my $attr ($class->_compute_getopt_attrs) {
5dac17c3 95 my $name = $attr->name;
3899e5df 96
de75868f 97 my $aliases;
98
99 if ($attr->isa('MooseX::Getopt::Meta::Attribute')) {
100 $name = $attr->cmd_flag if $attr->has_cmd_flag;
101 $aliases = $attr->cmd_aliases if $attr->has_cmd_aliases;
3899e5df 102 }
ee211848 103
de75868f 104 my $opt_string = $aliases
105 ? join(q{|}, $name, @$aliases)
106 : $name;
107
5dac17c3 108 if ($attr->has_type_constraint) {
109 my $type_name = $attr->type_constraint->name;
8034a232 110 if (MooseX::Getopt::OptionTypeMap->has_option_type($type_name)) {
de75868f 111 $opt_string .= MooseX::Getopt::OptionTypeMap->get_option_type($type_name);
5dac17c3 112 }
113 }
f63e6310 114
ee211848 115 push @options, {
116 name => $name,
117 init_arg => $attr->init_arg,
118 opt_string => $opt_string,
119 required => $attr->is_required,
120 ( $attr->has_documentation ? ( doc => $attr->documentation ) : () ),
121 }
f63e6310 122 }
123
ee211848 124 return @options;
5dac17c3 125}
126
8034a232 127no Moose::Role; 1;
5dac17c3 128
129__END__
130
131=pod
132
133=head1 NAME
134
8034a232 135MooseX::Getopt - A Moose role for processing command line options
5dac17c3 136
137=head1 SYNOPSIS
138
139 ## In your class
140 package My::App;
141 use Moose;
142
143 with 'MooseX::Getopt';
144
145 has 'out' => (is => 'rw', isa => 'Str', required => 1);
146 has 'in' => (is => 'rw', isa => 'Str', required => 1);
147
148 # ... rest of the class here
149
150 ## in your script
151 #!/usr/bin/perl
152
153 use My::App;
154
155 my $app = My::App->new_with_options();
156 # ... rest of the script here
157
158 ## on the command line
159 % perl my_app_script.pl -in file.input -out file.dump
160
161=head1 DESCRIPTION
162
8034a232 163This is a role which provides an alternate constructor for creating
164objects using parameters passed in from the command line.
165
166This module attempts to DWIM as much as possible with the command line
167params by introspecting your class's attributes. It will use the name
168of your attribute as the command line option, and if there is a type
169constraint defined, it will configure Getopt::Long to handle the option
3899e5df 170accordingly.
171
172You can use the attribute metaclass L<MooseX::Getopt::Meta::Attribute>
173to get non-default commandline option names and aliases.
174
0f8232b6 175You can use the attribute metaclass L<MooseX::Getopt::Meta::Attribute::NoGetOpt>
176to have C<MooseX::Getopt> ignore your attribute in the commandline options.
177
3899e5df 178By default, attributes which start with an underscore are not given
179commandline argument support, unless the attribute's metaclass is set
3d9a716d 180to L<MooseX::Getopt::Meta::Attribute>. If you don't want you accessors
181to have the leading underscore in thier name, you can do this:
182
183 # for read/write attributes
184 has '_foo' => (accessor => 'foo', ...);
185
186 # or for read-only attributes
187 has '_bar' => (reader => 'bar', ...);
188
189This will mean that Getopt will not handle a --foo param, but your
190code can still call the C<foo> method.
8034a232 191
ee69c4ba 192If your class also uses a configfile-loading role based on
193L<MooseX::ConfigFromFile>, such as L<MooseX::SimpleConfig>,
194L<MooseX::Getopt>'s C<new_with_options> will load the configfile
195specified by the C<--configfile> option for you.
196
8034a232 197=head2 Supported Type Constraints
198
199=over 4
200
201=item I<Bool>
202
203A I<Bool> type constraint is set up as a boolean option with
204Getopt::Long. So that this attribute description:
205
206 has 'verbose' => (is => 'rw', isa => 'Bool');
207
208would translate into C<verbose!> as a Getopt::Long option descriptor,
209which would enable the following command line options:
210
211 % my_script.pl --verbose
212 % my_script.pl --noverbose
213
214=item I<Int>, I<Float>, I<Str>
215
216These type constraints are set up as properly typed options with
217Getopt::Long, using the C<=i>, C<=f> and C<=s> modifiers as appropriate.
218
219=item I<ArrayRef>
220
221An I<ArrayRef> type constraint is set up as a multiple value option
222in Getopt::Long. So that this attribute description:
223
224 has 'include' => (
225 is => 'rw',
226 isa => 'ArrayRef',
227 default => sub { [] }
228 );
229
230would translate into C<includes=s@> as a Getopt::Long option descriptor,
231which would enable the following command line options:
232
233 % my_script.pl --include /usr/lib --include /usr/local/lib
234
235=item I<HashRef>
236
237A I<HashRef> type constraint is set up as a hash value option
238in Getopt::Long. So that this attribute description:
239
240 has 'define' => (
241 is => 'rw',
242 isa => 'HashRef',
243 default => sub { {} }
244 );
245
246would translate into C<define=s%> as a Getopt::Long option descriptor,
247which would enable the following command line options:
248
249 % my_script.pl --define os=linux --define vendor=debian
250
251=back
252
253=head2 Custom Type Constraints
254
255It is possible to create custom type constraint to option spec
256mappings if you need them. The process is fairly simple (but a
257little verbose maybe). First you create a custom subtype, like
258so:
259
260 subtype 'ArrayOfInts'
261 => as 'ArrayRef'
262 => where { scalar (grep { looks_like_number($_) } @$_) };
263
264Then you register the mapping, like so:
265
266 MooseX::Getopt::OptionTypeMap->add_option_type_to_map(
267 'ArrayOfInts' => '=i@'
268 );
269
270Now any attribute declarations using this type constraint will
271get the custom option spec. So that, this:
272
273 has 'nums' => (
274 is => 'ro',
275 isa => 'ArrayOfInts',
276 default => sub { [0] }
277 );
278
279Will translate to the following on the command line:
280
281 % my_script.pl --nums 5 --nums 88 --nums 199
282
283This example is fairly trivial, but more complex validations are
284easily possible with a little creativity. The trick is balancing
285the type constraint validations with the Getopt::Long validations.
286
287Better examples are certainly welcome :)
288
f63e6310 289=head2 Inferred Type Constraints
290
291If you define a custom subtype which is a subtype of one of the
292standard L</Supported Type Constraints> above, and do not explicitly
293provide custom support as in L</Custom Type Constraints> above,
294MooseX::Getopt will treat it like the parent type for Getopt
295purposes.
296
297For example, if you had the same custom C<ArrayOfInts> subtype
298from the examples above, but did not add a new custom option
299type for it to the C<OptionTypeMap>, it would be treated just
300like a normal C<ArrayRef> type for Getopt purposes (that is,
301C<=s@>).
302
5dac17c3 303=head1 METHODS
304
305=over 4
306
307=item B<new_with_options (%params)>
308
8034a232 309This method will take a set of default C<%params> and then collect
310params from the command line (possibly overriding those in C<%params>)
311and then return a newly constructed object.
312
f63e6310 313If L<Getopt::Long/GetOptions> fails (due to invalid arguments),
314C<new_with_options> will throw an exception.
315
3899e5df 316=item B<ARGV>
317
318This accessor contains a reference to a copy of the C<@ARGV> array
f63e6310 319as it originally existed at the time of C<new_with_options>.
320
321=item B<extra_argv>
322
323This accessor contains an arrayref of leftover C<@ARGV> elements that
324L<Getopt::Long> did not parse. Note that the real C<@ARGV> is left
325un-mangled.
3899e5df 326
5dac17c3 327=item B<meta>
328
8034a232 329This returns the role meta object.
330
5dac17c3 331=back
332
333=head1 BUGS
334
335All complex software has bugs lurking in it, and this module is no
336exception. If you find a bug please either email me, or add the bug
337to cpan-RT.
338
339=head1 AUTHOR
340
341Stevan Little E<lt>stevan@iinteractive.comE<gt>
342
e2911e34 343Brandon L. Black, E<lt>blblack@gmail.comE<gt>
344
5dac17c3 345=head1 COPYRIGHT AND LICENSE
346
347Copyright 2007 by Infinity Interactive, Inc.
348
349L<http://www.iinteractive.com>
350
351This library is free software; you can redistribute it and/or modify
352it under the same terms as Perl itself.
353
354=cut