X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FMooseX%2FGetopt.pm;h=7011a5584e3e04ae46494d56a898a21a3ef20780;hb=6bb4cb66b12d95e6e9bb764eed6fb10cb55383ea;hp=b9a5a1d17955751379a9d4f1abe0b38afe08b24a;hpb=2482085fd5e5adf1d2aa6a37a7099c5fcd139f01;p=gitmo%2FMooseX-Getopt.git diff --git a/lib/MooseX/Getopt.pm b/lib/MooseX/Getopt.pm index b9a5a1d..7011a55 100644 --- a/lib/MooseX/Getopt.pm +++ b/lib/MooseX/Getopt.pm @@ -2,22 +2,108 @@ package MooseX::Getopt; use Moose::Role; -use Getopt::Long (); +use Getopt::Long::Descriptive (); use MooseX::Getopt::OptionTypeMap; use MooseX::Getopt::Meta::Attribute; +use MooseX::Getopt::Meta::Attribute::NoGetopt; -our $VERSION = '0.05'; +our $VERSION = '0.08'; our $AUTHORITY = 'cpan:STEVAN'; has ARGV => (is => 'rw', isa => 'ArrayRef'); has extra_argv => (is => 'rw', isa => 'ArrayRef'); sub new_with_options { - my ($class, %params) = @_; + my ($class, @params) = @_; - my (@options, %name_to_init_arg); - foreach my $attr ($class->meta->compute_all_applicable_attributes) { + my %processed = $class->_parse_argv( + options => [ + $class->_attrs_to_options( @params ) + ] + ); + + my $params = $processed{params}; + + if($class->meta->does_role('MooseX::ConfigFromFile') + && defined $params->{configfile}) { + %$params = ( + %{$class->get_config_from_file($params->{configfile})}, + %$params, + ); + } + + $class->new( + ARGV => $processed{argv_copy}, + extra_argv => $processed{argv}, + @params, # explicit params to ->new + %$params, # params from CLI + ); +} + +sub _parse_argv { + my ( $class, %params ) = @_; + + local @ARGV = @{ $params{argv} || \@ARGV }; + + my ( @options, %name_to_init_arg ); + + foreach my $opt ( @{ $params{options} } ) { + push @options, [ + $opt->{opt_string}, + $opt->{doc} || ' ', + { + ( $opt->{required} ? (required => $opt->{required}) : () ), + ( exists $opt->{default} ? (default => $opt->{default}) : () ), + }, + ]; + + $name_to_init_arg{ $opt->{name} } = $opt->{init_arg}; + } + + # Get a clean copy of the original @ARGV + my $argv_copy = [ @ARGV ]; + + my @err; + + my ( $parsed_options, $usage ) = eval { + local $SIG{__WARN__} = sub { push @err, @_ }; + Getopt::Long::Descriptive::describe_options("usage: %c %o", @options) + }; + + die join "", grep { defined } @err, $@ if @err or $@; + + # Get a copy of the Getopt::Long-mangled @ARGV + my $argv_mangled = [ @ARGV ]; + + return ( + argv_copy => $argv_copy, + argv => $argv_mangled, + params => { + map { + $name_to_init_arg{$_} => $parsed_options->{$_} + } keys %$parsed_options, + } + ); +} + +sub _compute_getopt_attrs { + my $class = shift; + grep { + $_->isa("MooseX::Getopt::Meta::Attribute") + or + $_->name !~ /^_/ + && + !$_->isa('MooseX::Getopt::Meta::Attribute::NoGetopt') + } $class->meta->compute_all_applicable_attributes +} + +sub _attrs_to_options { + my $class = shift; + + my @options; + + foreach my $attr ($class->_compute_getopt_attrs) { my $name = $attr->name; my $aliases; @@ -26,55 +112,29 @@ sub new_with_options { $name = $attr->cmd_flag if $attr->has_cmd_flag; $aliases = $attr->cmd_aliases if $attr->has_cmd_aliases; } - else { - next if $name =~ /^_/; - } - - $name_to_init_arg{$name} = $attr->init_arg; - + my $opt_string = $aliases ? join(q{|}, $name, @$aliases) : $name; if ($attr->has_type_constraint) { my $type_name = $attr->type_constraint->name; - if (MooseX::Getopt::OptionTypeMap->has_option_type($type_name)) { - $opt_string .= MooseX::Getopt::OptionTypeMap->get_option_type($type_name); + if (MooseX::Getopt::OptionTypeMap->has_option_type($type_name)) { + $opt_string .= MooseX::Getopt::OptionTypeMap->get_option_type($type_name) } } - - push @options => $opt_string; - } - - my %options; - # Get a clean copy of the original @ARGV - my $argv_copy = [ @ARGV ]; - - { - local $SIG{__WARN__} = sub { die $_[0] }; - Getopt::Long::GetOptions(\%options, @options); + push @options, { + name => $name, + init_arg => $attr->init_arg, + opt_string => $opt_string, + required => $attr->is_required, + ( ( $attr->has_default && ( $attr->is_default_a_coderef xor $attr->is_lazy ) ) ? ( default => $attr->default({}) ) : () ), + ( $attr->has_documentation ? ( doc => $attr->documentation ) : () ), + } } - # Get a copy of the Getopt::Long-mangled @ARGV - my $argv_mangled = [ @ARGV ]; - - # Restore the original @ARGV; - @ARGV = @$argv_copy; - - #use Data::Dumper; - #warn Dumper \@options; - #warn Dumper \%name_to_init_arg; - #warn Dumper \%options; - - $class->new( - ARGV => $argv_copy, - extra_argv => $argv_mangled, - %params, - map { - $name_to_init_arg{$_} => $options{$_} - } keys %options, - ); + return @options; } no Moose::Role; 1; @@ -125,6 +185,9 @@ accordingly. You can use the attribute metaclass L to get non-default commandline option names and aliases. +You can use the attribute metaclass L +to have C ignore your attribute in the commandline options. + By default, attributes which start with an underscore are not given commandline argument support, unless the attribute's metaclass is set to L. If you don't want you accessors @@ -139,6 +202,11 @@ to have the leading underscore in thier name, you can do this: This will mean that Getopt will not handle a --foo param, but your code can still call the C method. +If your class also uses a configfile-loading role based on +L, such as L, +L's C will load the configfile +specified by the C<--configfile> option for you. + =head2 Supported Type Constraints =over 4