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