Разделение данных конфигурации и логики сценария в сценариях Perl

Я нахожу следующий анти-шаблон, повторяющийся в моих Perl-скриптах: скрипт содержит некоторые настройки, специфичные для машины/настройки, которые я храню в скрипте как константы, в то время как остальная часть скрипта носит общий характер:

#!/usr/bin/perl

use strict;
use warnings;

# machine specific settings at the start of the script.
my $SETTING_1 = "foo";
my @SETTING_2 = ("123", "456");
my $SETTING_3 = "something";

# general part of script follows.
...

Этот шаблон несколько удобен при работе на одной машине, но как только я хочу распространить сценарий на несколько машин, начинаются проблемы, поскольку я должен отслеживать, чтобы не перезаписать часть настроек новыми обновлениями в общей части.

Правильное решение, очевидно, состоит в том, чтобы иметь один общий файл сценария и читать файл конфигурации, специфичный для среды, в которой выполняется сценарий.

Мой вопрос: Какой модуль CPAN вы бы порекомендовали для решения этой проблемы? Почему?


person knorv    schedule 04.03.2010    source источник


Ответы (8)


Мне больше всего нравится Config::Std. Мне нравится, как он обрабатывает многострочный и составные значения конфигурации.

Вы должны быть осторожны, когда переменная потенциально многозначна: если в файле конфигурации существует одно значение, оно будет храниться в скаляре; если существует несколько значений, вы получите ссылку на массив.

Я считаю удобным иметь два файла конфигурации: один для значений, описывающих операционную среду (где найти библиотеки и т. д.), а другой — для изменяемого пользователем поведения.

Я также люблю писать обертку вокруг него. Например (обновлено для включения автоматически созданных средств доступа только для чтения):

#!/usr/bin/perl

package My::Config;
use strict; use warnings;

use Config::Std;
use FindBin qw($Bin);
use File::Spec::Functions qw( catfile );

sub new {
    my $class = shift;
    my ($config_file) = @_;

    $config_file = catfile($Bin, 'config.ini');
    read_config $config_file => my %config;

    my $object = bless \%config => $class;

    $object->gen_accessors(
        single => {
            install => [ qw( root ) ],
        },
        multi => {
            template => [ qw( dir ) ],
        },
    );

    return $object;
}

sub gen_accessors {
    my $config = shift;
    my %args = @_;

    my $class = ref $config;

    {
        no strict 'refs';
        for my $section ( keys %{ $args{single} } ) {
            my @vars = @{ $args{single}->{$section} };
            for my $var ( @vars ) {
                *{ "${class}::${section}_${var}" } = sub {
                    $config->{$section}{$var};
                };
            }
        }

        for my $section ( keys %{ $args{multi} } ) {
            my @vars = @{ $args{multi}->{$section} };
            for my $var ( @vars ) {
                *{ "${class}::${section}_${var}" } = sub {
                    my $val = $config->{$section}{$var};
                    return [ $val ] unless 'ARRAY' eq ref $val;
                    return $val;
                }
            }
        }
    }

    return;
}

package main;

use strict; use warnings;

my $config = My::Config->new;

use Data::Dumper;
print Dumper($config->install_root, $config->template_dir);
C:\Temp> cat config.ini
[install]
root = c:\opt

[template]
dir = C:\opt\app\tmpl
dir = C:\opt\common\tmpl

Вывод:

C:\Temp> g.pl
$VAR1 = 'c:\\opt';
$VAR2 = [
          'C:\\opt\\app\\tmpl',
          'C:\\opt\\common\\tmpl'
        ];
person Sinan Ünür    schedule 04.03.2010
comment
+1 Очень полезно -- спасибо. Кажется, должен быть модуль, который бы генерировал для нас такие методы, как template_dirs. Я наткнулся на Config::General, особенно на вариант -ExtendedAccess. Ни разу не пользовался, но выглядит интересно. - person FMc; 04.03.2010
comment
@FM Я не знал о Config::General. Это выглядит интересно, но я считаю, что иногда общие решения могут быть слишком общими. Можно, конечно, автоматически генерировать методы доступа для скалярных опций и потенциально многозначных опций по отдельности. - person Sinan Ünür; 05.03.2010

Для файлов конфигурации я предпочитаю использовать YAML. Простая, кроссплатформенная, удобочитаемая, и нет опасности того, что ваша конфигурация случайно превратится в настоящую программу.

person friedo    schedule 04.03.2010
comment
Я предпочитаю YAML::Tiny, так как он легкий и написан на чистом Perl (при необходимости его легко связать). - person Michael Carman; 04.03.2010
comment
@mobrule: я бы назвал это редактируемым человеком. Тривиально изменить существующие значения. Немного сложнее создать файл YAML вручную с нуля. - person Michael Carman; 04.03.2010
comment
Re, yaml: проверьте yaml.org Макет главной страницы на самом деле в yaml. Это довольно аккуратно. :) - person Robert P; 05.03.2010
comment
YAML становится очень плохим для редактирования человеком, если ваши данные конфигурации структурированы (точка отсчета: Catalyst). Предоставление примеров YAML в документации POD также является проблемой, потому что POD дословно представляет блоки с отступами, а perldoc отображает их с отступами, а YAML чувствителен к отступам. - person hobbs; 05.03.2010

Библиотека Config:Properties удобна для чтения и записи. файлы свойств пары ключ/значение.

person john    schedule 04.03.2010

Я предпочитаю YAML и YAML::XS для данных конфигурации. Он простой, читаемый и имеет привязки практически для любого языка программирования. Другой популярный вариант — Config::General.

person Eugene Yarmash    schedule 04.03.2010

Обычный низкотехнологичный метод заключается в простом выполнении EXPR файла конфигурации. . Вы изучали это?

person ephemient    schedule 04.03.2010
comment
Это не обеспечивает никакой проверки ошибок и позволяет выполнять произвольный код. Никогда не оценивайте содержимое выражений или файлов, если вместо этого их содержимое можно просто считать в переменные. - person Ether; 04.03.2010
comment
@Ether Метод do является встроенным, простым и широко используемым. Многие крупные или популярные пакеты по-прежнему используют его в пользу более безопасных, но более сложных конфигурационных схем, например gna.org/projects. /savane и packages.debian.org/sbuild и Net::Config (входит в дистрибутив Perl). Это прекрасно, если вы доверяете пользователю, создающему файлы конфигурации. - person ephemient; 05.03.2010

Рискуя быть высмеянным из класса, одно из решений состоит в том, чтобы хранить конфигурацию в XML (или, для более авантюрного, в JSON). Доступный человеку, интероперабельный за пределами Perl, не должен жить на локальном ПК (и XML, и JSON могут быть запрошены из «URL-адреса конфигурации») и набора стандартных модулей (XML::Simple обычно достаточно хорош для XML-файлы конфигурации) существуют на CPAN.

person DVK    schedule 05.03.2010

Для такой простой конфигурации, особенно для тривиальных вещей, когда я не ожидаю, что эти данные изменятся в реальном мире, я часто просто использую YAML. Простота не может быть превзойдена:

Сначала напишите свою структуру данных Perl, содержащую вашу конфигурацию.

use YAML;

my $SETTINGS = {
    '1' => "foo",
    '2' => ["123", "456"],
    '3' => "something",
};

Затем передайте его в YAML::DumpFile();

YAML::DumpFile("~/.$appname.yaml", $SETTINGS);

Удалите структуру данных и замените ее на

my $SETTINGS = YAML::LoadFile("~/.$appname.yaml");

А потом забудьте об этом. Даже если вы не знаете или не хотите изучать синтаксис YAML, небольшие изменения в конфигурации можно внести вручную, а более серьезные — в Perl, а затем повторно выгрузить в YAML.

person sorpigal    schedule 05.03.2010