| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package MouseX::Getopt::Basic; | 
| 2 |  |  |  |  |  |  | # ABSTRACT: MouseX::Getopt::Basic - role to implement the Getopt::Long functionality | 
| 3 |  |  |  |  |  |  |  | 
| 4 | 22 |  |  | 22 |  | 78150 | use Mouse::Role; | 
|  | 22 |  |  |  |  | 29115 |  | 
|  | 22 |  |  |  |  | 150 |  | 
| 5 |  |  |  |  |  |  |  | 
| 6 | 22 |  |  | 22 |  | 17162 | use MouseX::Getopt::OptionTypeMap; | 
|  | 22 |  |  |  |  | 58 |  | 
|  | 22 |  |  |  |  | 659 |  | 
| 7 | 22 |  |  | 22 |  | 9122 | use MouseX::Getopt::Meta::Attribute; | 
|  | 22 |  |  |  |  | 64 |  | 
|  | 22 |  |  |  |  | 666 |  | 
| 8 | 22 |  |  | 22 |  | 9372 | use MouseX::Getopt::Meta::Attribute::NoGetopt; | 
|  | 22 |  |  |  |  | 68 |  | 
|  | 22 |  |  |  |  | 625 |  | 
| 9 | 22 |  |  | 22 |  | 143 | use Carp (); | 
|  | 22 |  |  |  |  | 49 |  | 
|  | 22 |  |  |  |  | 460 |  | 
| 10 |  |  |  |  |  |  |  | 
| 11 | 22 |  |  | 22 |  | 845 | use Getopt::Long 2.37 (); | 
|  | 22 |  |  |  |  | 12786 |  | 
|  | 22 |  |  |  |  | 29540 |  | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | has ARGV       => (is => 'rw', isa => 'ArrayRef', metaclass => "NoGetopt"); | 
| 14 |  |  |  |  |  |  | has extra_argv => (is => 'rw', isa => 'ArrayRef', metaclass => "NoGetopt"); | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | sub new_with_options { | 
| 17 | 109 |  |  | 109 | 1 | 276985 | my ($class, @params) = @_; | 
| 18 |  |  |  |  |  |  |  | 
| 19 | 109 |  |  |  |  | 229 | my $config_from_file; | 
| 20 | 109 | 100 |  |  |  | 412 | if($class->meta->does_role('MouseX::ConfigFromFile')) { | 
| 21 | 11 |  |  |  |  | 879 | local @ARGV = @ARGV; | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | # just get the configfile arg now; the rest of the args will be | 
| 24 |  |  |  |  |  |  | # fetched later | 
| 25 | 11 |  |  |  |  | 20 | my $configfile; | 
| 26 | 11 |  |  |  |  | 60 | my $opt_parser = Getopt::Long::Parser->new( config => [ qw( no_auto_help pass_through ) ] ); | 
| 27 | 11 |  |  |  |  | 1320 | $opt_parser->getoptions( "configfile=s" => \$configfile ); | 
| 28 |  |  |  |  |  |  |  | 
| 29 | 11 | 100 |  |  |  | 3872 | if(!defined $configfile) { | 
| 30 | 6 |  |  |  |  | 24 | my $cfmeta = $class->meta->find_attribute_by_name('configfile'); | 
| 31 | 6 | 100 |  |  |  | 635 | $configfile = $cfmeta->default if $cfmeta->has_default; | 
| 32 | 6 | 100 |  |  |  | 22 | if (ref $configfile eq 'CODE') { | 
| 33 |  |  |  |  |  |  | # not sure theres a lot you can do with the class and may break some assumptions | 
| 34 |  |  |  |  |  |  | # warn? | 
| 35 | 2 |  |  |  |  | 8 | $configfile = &$configfile($class); | 
| 36 |  |  |  |  |  |  | } | 
| 37 | 6 | 100 |  |  |  | 50 | if (defined $configfile) { | 
| 38 | 3 |  |  |  |  | 6 | $config_from_file = eval { | 
| 39 | 3 |  |  |  |  | 17 | $class->get_config_from_file($configfile); | 
| 40 |  |  |  |  |  |  | }; | 
| 41 | 3 | 50 |  |  |  | 10799 | if ($@) { | 
| 42 | 0 | 0 |  |  |  | 0 | die $@ unless $@ =~ /Specified configfile '\Q$configfile\E' does not exist/; | 
| 43 |  |  |  |  |  |  | } | 
| 44 |  |  |  |  |  |  | } | 
| 45 |  |  |  |  |  |  | } | 
| 46 |  |  |  |  |  |  | else { | 
| 47 | 5 |  |  |  |  | 19 | $config_from_file = $class->get_config_from_file($configfile); | 
| 48 |  |  |  |  |  |  | } | 
| 49 |  |  |  |  |  |  | } | 
| 50 |  |  |  |  |  |  |  | 
| 51 | 109 | 100 |  |  |  | 6889 | my $constructor_params = ( @params == 1 ? $params[0] : {@params} ); | 
| 52 |  |  |  |  |  |  |  | 
| 53 | 109 | 50 |  |  |  | 349 | Carp::croak("Single parameters to new_with_options() must be a HASH ref") | 
| 54 |  |  |  |  |  |  | unless ref($constructor_params) eq 'HASH'; | 
| 55 |  |  |  |  |  |  |  | 
| 56 | 109 |  |  |  |  | 350 | my %processed = $class->_parse_argv( | 
| 57 |  |  |  |  |  |  | options => [ | 
| 58 |  |  |  |  |  |  | $class->_attrs_to_options( $config_from_file ) | 
| 59 |  |  |  |  |  |  | ], | 
| 60 |  |  |  |  |  |  | params => $constructor_params, | 
| 61 |  |  |  |  |  |  | ); | 
| 62 |  |  |  |  |  |  |  | 
| 63 | 99 | 100 |  |  |  | 607 | my $params = $config_from_file ? { %$config_from_file, %{$processed{params}} } : $processed{params}; | 
|  | 7 |  |  |  |  | 27 |  | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | # did the user request usage information? | 
| 66 | 99 | 100 | 100 |  |  | 945 | if ( $processed{usage} and $params->{help_flag} ) | 
| 67 |  |  |  |  |  |  | { | 
| 68 | 6 |  |  |  |  | 2309 | $class->_getopt_full_usage($processed{usage}); | 
| 69 |  |  |  |  |  |  | } | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | $class->new( | 
| 72 |  |  |  |  |  |  | ARGV       => $processed{argv_copy}, | 
| 73 |  |  |  |  |  |  | extra_argv => $processed{argv}, | 
| 74 | 93 | 100 |  |  |  | 91505 | ( $processed{usage} ? ( usage => $processed{usage} ) : () ), | 
| 75 |  |  |  |  |  |  | %$constructor_params, # explicit params to ->new | 
| 76 |  |  |  |  |  |  | %$params, # params from CLI | 
| 77 |  |  |  |  |  |  | ); | 
| 78 |  |  |  |  |  |  | } | 
| 79 |  |  |  |  |  |  |  | 
| 80 | 23 |  |  | 23 |  | 64 | sub _getopt_spec { shift->_traditional_spec(@_); } | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | sub _parse_argv { | 
| 83 | 109 |  |  | 109 |  | 432 | my ( $class, %params ) = @_; | 
| 84 |  |  |  |  |  |  |  | 
| 85 | 109 | 100 |  |  |  | 238 | local @ARGV = @{ $params{params}{argv} || \@ARGV }; | 
|  | 109 |  |  |  |  | 637 |  | 
| 86 |  |  |  |  |  |  |  | 
| 87 | 109 |  |  |  |  | 502 | my ( $opt_spec, $name_to_init_arg ) = $class->_getopt_spec(%params); | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | # Get a clean copy of the original @ARGV | 
| 90 | 109 |  |  |  |  | 311 | my $argv_copy = [ @ARGV ]; | 
| 91 |  |  |  |  |  |  |  | 
| 92 | 109 |  |  |  |  | 233 | my @warnings; | 
| 93 | 109 |  |  |  |  | 281 | my ( $parsed_options, $usage ) = eval { | 
| 94 | 109 |  |  | 8 |  | 833 | local $SIG{__WARN__} = sub { push @warnings, @_ }; | 
|  | 8 |  |  |  |  | 7777 |  | 
| 95 |  |  |  |  |  |  |  | 
| 96 | 109 |  |  |  |  | 449 | return $class->_getopt_get_options(\%params, $opt_spec); | 
| 97 |  |  |  |  |  |  | }; | 
| 98 | 109 |  |  |  |  | 133228 | my $e = $@; | 
| 99 |  |  |  |  |  |  |  | 
| 100 | 109 | 100 |  |  |  | 391 | $class->_getopt_spec_warnings(@warnings) if @warnings; | 
| 101 | 109 | 100 |  |  |  | 287 | $class->_getopt_spec_exception(\@warnings, $e) if $e; | 
| 102 |  |  |  |  |  |  |  | 
| 103 |  |  |  |  |  |  | # Get a copy of the Getopt::Long-mangled @ARGV | 
| 104 | 99 |  |  |  |  | 214 | my $argv_mangled = [ @ARGV ]; | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | my %constructor_args = ( | 
| 107 |  |  |  |  |  |  | map { | 
| 108 | 99 |  |  |  |  | 497 | $name_to_init_arg->{$_} => $parsed_options->{$_} | 
|  | 107 |  |  |  |  | 442 |  | 
| 109 |  |  |  |  |  |  | } keys %$parsed_options, | 
| 110 |  |  |  |  |  |  | ); | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | return ( | 
| 113 | 99 | 100 |  |  |  | 955 | params    => \%constructor_args, | 
| 114 |  |  |  |  |  |  | argv_copy => $argv_copy, | 
| 115 |  |  |  |  |  |  | argv      => $argv_mangled, | 
| 116 |  |  |  |  |  |  | ( defined($usage) ? ( usage => $usage ) : () ), | 
| 117 |  |  |  |  |  |  | ); | 
| 118 |  |  |  |  |  |  | } | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | sub _getopt_get_options { | 
| 121 | 23 |  |  | 23 |  | 49 | my ($class, $params, $opt_spec) = @_; | 
| 122 | 23 |  |  |  |  | 51 | my %options; | 
| 123 | 23 |  |  |  |  | 110 | Getopt::Long::GetOptions(\%options, @$opt_spec); | 
| 124 | 23 |  |  |  |  | 12291 | return ( \%options, undef ); | 
| 125 |  |  |  |  |  |  | } | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  | 8 |  |  | sub _getopt_spec_warnings { } | 
| 128 |  |  |  |  |  |  |  | 
| 129 |  |  |  |  |  |  | sub _getopt_spec_exception { | 
| 130 | 10 |  |  | 10 |  | 42 | my ($self, $warnings, $exception) = @_; | 
| 131 | 10 |  |  |  |  | 127 | die @$warnings, $exception; | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | sub _getopt_full_usage { | 
| 135 | 6 |  |  | 6 |  | 38 | my ($self, $usage) = @_; | 
| 136 | 6 |  |  |  |  | 23 | $usage->die; | 
| 137 |  |  |  |  |  |  | } | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | sub _usage_format { | 
| 140 | 86 |  |  | 86 |  | 317 | return "usage: %c %o"; | 
| 141 |  |  |  |  |  |  | } | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | sub _traditional_spec { | 
| 144 | 23 |  |  | 23 |  | 54 | my ( $class, %params ) = @_; | 
| 145 |  |  |  |  |  |  |  | 
| 146 | 23 |  |  |  |  | 39 | my ( @options, %name_to_init_arg, %options ); | 
| 147 |  |  |  |  |  |  |  | 
| 148 | 23 |  |  |  |  | 35 | foreach my $opt ( @{ $params{options} } ) { | 
|  | 23 |  |  |  |  | 52 |  | 
| 149 | 119 |  |  |  |  | 207 | push @options, $opt->{opt_string}; | 
| 150 |  |  |  |  |  |  |  | 
| 151 | 119 |  |  |  |  | 207 | my $identifier = $opt->{name}; | 
| 152 | 119 |  |  |  |  | 203 | $identifier =~ s/\W/_/g; # Getopt::Long does this to all option names | 
| 153 |  |  |  |  |  |  |  | 
| 154 | 119 |  |  |  |  | 245 | $name_to_init_arg{$identifier} = $opt->{init_arg}; | 
| 155 |  |  |  |  |  |  | } | 
| 156 |  |  |  |  |  |  |  | 
| 157 | 23 |  |  |  |  | 85 | return ( \@options, \%name_to_init_arg ); | 
| 158 |  |  |  |  |  |  | } | 
| 159 |  |  |  |  |  |  |  | 
| 160 |  |  |  |  |  |  | sub _compute_getopt_attrs { | 
| 161 | 109 |  |  | 109 |  | 179 | my $class = shift; | 
| 162 | 915 |  |  |  |  | 5137 | sort { $a->insertion_order <=> $b->insertion_order } | 
| 163 |  |  |  |  |  |  | grep { | 
| 164 | 610 | 100 |  |  |  | 16677 | $_->does("MouseX::Getopt::Meta::Attribute::Trait") | 
| 165 |  |  |  |  |  |  | or | 
| 166 |  |  |  |  |  |  | $_->name !~ /^_/ | 
| 167 |  |  |  |  |  |  | } grep { | 
| 168 | 109 |  |  |  |  | 315 | !$_->does('MouseX::Getopt::Meta::Attribute::Trait::NoGetopt') | 
|  | 919 |  |  |  |  | 28824 |  | 
| 169 |  |  |  |  |  |  | } $class->meta->get_all_attributes | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | sub _get_cmd_flags_for_attr { | 
| 173 | 556 |  |  | 556 |  | 951 | my ( $class, $attr ) = @_; | 
| 174 |  |  |  |  |  |  |  | 
| 175 | 556 |  |  |  |  | 1142 | my $flag = $attr->name; | 
| 176 |  |  |  |  |  |  |  | 
| 177 | 556 |  |  |  |  | 771 | my @aliases; | 
| 178 |  |  |  |  |  |  |  | 
| 179 | 556 | 100 |  |  |  | 1212 | if ($attr->does('MouseX::Getopt::Meta::Attribute::Trait')) { | 
| 180 | 319 | 100 |  |  |  | 12041 | $flag = $attr->cmd_flag if $attr->has_cmd_flag; | 
| 181 | 319 | 100 |  |  |  | 853 | @aliases = @{ $attr->cmd_aliases } if $attr->has_cmd_aliases; | 
|  | 223 |  |  |  |  | 675 |  | 
| 182 |  |  |  |  |  |  | } | 
| 183 |  |  |  |  |  |  |  | 
| 184 | 556 |  |  |  |  | 4700 | return ( $flag, @aliases ); | 
| 185 |  |  |  |  |  |  | } | 
| 186 |  |  |  |  |  |  |  | 
| 187 |  |  |  |  |  |  | sub _attrs_to_options { | 
| 188 | 109 |  |  | 109 |  | 226 | my $class = shift; | 
| 189 | 109 |  | 100 |  |  | 469 | my $config_from_file = shift || {}; | 
| 190 |  |  |  |  |  |  |  | 
| 191 | 109 |  |  |  |  | 197 | my @options; | 
| 192 |  |  |  |  |  |  |  | 
| 193 | 109 |  |  |  |  | 297 | foreach my $attr ($class->_compute_getopt_attrs) { | 
| 194 | 556 |  |  |  |  | 1631 | my ( $flag, @aliases ) = $class->_get_cmd_flags_for_attr($attr); | 
| 195 |  |  |  |  |  |  |  | 
| 196 | 556 |  |  |  |  | 1244 | my $opt_string = join(q{|}, $flag, @aliases); | 
| 197 |  |  |  |  |  |  |  | 
| 198 | 556 | 100 |  |  |  | 2025 | if ($attr->name eq 'configfile') { | 
|  |  | 50 |  |  |  |  |  | 
| 199 | 11 |  |  |  |  | 16 | $opt_string .= '=s'; | 
| 200 |  |  |  |  |  |  | } | 
| 201 |  |  |  |  |  |  | elsif ($attr->has_type_constraint) { | 
| 202 | 545 |  |  |  |  | 1067 | my $type = $attr->type_constraint; | 
| 203 | 545 | 50 |  |  |  | 1332 | if (MouseX::Getopt::OptionTypeMap->has_option_type($type)) { | 
| 204 | 545 |  |  |  |  | 1268 | $opt_string .= MouseX::Getopt::OptionTypeMap->get_option_type($type) | 
| 205 |  |  |  |  |  |  | } | 
| 206 |  |  |  |  |  |  | } | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | push @options, { | 
| 209 |  |  |  |  |  |  | name       => $flag, | 
| 210 |  |  |  |  |  |  | init_arg   => $attr->init_arg, | 
| 211 |  |  |  |  |  |  | opt_string => $opt_string, | 
| 212 | 556 | 100 | 66 |  |  | 3982 | required   => $attr->is_required && !$attr->has_default && !$attr->has_builder && !exists $config_from_file->{$attr->name}, | 
| 213 |  |  |  |  |  |  | # NOTE: | 
| 214 |  |  |  |  |  |  | # this "feature" was breaking because | 
| 215 |  |  |  |  |  |  | # Getopt::Long::Descriptive would return | 
| 216 |  |  |  |  |  |  | # the default value as if it was a command | 
| 217 |  |  |  |  |  |  | # line flag, which would then override the | 
| 218 |  |  |  |  |  |  | # one passed into a constructor. | 
| 219 |  |  |  |  |  |  | # See 100_gld_default_bug.t for an example | 
| 220 |  |  |  |  |  |  | # - SL | 
| 221 |  |  |  |  |  |  | #( ( $attr->has_default && ( $attr->is_default_a_coderef xor $attr->is_lazy ) ) ? ( default => $attr->default({}) ) : () ), | 
| 222 |  |  |  |  |  |  | ( $attr->has_documentation ? ( doc => $attr->documentation ) : () ), | 
| 223 |  |  |  |  |  |  | } | 
| 224 |  |  |  |  |  |  | } | 
| 225 |  |  |  |  |  |  |  | 
| 226 | 109 |  |  |  |  | 572 | return @options; | 
| 227 |  |  |  |  |  |  | } | 
| 228 |  |  |  |  |  |  |  | 
| 229 | 22 |  |  | 22 |  | 194 | no Mouse::Role; | 
|  | 22 |  |  |  |  | 50 |  | 
|  | 22 |  |  |  |  | 110 |  | 
| 230 |  |  |  |  |  |  | 1; | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | ## In your class | 
| 235 |  |  |  |  |  |  | package My::App; | 
| 236 |  |  |  |  |  |  | use Mouse; | 
| 237 |  |  |  |  |  |  |  | 
| 238 |  |  |  |  |  |  | with 'MouseX::Getopt::Basic'; | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | has 'out' => (is => 'rw', isa => 'Str', required => 1); | 
| 241 |  |  |  |  |  |  | has 'in'  => (is => 'rw', isa => 'Str', required => 1); | 
| 242 |  |  |  |  |  |  |  | 
| 243 |  |  |  |  |  |  | # ... rest of the class here | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | ## in your script | 
| 246 |  |  |  |  |  |  | #!/usr/bin/perl | 
| 247 |  |  |  |  |  |  |  | 
| 248 |  |  |  |  |  |  | use My::App; | 
| 249 |  |  |  |  |  |  |  | 
| 250 |  |  |  |  |  |  | my $app = My::App->new_with_options(); | 
| 251 |  |  |  |  |  |  | # ... rest of the script here | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | ## on the command line | 
| 254 |  |  |  |  |  |  | % perl my_app_script.pl --in file.input --out file.dump | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 257 |  |  |  |  |  |  |  | 
| 258 |  |  |  |  |  |  | This is like L and can be used instead except that it | 
| 259 |  |  |  |  |  |  | doesn't make use of L (or "GLD" for short). | 
| 260 |  |  |  |  |  |  |  | 
| 261 |  |  |  |  |  |  | =over 4 | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | =item new_with_options | 
| 264 |  |  |  |  |  |  |  | 
| 265 |  |  |  |  |  |  | See L. | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  | =back | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | =cut |