使用Pod :: Usage和GetOpt :: Long获取-h违反了DRY原则

时间:2015-06-21 08:20:51

标签: perl

我想为我的Perl程序提供更好的文档。为此,我没有很多解决方案。 Actuatlly我发现只有一个:POD。

对于我们通常拥有的NAME部分:

=head1 NAME

program_name - Short description

=cut

需要5行,其中唯一的相关信息是简短描述。 program_name应自动填充basename($0)

然后是选项。从GetOptions参数中,我可以自动提取:

  • 每个选项的名称
  • 参数必须是

我们可以轻松添加categorymandatorydescriptionin_conflict_with等选项。那我为什么要在POD中重复一遍呢?

为了说明我的例子,我写了这个:

#!/usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use Getopt::Long;
use Pod::Usage 'pod2usage';
use File::Basename;

=head1 AUTHOR

John Doe <j@doe.com>

=head1 DESCRIPTION

B<This program> does nothing important. But at least it is properly documented.

=head1 COPYRIGHT

    Copyright(c) Nobody

=head1 VERSION

    v0.1

=cut


my %cfg;
CliInfo (
    args => [
    {arg => "l|length=i", dest => \$cfg{length}, desc => "Length of the string", type => 'mandatory'},
    {arg => "f|file=s"  , dest => \$cfg{data},   desc => "Input filename", type => 'mandatory'},
    {arg => "v"         , dest => sub {\$cfg{verbose}++}, desc => "Verbose"},
    ]
);

sub CliInfo { 
    my %info = @_;
    my $programName = basename $0;
    GetOptions(map( {$_->{arg} => $_->{dest}}  @{$info{args}}), 
        'h|help' => sub {
            say "Usage: $programName [options]...";
            say pod2scalar(-verbose => 99, -sections => "DESCRIPTION");
            say "\nOptions:";
            say sprintf('%-20s', (sub {
                my @opt = split /\|/, shift =~ s/=.*//r; 
                return "  -$opt[0], --$opt[1]" if @opt > 1;
                return "      --$opt[0]" if length $opt[0] > 1;
                return "  -$opt[0]";
            })->($_->{arg})), $_->{desc} for @{$info{args}};
            say "\n";
        }, 
        'version' => sub {
            my $ver = pod2scalar(-verbose => 99, -sections => "VERSION");
            say STDERR $programName, " - $ver";
            say pod2scalar(-verbose => 99, -sections => "COPYRIGHT");
            say "";
            exit;
        })
        or say "Invalid option, try --help" and exit 1; 

    sub pod2scalar {
        open my $fh, '>', \my $text;
        pod2usage(@_, -output => $fh, -exitval => 'NOEXIT');
        $text =~ s/^(?:.*\n)//;
        $text =~ s/(?:\s*\n)\z//r;
    }
}

__END__

这给出了这个输出(也与GNU标准非常兼容):

$ ./help.pl  --help
Usage: help.pl [options]...
This program does nothing important. But at least it is properly documented.

Options:
  -l, --length      Length of the string
  -f, --file        Input filename
  -v                Verbose

所以问题是:

  

对于DRY原则,我在这里展示的是否有任何标准和类似的解决方案?

1 个答案:

答案 0 :(得分:1)

也许你需要Perl库Getopt::Long::Descriptive

这是用Getopt::Long::Descriptive重写的脚本:

#!/usr/bin/env perl

use strict;
use warnings;

use Pod::Usage 'pod2usage';
use Capture::Tiny ':all';
use Getopt::Long::Descriptive;

=head1 AUTHOR

John Doe <j@doe.com>

=head1 DESCRIPTION

B<This program> does nothing important. But at least it is properly documented.

=head1 COPYRIGHT

    Copyright(c) Nobody

=head1 VERSION

    v0.1

=cut

sub pod2scalar {
    my $stdout = capture_merged {
        pod2usage(-verbose => 99, -sections => "DESCRIPTION", -exitval => "noexit");
    };

    return $stdout;
}

my ($opt, $usage) = describe_options(
    pod2scalar() . "%c %o <some-arg>",
    [ 'l|length=i', 'Length of the string', { required => 1, default => 4  } ],
    [ 'f|file=s', 'Input filename', { required => 1  } ],
    [ 'v', 'Verbose' ],
    [ 'help', "print usage message and exit" ],
    {
        show_defaults => 1,
    },
);

if ($opt->help) {
    print($usage->text);
    exit;
}

这是原始脚本的输出:

$ ./before.pl --help
Usage: before.pl [options]...
    This program does nothing important. But at least it is properly
    documented.

Options:
  -l, --length      Length of the string
  -f, --file        Input filename
  -v                Verbose

这是新脚本的输出:

$ ./after.pl --help
Mandatory parameter 'f' missing in call to (eval)

Description:
 This program does nothing important. But at least it is properly
 documented.

after.pl [-flv] [long options...] <some-arg>
        --length INT -l INT   Length of the string
                              (default value: 4)
        --file STR -f STR     Input filename
        -v                    Verbose
        --help                print usage message and exit