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