From: Piotr Roszatycki Date: Sun, 26 Oct 2008 19:16:44 +0000 (+0000) Subject: * Handling with Getopt parser implemented as strategy pattern. X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=550da40200cbc32e0f608ccd8ab6ec5ce4ccce75;p=gitmo%2FMooseX-Getopt.git * Handling with Getopt parser implemented as strategy pattern. --- diff --git a/ChangeLog b/ChangeLog index 7477c6f..96b8290 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,13 @@ Revision history for Perl extension MooseX-Getopt * MooseX::Getopt - Use Moose's throw_error() method. (dexter) + * MooseX::Getopt + * MooseX::Getopt::Parser + * MooseX::Getopt::Parser::Long + * MooseX::Getopt::Parser::Descriptive + - Handling with Getopt parser implemented as strategy pattern. + (dexter) + 0.15 Sat. July 26 2008 * MooseX::Getopt::OptionTypeMap - Accept type constraint objects in the type mapping, not just names diff --git a/Makefile.PL b/Makefile.PL index 6015a1f..7d2e0d2 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -7,6 +7,7 @@ all_from 'lib/MooseX/Getopt.pm'; requires 'Moose' => '0.43'; requires 'Getopt::Long' => '2.37'; +requires 'maybe' => '0.02'; # optional requires 'Getopt::Long::Descriptive' => 0; diff --git a/lib/MooseX/Getopt.pm b/lib/MooseX/Getopt.pm index 59e32a7..3a087cf 100644 --- a/lib/MooseX/Getopt.pm +++ b/lib/MooseX/Getopt.pm @@ -2,23 +2,57 @@ package MooseX::Getopt; use Moose::Role; +use Moose::Util::TypeConstraints; + use MooseX::Getopt::OptionTypeMap; + +use MooseX::Getopt::Parser::Long; +use maybe 'MooseX::Getopt::Parser::Descriptive'; + use MooseX::Getopt::Meta::Attribute; use MooseX::Getopt::Meta::Attribute::NoGetopt; -use Getopt::Long (); # GLD uses it anyway, doesn't hurt -use constant HAVE_GLD => not not eval { require Getopt::Long::Descriptive }; our $VERSION = '0.15'; our $AUTHORITY = 'cpan:STEVAN'; -has ARGV => (is => 'rw', isa => 'ArrayRef', metaclass => "NoGetopt"); -has extra_argv => (is => 'rw', isa => 'ArrayRef', metaclass => "NoGetopt"); + +has ARGV => ( + is => 'rw', + isa => 'ArrayRef', + metaclass => 'NoGetopt', +); + +has extra_argv => ( + is => 'rw', + isa => 'ArrayRef', + metaclass => 'NoGetopt', +); + +has getopt_parser => ( + is => 'rw', + does => 'MooseX::Getopt::Parser', + metaclass => 'NoGetopt', +); sub new_with_options { my ($class, @params) = @_; my $config_from_file; + + my $constructor_params = ( @params == 1 ? $params[0] : {@params} ); + + my $getopt_parser; + if (defined $constructor_params->{getopt_parser}) { + $getopt_parser = $constructor_params->{getopt_parser}; + $getopt_parser = $getopt_parser->new if not ref $getopt_parser; + } + else { + $getopt_parser = maybe::HAVE_MOOSEX_GETOPT_PARSER_DESCRIPTIVE + ? MooseX::Getopt::Parser::Descriptive->new + : MooseX::Getopt::Parser::Long->new; + } + if($class->meta->does_role('MooseX::ConfigFromFile')) { local @ARGV = @ARGV; @@ -36,8 +70,6 @@ sub new_with_options { } } - my $constructor_params = ( @params == 1 ? $params[0] : {@params} ); - $class->throw_error("Single parameters to new_with_options() must be a HASH ref") unless ref($constructor_params) eq 'HASH'; @@ -46,6 +78,7 @@ sub new_with_options { $class->_attrs_to_options( $config_from_file ) ], params => $constructor_params, + getopt_parser => $getopt_parser, ); my $params = $config_from_file ? { %$config_from_file, %{$processed{params}} } : $processed{params}; @@ -53,17 +86,18 @@ sub new_with_options { $class->new( ARGV => $processed{argv_copy}, extra_argv => $processed{argv}, + getopt_parser => $getopt_parser, @params, # explicit params to ->new %$params, # params from CLI ); } sub _parse_argv { - my ( $class, %params ) = @_; + my ($class, %params) = @_; local @ARGV = @{ $params{argv} || \@ARGV }; - my ( $opt_spec, $name_to_init_arg ) = ( HAVE_GLD ? $class->_gld_spec(%params) : $class->_traditional_spec(%params) ); + my ( $opt_spec, $name_to_init_arg ) = ( $params{getopt_parser}->_get_getopt_spec(%params) ); # Get a clean copy of the original @ARGV my $argv_copy = [ @ARGV ]; @@ -73,13 +107,7 @@ sub _parse_argv { my ( $parsed_options, $usage ) = eval { local $SIG{__WARN__} = sub { push @err, @_ }; - if ( HAVE_GLD ) { - return Getopt::Long::Descriptive::describe_options($class->_usage_format(%params), @$opt_spec); - } else { - my %options; - Getopt::Long::GetOptions(\%options, @$opt_spec); - return ( \%options, undef ); - } + return $params{getopt_parser}->getoptions($opt_spec); }; die join "", grep { defined } @err, $@ if @err or $@; @@ -101,59 +129,6 @@ sub _parse_argv { ); } -sub _usage_format { - return "usage: %c %o"; -} - -sub _traditional_spec { - my ( $class, %params ) = @_; - - my ( @options, %name_to_init_arg, %options ); - - foreach my $opt ( @{ $params{options} } ) { - push @options, $opt->{opt_string}; - - my $identifier = $opt->{name}; - $identifier =~ s/\W/_/g; # Getopt::Long does this to all option names - - $name_to_init_arg{$identifier} = $opt->{init_arg}; - } - - return ( \@options, \%name_to_init_arg ); -} - -sub _gld_spec { - my ( $class, %params ) = @_; - - my ( @options, %name_to_init_arg ); - - my $constructor_params = $params{params}; - - foreach my $opt ( @{ $params{options} } ) { - push @options, [ - $opt->{opt_string}, - $opt->{doc} || ' ', # FIXME new GLD shouldn't need this hack - { - ( ( $opt->{required} && !exists($constructor_params->{$opt->{init_arg}}) ) ? (required => $opt->{required}) : () ), - # NOTE: - # remove this 'feature' because it didn't work - # all the time, and so is better to not bother - # since Moose will handle the defaults just - # fine anyway. - # - SL - #( exists $opt->{default} ? (default => $opt->{default}) : () ), - }, - ]; - - my $identifier = $opt->{name}; - $identifier =~ s/\W/_/g; # Getopt::Long does this to all option names - - $name_to_init_arg{$identifier} = $opt->{init_arg}; - } - - return ( \@options, \%name_to_init_arg ); -} - sub _compute_getopt_attrs { my $class = shift; grep { diff --git a/lib/MooseX/Getopt/Parser.pm b/lib/MooseX/Getopt/Parser.pm new file mode 100644 index 0000000..0c67e74 --- /dev/null +++ b/lib/MooseX/Getopt/Parser.pm @@ -0,0 +1,7 @@ + +package MooseX::Getopt::Parser; + +use Moose::Role; +requires '_get_getopt_spec', 'getoptions'; + +1; diff --git a/lib/MooseX/Getopt/Parser/Descriptive.pm b/lib/MooseX/Getopt/Parser/Descriptive.pm new file mode 100644 index 0000000..bdcc26e --- /dev/null +++ b/lib/MooseX/Getopt/Parser/Descriptive.pm @@ -0,0 +1,51 @@ + +package MooseX::Getopt::Parser::Descriptive; + +use Moose; + +with 'MooseX::Getopt::Parser'; + +use Getopt::Long::Descriptive; + +sub getoptions { + my ($class, $opt_spec) = @_; + return Getopt::Long::Descriptive::describe_options($class->_usage_format, @$opt_spec); +} + +sub _get_getopt_spec { + my ($class, %params) = @_; + + my (@options, %name_to_init_arg ); + + my $constructor_params = $params{params}; + + foreach my $opt ( @{ $params{options} } ) { + push @options, [ + $opt->{opt_string}, + $opt->{doc} || ' ', # FIXME new GLD shouldn't need this hack + { + ( ( $opt->{required} && !exists($constructor_params->{$opt->{init_arg}}) ) ? (required => $opt->{required}) : () ), + # NOTE: + # remove this 'feature' because it didn't work + # all the time, and so is better to not bother + # since Moose will handle the defaults just + # fine anyway. + # - SL + #( exists $opt->{default} ? (default => $opt->{default}) : () ), + }, + ]; + + my $identifier = $opt->{name}; + $identifier =~ s/\W/_/g; # Getopt::Long does this to all option names + + $name_to_init_arg{$identifier} = $opt->{init_arg}; + } + + return ( \@options, \%name_to_init_arg ); +} + +sub _usage_format { + return "usage: %c %o"; +} + +1; diff --git a/lib/MooseX/Getopt/Parser/Long.pm b/lib/MooseX/Getopt/Parser/Long.pm new file mode 100644 index 0000000..e7fd7d3 --- /dev/null +++ b/lib/MooseX/Getopt/Parser/Long.pm @@ -0,0 +1,35 @@ + +package MooseX::Getopt::Parser::Long; + +use Moose; + +with 'MooseX::Getopt::Parser'; + +sub getoptions { + my ($class, $opt_spec) = @_; + + my %options; + + my $getopt = Getopt::Long::Parser->new; + $getopt->getoptions(\%options, @$opt_spec); + return ( \%options, undef ); +} + +sub _get_getopt_spec { + my ($class, %params) = @_; + + my ( @options, %name_to_init_arg, %options ); + + foreach my $opt ( @{ $params{options} } ) { + push @options, $opt->{opt_string}; + + my $identifier = $opt->{name}; + $identifier =~ s/\W/_/g; # Getopt::Long does this to all option names + + $name_to_init_arg{$identifier} = $opt->{init_arg}; + } + + return ( \@options, \%name_to_init_arg ); +} + +1;