Docs of MooseX::Getopt::Basic now refer elsewhere.
[gitmo/MooseX-Getopt.git] / lib / MooseX / Getopt / Basic.pm
1 package MooseX::Getopt::Basic;
2 use Moose::Role;
3
4 use MooseX::Getopt::OptionTypeMap;
5 use MooseX::Getopt::Meta::Attribute;
6 use MooseX::Getopt::Meta::Attribute::NoGetopt;
7 use Carp ();
8
9 use Getopt::Long (); # GLD uses it anyway, doesn't hurt
10
11 our $VERSION   = '0.20';
12 our $AUTHORITY = 'cpan:STEVAN';
13
14 has ARGV       => (is => 'rw', isa => 'ArrayRef', metaclass => "NoGetopt");
15 has extra_argv => (is => 'rw', isa => 'ArrayRef', metaclass => "NoGetopt");
16
17 # _getopt_spec() and _getoptions() are overrided by MooseX::Getopt::GLD.
18
19 sub _getopt_spec {
20     my ($class, %params) = @_;
21     return $class->_traditional_spec(%params) 
22 }
23
24 sub _get_options {
25     my ($class, undef, $opt_spec) = @_;
26     my %options;
27     Getopt::Long::GetOptions(\%options, @$opt_spec);
28     return ( \%options, undef );
29 }
30
31 sub new_with_options {
32     my ($class, @params) = @_;
33
34     my $config_from_file;
35     if($class->meta->does_role('MooseX::ConfigFromFile')) {
36         local @ARGV = @ARGV;
37
38         my $configfile;
39         my $opt_parser = Getopt::Long::Parser->new( config => [ qw( pass_through ) ] );
40         $opt_parser->getoptions( "configfile=s" => \$configfile );
41
42         if(!defined $configfile) {
43             my $cfmeta = $class->meta->find_attribute_by_name('configfile');
44             $configfile = $cfmeta->default if $cfmeta->has_default;
45         }
46
47         if(defined $configfile) {
48             $config_from_file = $class->get_config_from_file($configfile);
49         }
50     }
51
52     my $constructor_params = ( @params == 1 ? $params[0] : {@params} );
53     
54     Carp::croak("Single parameters to new_with_options() must be a HASH ref")
55         unless ref($constructor_params) eq 'HASH';
56
57     my %processed = $class->_parse_argv(
58         options => [
59             $class->_attrs_to_options( $config_from_file )
60         ],
61         params => $constructor_params,
62     );
63
64     my $params = $config_from_file ? { %$config_from_file, %{$processed{params}} } : $processed{params};
65
66     # did the user request usage information?
67     if ( $processed{usage} && ($params->{'?'} or $params->{help} or $params->{usage}) )
68     {
69         $processed{usage}->die();
70     }
71
72     $class->new(
73         ARGV       => $processed{argv_copy},
74         extra_argv => $processed{argv},
75         %$constructor_params, # explicit params to ->new
76         %$params, # params from CLI
77     );
78 }
79
80 sub _parse_argv {
81     my ( $class, %params ) = @_;
82
83     local @ARGV = @{ $params{params}{argv} || \@ARGV };
84
85     my ( $opt_spec, $name_to_init_arg ) = $class->_getopt_spec(%params);
86
87     # Get a clean copy of the original @ARGV
88     my $argv_copy = [ @ARGV ];
89
90     my @err;
91
92     my ( $parsed_options, $usage ) = eval {
93         local $SIG{__WARN__} = sub { push @err, @_ };
94
95         return $class->_get_options(\%params, $opt_spec);
96     };
97
98     die join "", grep { defined } @err, $@ if @err or $@;
99
100     # Get a copy of the Getopt::Long-mangled @ARGV
101     my $argv_mangled = [ @ARGV ];
102
103     my %constructor_args = (
104         map {
105             $name_to_init_arg->{$_} => $parsed_options->{$_}
106         } keys %$parsed_options,
107     );
108
109     return (
110         params    => \%constructor_args,
111         argv_copy => $argv_copy,
112         argv      => $argv_mangled,
113         ( defined($usage) ? ( usage => $usage ) : () ),
114     );
115 }
116
117 sub _usage_format {
118     return "usage: %c %o";
119 }
120
121 sub _traditional_spec {
122     my ( $class, %params ) = @_;
123
124     my ( @options, %name_to_init_arg, %options );
125
126     foreach my $opt ( @{ $params{options} } ) {
127         push @options, $opt->{opt_string};
128
129         my $identifier = $opt->{name};
130         $identifier =~ s/\W/_/g; # Getopt::Long does this to all option names
131
132         $name_to_init_arg{$identifier} = $opt->{init_arg};
133     }
134
135     return ( \@options, \%name_to_init_arg );
136 }
137
138 sub _compute_getopt_attrs {
139     my $class = shift;
140     grep {
141         $_->does("MooseX::Getopt::Meta::Attribute::Trait")
142             or
143         $_->name !~ /^_/
144     } grep {
145         !$_->does('MooseX::Getopt::Meta::Attribute::Trait::NoGetopt')
146     } $class->meta->get_all_attributes
147 }
148
149 sub _get_cmd_flags_for_attr {
150     my ( $class, $attr ) = @_;
151
152     my $flag = $attr->name;
153
154     my @aliases;
155
156     if ($attr->does('MooseX::Getopt::Meta::Attribute::Trait')) {
157         $flag = $attr->cmd_flag if $attr->has_cmd_flag;
158         @aliases = @{ $attr->cmd_aliases } if $attr->has_cmd_aliases;
159     }
160
161     return ( $flag, @aliases );
162 }
163
164 sub _attrs_to_options {
165     my $class = shift;
166     my $config_from_file = shift || {};
167
168     my @options;
169
170     foreach my $attr ($class->_compute_getopt_attrs) {
171         my ( $flag, @aliases ) = $class->_get_cmd_flags_for_attr($attr);
172
173         my $opt_string = join(q{|}, $flag, @aliases);
174
175         if ($attr->name eq 'configfile') {
176             $opt_string .= '=s';
177         }
178         elsif ($attr->has_type_constraint) {
179             my $type = $attr->type_constraint;
180             if (MooseX::Getopt::OptionTypeMap->has_option_type($type)) {
181                 $opt_string .= MooseX::Getopt::OptionTypeMap->get_option_type($type)
182             }
183         }
184
185         push @options, {
186             name       => $flag,
187             init_arg   => $attr->init_arg,
188             opt_string => $opt_string,
189             required   => $attr->is_required && !$attr->has_default && !$attr->has_builder && !exists $config_from_file->{$attr->name},
190             # NOTE:
191             # this "feature" was breaking because 
192             # Getopt::Long::Descriptive would return 
193             # the default value as if it was a command 
194             # line flag, which would then override the
195             # one passed into a constructor.
196             # See 100_gld_default_bug.t for an example
197             # - SL
198             #( ( $attr->has_default && ( $attr->is_default_a_coderef xor $attr->is_lazy ) ) ? ( default => $attr->default({}) ) : () ),
199             ( $attr->has_documentation ? ( doc => $attr->documentation ) : () ),
200         }
201     }
202
203     return @options;
204 }
205
206 no Moose::Role; 1;
207
208 1;
209
210 =pod
211
212 =head1 NAME
213
214 MooseX::Getopt::Basic - role to implement the basic functionality of
215 L<MooseX::Getopt> without GLD.
216
217 =head1 SYNOPSIS
218
219   ## In your class
220   package My::App;
221   use Moose;
222
223   with 'MooseX::Getopt::Basic';
224
225   has 'out' => (is => 'rw', isa => 'Str', required => 1);
226   has 'in'  => (is => 'rw', isa => 'Str', required => 1);
227
228   # ... rest of the class here
229
230   ## in your script
231   #!/usr/bin/perl
232
233   use My::App;
234
235   my $app = My::App->new_with_options();
236   # ... rest of the script here
237
238   ## on the command line
239   % perl my_app_script.pl --in file.input --out file.dump
240
241 =head1 DESCRIPTION
242
243 This is like L<MooseX::Getopt> and can be used instead except that it
244 doesn't make use of L<Getopt::Long::Descriptive> (or "GLD" for short).
245
246 =head1 METHODS
247
248 =over 4
249
250 =item B<new_with_options>
251
252 See L<MooseX::Getopt> .
253
254 =item B<meta>
255
256 This returns the role meta object.
257
258 =back
259
260 =head1 BUGS
261
262 All complex software has bugs lurking in it, and this module is no
263 exception. If you find a bug please either email me, or add the bug
264 to cpan-RT.
265
266 =head1 AUTHOR
267
268 Stevan Little E<lt>stevan@iinteractive.comE<gt>
269
270 Brandon L. Black, E<lt>blblack@gmail.comE<gt>
271
272 Yuval Kogman, E<lt>nothingmuch@woobling.orgE<gt>
273
274 =head1 CONTRIBUTORS
275
276 Ryan D Johnson, E<lt>ryan@innerfence.comE<gt>
277
278 Drew Taylor, E<lt>drew@drewtaylor.comE<gt>
279
280 Shlomi Fish E<lt>shlomif@cpan.orgE<gt>
281
282 =head1 COPYRIGHT AND LICENSE
283
284 Copyright 2007-2008 by Infinity Interactive, Inc.
285
286 L<http://www.iinteractive.com>
287
288 This library is free software; you can redistribute it and/or modify
289 it under the same terms as Perl itself.
290
291 =cut
292