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