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