* MooseX::Getopt: use explict Getopt::Long module.
[gitmo/MooseX-Getopt.git] / lib / MooseX / Getopt.pm
1
2 package MooseX::Getopt;
3 use Moose::Role;
4
5 use Moose::Util::TypeConstraints;
6
7 use MooseX::Getopt::OptionTypeMap;
8
9 use MooseX::Getopt::Session;
10
11 use MooseX::Getopt::Meta::Attribute;
12 use MooseX::Getopt::Meta::Attribute::NoGetopt;
13
14
15 use Getopt::Long ();
16
17
18 our $VERSION   = '0.150001';
19 our $AUTHORITY = 'cpan:STEVAN';
20
21
22 use constant _default_getopt_session => 'MooseX::Getopt::Session';
23
24
25 has getopt => (
26     is => 'rw',
27     isa => 'MooseX::Getopt::Session',
28     metaclass => 'NoGetopt',
29     handles => [ 'ARGV', 'extra_argv' ],
30 );
31
32
33 sub new_with_options {
34     my $class = shift;
35
36     return $class->new( $class->get_options_from_argv(@_) );
37 };
38
39
40 sub get_options_from_argv {
41     my $class = shift;
42
43     Moose->throw_error("Single parameters to get_options_from_argv() must be a HASH ref")
44         if ref $_[0] and ref $_ ne 'HASH';
45
46     my $options = { %{ $class->get_options_from_configfile }, @_ == 1 ? %{ $_[0] } : @_ };
47
48     my $getopt = defined $options->{getopt}
49                  ? $options->{getopt}
50                  : $class->_default_getopt_session->new(
51                        classes_filter => sub { $_ eq $class },
52                        options => $options,
53                    );
54
55     my $new_options = {
56         %{ $options },                          # explicit options to ->new
57         %{ $getopt->options },                  # options from CLI
58         getopt => $getopt,
59     };
60
61     return $new_options;
62 };
63
64
65 sub get_options_from_configfile {
66     my $class = shift;
67
68     my $options = {};
69
70     if ($class->meta->does_role('MooseX::ConfigFromFile')) {
71         local @ARGV = @ARGV;
72
73         my $configfile;
74         my $opt_parser = Getopt::Long::Parser->new( config => [ 'pass_through' ] );
75         $opt_parser->getoptions( "configfile=s" => \$configfile );
76
77         if (not defined $configfile) {
78             my $cfmeta = $class->meta->find_attribute_by_name('configfile');
79             $configfile = $cfmeta->default if $cfmeta->has_default;
80         };
81
82         if (defined $configfile) {
83             $options = $class->get_config_from_file($configfile);
84         };
85     };
86
87     return $options;
88 };
89
90
91 sub _compute_getopt_attrs {
92     my $class = shift;
93
94     return grep {
95         $_->does('MooseX::Getopt::Meta::Attribute::Trait')
96             or
97         $_->name !~ /^_/
98     } grep {
99         !$_->does('MooseX::Getopt::Meta::Attribute::Trait::NoGetopt')
100     } $class->meta->compute_all_applicable_attributes;
101 };
102
103
104 no Moose::Role; 1;
105
106 __END__
107
108 =pod
109
110 =head1 NAME
111
112 MooseX::Getopt - A Moose role for processing command line options
113
114 =head1 SYNOPSIS
115
116   ## In your class
117   package My::App;
118   use Moose;
119
120   with 'MooseX::Getopt';
121
122   has 'out' => (is => 'rw', isa => 'Str', required => 1);
123   has 'in'  => (is => 'rw', isa => 'Str', required => 1);
124
125   # ... rest of the class here
126
127   ## in your script
128   #!/usr/bin/perl
129
130   use My::App;
131
132   my $app = My::App->new_with_options();
133   # ... rest of the script here
134
135   ## on the command line
136   % perl my_app_script.pl -in file.input -out file.dump
137
138 =head1 DESCRIPTION
139
140 This is a role which provides an alternate constructor for creating
141 objects using parameters passed in from the command line.
142
143 This module attempts to DWIM as much as possible with the command line
144 params by introspecting your class's attributes. It will use the name
145 of your attribute as the command line option, and if there is a type
146 constraint defined, it will configure Getopt::Long to handle the option
147 accordingly.
148
149 You can use the trait L<MooseX::Getopt::Meta::Attribute::Trait> or the
150 attribute metaclass L<MooseX::Getopt::Meta::Attribute> to get non-default
151 commandline option names and aliases.
152
153 You can use the trait L<MooseX::Getopt::Meta::Attribute::Trait::NoGetopt>
154 or the attribute metaclass L<MooseX::Getopt::Meta::Attribute::NoGetopt>
155 to have C<MooseX::Getopt> ignore your attribute in the commandline options.
156
157 By default, attributes which start with an underscore are not given
158 commandline argument support, unless the attribute's metaclass is set
159 to L<MooseX::Getopt::Meta::Attribute>. If you don't want you accessors
160 to have the leading underscore in thier name, you can do this:
161
162   # for read/write attributes
163   has '_foo' => (accessor => 'foo', ...);
164
165   # or for read-only attributes
166   has '_bar' => (reader => 'bar', ...);
167
168 This will mean that Getopt will not handle a --foo param, but your
169 code can still call the C<foo> method.
170
171 If your class also uses a configfile-loading role based on
172 L<MooseX::ConfigFromFile>, such as L<MooseX::SimpleConfig>,
173 L<MooseX::Getopt>'s C<new_with_options> will load the configfile
174 specified by the C<--configfile> option (or the default you've
175 given for the configfile attribute) for you.
176
177 Options specified in multiple places follow the following
178 precendence order: commandline overrides configfile, which
179 overrides explicit new_with_options parameters.
180
181 =head2 Supported Type Constraints
182
183 =over 4
184
185 =item I<Bool>
186
187 A I<Bool> type constraint is set up as a boolean option with
188 Getopt::Long. So that this attribute description:
189
190   has 'verbose' => (is => 'rw', isa => 'Bool');
191
192 would translate into C<verbose!> as a Getopt::Long option descriptor,
193 which would enable the following command line options:
194
195   % my_script.pl --verbose
196   % my_script.pl --noverbose
197
198 =item I<Int>, I<Float>, I<Str>
199
200 These type constraints are set up as properly typed options with
201 Getopt::Long, using the C<=i>, C<=f> and C<=s> modifiers as appropriate.
202
203 =item I<ArrayRef>
204
205 An I<ArrayRef> type constraint is set up as a multiple value option
206 in Getopt::Long. So that this attribute description:
207
208   has 'include' => (
209       is      => 'rw',
210       isa     => 'ArrayRef',
211       default => sub { [] }
212   );
213
214 would translate into C<includes=s@> as a Getopt::Long option descriptor,
215 which would enable the following command line options:
216
217   % my_script.pl --include /usr/lib --include /usr/local/lib
218
219 =item I<HashRef>
220
221 A I<HashRef> type constraint is set up as a hash value option
222 in Getopt::Long. So that this attribute description:
223
224   has 'define' => (
225       is      => 'rw',
226       isa     => 'HashRef',
227       default => sub { {} }
228   );
229
230 would translate into C<define=s%> as a Getopt::Long option descriptor,
231 which would enable the following command line options:
232
233   % my_script.pl --define os=linux --define vendor=debian
234
235 =back
236
237 =head2 Custom Type Constraints
238
239 It is possible to create custom type constraint to option spec
240 mappings if you need them. The process is fairly simple (but a
241 little verbose maybe). First you create a custom subtype, like
242 so:
243
244   subtype 'ArrayOfInts'
245       => as 'ArrayRef'
246       => where { scalar (grep { looks_like_number($_) } @$_)  };
247
248 Then you register the mapping, like so:
249
250   MooseX::Getopt::OptionTypeMap->add_option_type_to_map(
251       'ArrayOfInts' => '=i@'
252   );
253
254 Now any attribute declarations using this type constraint will
255 get the custom option spec. So that, this:
256
257   has 'nums' => (
258       is      => 'ro',
259       isa     => 'ArrayOfInts',
260       default => sub { [0] }
261   );
262
263 Will translate to the following on the command line:
264
265   % my_script.pl --nums 5 --nums 88 --nums 199
266
267 This example is fairly trivial, but more complex validations are
268 easily possible with a little creativity. The trick is balancing
269 the type constraint validations with the Getopt::Long validations.
270
271 Better examples are certainly welcome :)
272
273 =head2 Inferred Type Constraints
274
275 If you define a custom subtype which is a subtype of one of the
276 standard L</Supported Type Constraints> above, and do not explicitly
277 provide custom support as in L</Custom Type Constraints> above,
278 MooseX::Getopt will treat it like the parent type for Getopt
279 purposes.
280
281 For example, if you had the same custom C<ArrayOfInts> subtype
282 from the examples above, but did not add a new custom option
283 type for it to the C<OptionTypeMap>, it would be treated just
284 like a normal C<ArrayRef> type for Getopt purposes (that is,
285 C<=s@>).
286
287 =head2 Session
288
289 L<MooseX::Getopt> can handle more than one class which contain
290 attributes filled from CLI.  In this case, you need to use explicite
291 L<MooseX::Getopt::Session> object and then the Getopt attributes will be
292 searched in any class which does L<MooseX::Getopt>.
293
294   package My::App;
295   use Moose;
296   with 'MooseX::Getopt';
297   has 'send' => (is => 'rw', predicate => 'has_send');
298
299   package My::App::Send;
300   use Moose;
301   with 'MooseX::Getopt';
302   has 'to' => (is => 'rw', isa => 'Str', default => 'localhost');
303   sub send { my $self = shift; warn "Sending mail to ", $self->to; }
304
305   # ... rest of the class here
306
307   ## in your script
308   #!/usr/bin/perl
309
310   my $getopt = MooseX::Getopt::Session->new;
311
312   my $app = My::App->new_with_options( getopt => $getopt );
313   if ($app->has_send) {
314       # Use the same command line
315       my $sender = My::App::Send->new_with_options( getopt => $getopt );
316       $sender->send;
317   }
318   # ... rest of the script here
319
320   ## on the command line
321   % perl my_app_script.pl --send --to server.example.net
322
323 =head1 METHODS
324
325 =over 4
326
327 =item B<new_with_options (%params)>
328
329 This method will take a set of default C<%params> and then collect
330 params from the command line (possibly overriding those in C<%params>)
331 and then return a newly constructed object.
332
333 If L<Getopt::Long/GetOptions> fails (due to invalid arguments),
334 C<new_with_options> will throw an exception.
335
336 If you have L<Getopt::Long::Descriptive> a the C<usage> param is also passed to
337 C<new>.
338
339 =item B<get_options_from_argv (%params)>
340
341 This method returns the list of parameters collected from command line
342 without creating the new object.
343
344 =item B<get_options_from_configfile>
345
346 This method returns the list of parameters collected with
347 L<MooseX::ConfigFromFile> mechanism.
348
349 =item B<ARGV>
350
351 This accessor contains a reference to a copy of the C<@ARGV> array as it
352 originally existed at the time of C<new_with_options>.
353
354 The C<ARGV> is delegated from L<MooseX::Getopt::Session> object.
355
356 =item B<extra_argv>
357
358 This accessor contains an arrayref of leftover C<@ARGV> elements that
359 L<Getopt::Long> did not parse.  Note that the real C<@ARGV> is left
360 un-mangled.
361
362 The C<extra_argv> is delegated from L<MooseX::Getopt::Session> object.
363
364 =item B<getopt>
365
366 This accessor contains a L<MooseX::Getopt::Session> object.  This object can
367 be shared between more than one class which does L<MooseX::Getopt>.  The new
368 object is created by default.
369
370 =item B<meta>
371
372 This returns the role meta object.
373
374 =back
375
376 =head1 SEE ALSO
377
378 =over 4
379
380 =item L<MooseX::Getopt::Strict>
381
382 =item L<MooseX::Getopt::Session>
383
384 =item L<MooseX::Getopt::Parser>
385
386 =back
387
388 =head1 BUGS
389
390 All complex software has bugs lurking in it, and this module is no
391 exception. If you find a bug please either email me, or add the bug
392 to cpan-RT.
393
394 =head1 AUTHOR
395
396 Stevan Little E<lt>stevan@iinteractive.comE<gt>
397
398 Brandon L. Black, E<lt>blblack@gmail.comE<gt>
399
400 Yuval Kogman, E<lt>nothingmuch@woobling.orgE<gt>
401
402 =head1 CONTRIBUTORS
403
404 Ryan D Johnson, E<lt>ryan@innerfence.comE<gt>
405
406 Piotr Roszatycki, E<lt>dexter@cpan.orgE<gt>
407
408 =head1 COPYRIGHT AND LICENSE
409
410 Copyright 2007-2008 by Infinity Interactive, Inc.
411
412 L<http://www.iinteractive.com>
413
414 This library is free software; you can redistribute it and/or modify
415 it under the same terms as Perl itself.
416
417 =cut