Reputation: 29096
I would like to give a better documentation to my Perl Programs. For this I don't have many solutions. Actuatlly I found only one: the POD.
For the NAME
section we usually have:
=head1 NAME
program_name - Short description
=cut
It takes 5 lines where the only relevant information is the short description. The program_name should be automatically filled with basename($0)
.
Then it comes the options. From the GetOptions
arguments, I can automatically extract:
We can easily add options such as category
, mandatory
, description
and in_conflict_with
. So why should I repeat myself in the POD?
To illustrate my example I wrote this:
#!/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 <[email protected]>
=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__
Which gives this output (Also pretty compatible with the GNU standards):
$ ./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
So the question:
Is there any standard and similar solution to what I am showing here with respect of the DRY principle?
Upvotes: 0
Views: 78
Reputation: 11871
Maybe Perl library Getopt::Long::Descriptive is what you need.
This is your script rewritten with 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 <[email protected]>
=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;
}
This is the output of your original script:
$ ./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
And here is the output of the new script:
$ ./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
Upvotes: 1