Как указать ширину в pod2usage?

У меня есть небольшой модуль perl, и я использую Getopt::Long, и я подумал, что мог бы также использовать Pod::Usage, чтобы получить красивое отображение справки.

После некоторой возни я заставил его работать достаточно хорошо, за одним небольшим исключением. Я не могу установить ширину вывода.

Мой терминал имеет ширину 191 символ. Используя perldoc Module.pm, он правильно форматирует документацию по этой ширине. Используя pod2usage(), он использует ширину по умолчанию 76 символов.

Я не могу понять, как передать параметр ширины форматтеру. В документации показано, как установить другой модуль форматирования (например, Pod::Text::Termcap) с помощью блока BEGIN, и я использовал Term::ReadKey для получения ширины (проверено), но я просто не могу заставить форматировщик видеть это.

Любые подсказки?

Вот полный модуль, который я пытаюсь протестировать, вместе с небольшим тестовым скриптом для его загрузки. Чтобы понять, что я имею в виду, откройте терминал с разумной шириной (132 или больше, так что это очевидно) и сравните вывод «./test.pl --man» с выводом «perldoc MUD::Config» .

Я могу жить без верхних и нижних колонтитулов в стиле справочной страницы, которые добавляет perldoc, но я хотел бы, чтобы он уважал (и использовал) ширину терминала.

test.pl

#!/usr/bin/perl -w

use strict;
use warnings;

use MUD::Config;
#use MUD::Logging;

my $config = new MUD::Config @ARGV;
#my $logger = new MUD::Logging $config;

#$bootlog->info("Logging initialized");
#$bootlog->info("Program exiting");

и MUD/Config.pm

#!/usr/bin/perl -w

package MUD::Config;

=pod

=head1 NAME

MUD::Config --  Configuration options for PocketMUD

=head1 SYNOPSIS

./PocketMUD [OPTIONS]

=head1 OPTIONS

=over 8

=item B<--dbname>

Specifiy the name of the database used by PocketMUD S<(default B<pocketmud>)>.

=item B<--dbhost>

Specify the IP address used to connect to the database S<(default B<localhost>)>.

=item B<--dbport>

Specify the port number used to connect to the database S<(default B<5432>)>.

=item B<--dbuser>

Specify the username used to connect to the database S<(default B<quixadhal>)>.

=item B<--dbpass>

Specify the password used to connect to the database S<(default B<password>)>.

=item B<--dsn>

The DSN is the full connection string used to connect to the database.  It includes the
values listed above, as well as several other options specific to the database used.

S<(default B<DBI:Pg:dbname=$db_name;host=$db_host;port=$db_port;sslmode=prefer;options=--autocommit=on>)>

=item B<--logfile>

Specify the text file used for debugging/logging output S<(default B</home/quixadhal/PocketMUD/debug-server.log>)>.

=item B<--port>

Specify the port used for player connections S<(default B<4444>)>.

=item B<--help>

Display usage information for PocketmUD.

=item B<--man>

Display full documentation of configuration module details.

=back

=head1 DESCRIPTION

PocketMUD is a perl re-implementation of SocketMUD.

It is meant to be a barebones MUD server, written in perl,
which can be easily modified and extended.

=head1 METHODS

=cut

use strict;
use warnings;
use Getopt::Long qw( GetOptionsFromArray );
use Config::IniFiles;
use Data::Dumper;

BEGIN {
    use Term::ReadKey;

    my ($width, $height, $pixel_width, $pixel_height) = GetTerminalSize();
    #print "WIDTH: $width\n";
    $Pod::Usage::Formatter = 'Pod::Text::Termcap';
    $Pod::Usage::width = $width;
}

use Pod::Usage;
use Pod::Find qw(pod_where);

Getopt::Long::Configure('prefix_pattern=(?:--|-)?');    # Make dashes optional for arguments

=pod

B<new( @ARGV )> (constructor)

Create a new configuration class.  You should only need ONE instance of this
class, under normal circumstances.

Parameters passed in are usually the command line's B<@ARGV> array.  Options that
can be specified are listed in the B<OPTIONS> section, above.

Returns: configuration data object.

=cut


sub new {
    my $class = shift;
    my @args = @_;
    my ($db_name, $db_host, $db_port, $db_user, $db_pass, $DSN);
    my ($logfile, $port);
    my $HOME = $ENV{HOME} || ".";

    # Order matters... First we check the global config file, then the local one...
    foreach my $cfgfile ( "/etc/pocketmud.ini", "$HOME/.pocketmud.ini", "./pocketmud.ini" ) {
        next if !-e $cfgfile;
        my $cfg = Config::IniFiles->new( -file  => "$cfgfile", -handle_trailing_comment => 1, -nocase => 1, -fallback => 'GENERAL', -default => 'GENERAL' );
        $db_name = $cfg->val('database', 'name')        if $cfg->exists('database', 'name');
        $db_host = $cfg->val('database', 'host')        if $cfg->exists('database', 'host');
        $db_port = $cfg->val('database', 'port')        if $cfg->exists('database', 'port');
        $db_user = $cfg->val('database', 'user')        if $cfg->exists('database', 'user');
        $db_pass = $cfg->val('database', 'password')    if $cfg->exists('database', 'password');
        $DSN = $cfg->val('database', 'dsn')             if $cfg->exists('database', 'dsn');
        $logfile = $cfg->val('general', 'logfile')      if $cfg->exists('general', 'logfile');
        $port = $cfg->val('general', 'port')            if $cfg->exists('general', 'port');
    }

    # Then we check arguments from the constructor
    GetOptionsFromArray( \@args ,
        'dbname:s'      => \$db_name,
        'dbhost:s'      => \$db_host,
        'dbport:i'      => \$db_port,
        'dbuser:s'      => \$db_user,
        'dbpass:s'      => \$db_pass,
        'dsn:s'         => \$DSN,
        'logfile:s'     => \$logfile,
        'port:i'        => \$port,
        'help|?'        => sub { pod2usage( -input => pod_where( {-inc => 1}, __PACKAGE__), -exitval => 1 ); },
        'man'           => sub { pod2usage( -input => pod_where( {-inc => 1}, __PACKAGE__), -exitval => 2, -verbose => 2 ); },
    );

    # Finally, we fall back to hard-coded defaults
    $db_name = 'pocketmud'  if !defined $db_name and !defined $DSN;
    $db_host = 'localhost'  if !defined $db_host and !defined $DSN;
    $db_port = 5432         if !defined $db_port and !defined $DSN;
    $db_user = 'quixadhal'  if !defined $db_user;
    $db_pass = 'password'   if !defined $db_pass;
    $logfile = '/home/quixadhal/PocketMUD/debug-server.log' if !defined $logfile;
    $port    = 4444         if !defined $port;

    $DSN = "DBI:Pg:dbname=$db_name;host=$db_host;port=$db_port;sslmode=prefer;options=--autocommit=on" if !defined $DSN and defined $db_name and defined $db_host and defined $db_port;

    die "Either a valid DSN or a valid database name, host, and port MUST exist in configuration data" if !defined $DSN;
    die "A valid database username MUST exist in configuration data" if !defined $db_user;
    die "A valid database password MUST exist in configuration data" if !defined $db_pass;
    die "A valid logfile MUST be defined in configuration data" if !defined $logfile;
    die "A valid port MUST be defined in configuration data" if !defined $port;


    my $self = {
        DB_NAME => $db_name,
        DB_HOST => $db_host,
        DB_PORT => $db_port,
        DB_USER => $db_user,
        DB_PASS => $db_pass,
        DSN     => $DSN,
        LOGFILE => $logfile,
        PORT    => $port,
    };

    bless $self, $class;
    print Dumper($self);
    return $self;
}

sub dsn {
    my $self = shift;
    if ( @_ ) {
        $self->{DSN} = shift;
    }
    return $self->{DSN};
}

sub db_user {
    my $self = shift;
    if ( @_ ) {
        $self->{DB_USER} = shift;
    }
    return $self->{DB_USER};
}

sub db_pass {
    my $self = shift;
    if ( @_ ) {
        $self->{DB_PASS} = shift;
    }
    return $self->{DB_PASS};
}

sub logfile {
    my $self = shift;
    if ( @_ ) {
        $self->{LOGFILE} = shift;
    }
    return $self->{LOGFILE};
}

sub port {
    my $self = shift;
    if ( @_ ) {
        $self->{PORT} = shift;
    }
    return $self->{PORT};
}

1;

person Quixadhal    schedule 13.10.2013    source источник
comment
Возможно, я забыл использовать Getopt::Long qw( GetOptionsFromArray ); Поскольку я вырезал и вставил соответствующие детали, чтобы не публиковать большой файл с лишним мусором.   -  person Quixadhal    schedule 13.10.2013
comment
Ok. Я выложу полный модуль и небольшой тестовый скрипт, который его использует. pod::find был необходим для хранения документации по использованию в модуле конфигурации, а не на верхнем уровне.   -  person Quixadhal    schedule 14.10.2013


Ответы (2)


Чувак, я действительно искал вызов в этом...

Я взглянул на код для Pod::Usage, чтобы увидеть, что происходит. Pod::Usage использует команду perldoc напрямую, поэтому кажется, что и perldoc, и pod2usage будут печататься одинаково. Фактически, в моей системе и perldoc, и pod2usage по умолчанию имеют более или менее 80 столбцов. Я не знаю, почему в вашей системе по-другому.

Есть одно исключение:

Если вы установите аргумент -noperldoc, он будет использовать Pod::Text для форматирования, а Pod::Text имеет параметр -width => $width при создании нового объекта Pod::Text.

Я подумал, что смогу передать недокументированный параметр -width в pod2usage, и он будет передан созданному объекту $parser. Этот объект является объектом Pod::Usage, но Pod::Usage является подклассом Pod::Text.

Нет игральных костей.

Параметры передаются как часть отдельных параметров USAGE_OPT, поэтому $opt_width задано неправильно. Для Pod::Text нет функционального интерфейса, поэтому ширина связана с конкретным объектом $parser, а не с пакетом в целом. Вы не можете установить $Pod::Text::width и переопределить значение по умолчанию 76. Это жестко запрограммировано в программе по умолчанию.

Есть способ использовать Pod::Text, найти способ получить ширину терминала, передать ее вашему объекту Pod::Text, а затем использовать этот объект, вызывая метод parse_from_file to в исходном файле.

Было бы много работы, если бы вы могли видеть вывод POD шириной 130 символов, если ширина терминала составляет 130 символов.

person David W.    schedule 14.10.2013
comment
Что еще более странно, так это то, что если я вызову perldoc MUD::Config в командной строке, в интерактивной подсказке bash, он отобразит документацию в виде правильной справочной страницы... следуя правильной ширине терминала и используя полужирный шрифт, как я и ожидал.‹ p> Однако, делая тот же вызов внутри Perl, используя обратные кавычки, я получаю открытый текст из 76 столбцов, который мне не нужен. - person Quixadhal; 15.10.2013
comment
@Quixadhal pod2usage в конечном итоге использует Perldoc, который выглядит так, как будто он использовал Pod::Man, а затем использует nroff для фактического создания макета. Если что-то форматирует страницу до ширины 60 символов, это будет nroff. Например, вы можете использовать pod2man | troff -man | less, что на самом деле делает perldoc. -man на самом деле является макросом форматирования an, который будет использовать nroff. Здесь будет указана ширина страницы. Вы говорите о технологии форматирования страниц 1970-х годов. Я понятия не имею, как работают эти различные макросы. - person David W.; 15.10.2013
comment
Я знаком с nroff (и troff) и могу сказать вам, что nroff может (и должен) учитывать размер вашего терминала. Вот почему большинство систем хранят справочные страницы в формате nroff, а не запускают catman для их предварительного рендеринга, так как версии с предварительным рендерингом будут иметь фиксированную ширину. - person Quixadhal; 16.10.2013

Неудивительно, что в ответах на этот вопрос в комментариях высказывались разные мнения о том, сколько столбцов должно отображаться при выполнении perldoc, поскольку библиотека perldoc «ToMan» определяет несколько способов определения длины строки в зависимости от вашей среды.

В частности, он будет использовать переменную окружения MANWIDTH, или выполнить «stty -a», или, наконец, по умолчанию использовать 73 символа — именно в таком порядке.

О, но подождите, эта стратегия для определения длины строки? Это только для некоторых перестановок Linux. Другие платформы напрямую зависят от nroff для определения того, сколько столбцов следует использовать.

На этих других платформах могут быть реализации, которые правильно определяют длину строки через nroff (как предлагает Quixadhal), или они могут иметь где-то жестко закодированное значение (как предполагает Дэвид В.).

К сожалению, похоже, что на этот конкретный вопрос нет универсального ответа - по крайней мере, исходя из моего собственного опыта сравнения FreeBSD и Linux.

Одна хитрость, которая может оказаться полезной: вы можете передавать аргументы непосредственно в nroff через perldoc. Итак, если ваш pod2usage происходит с псевдонимом perldoc (иногда работает, иногда нет?), то вы можете попробовать передать аргумент nroff, который, возможно, заставит это работать на вас:

pod2usage -n 'nroff -rLL=120n' sample.pm

Это будет работать только в том случае, если pod2usage вызывает perldoc, однако в некоторых случаях это совершенно отдельная реализация, которая не принимает аргумент -n.

person kamelkev    schedule 19.03.2015