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