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