Все задачи

Конфигурационные файлы

06 Nov 2013

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

use strict;
use warnings;

package Config;

use YAML;

sub load 
{
    my ($class, %param) = @_;

    my $conf;

    if ( $param{file} =~ /\.yaml$/ ){

        print $param{file}."\n";
        $conf->{data} = YAML::LoadFile( $param{file} );
        $conf->{format} = "yaml";
        $conf->{file} = $param{file};
    } else {
        die "unsupported format of config, stop";
    }

    return bless $conf;
}


sub write
{
    my ($self, %param) = @_;

    YAML::DumpFile($param{file}, $self->{data});

    return;
}


sub get
{
    my ($self, $key) = @_;

    return $self->{data}->{$key};
}

sub set
{
    my ($self, $key, $value) = @_;

    $self->{data}->{$key} = $value;

    return;
}

1;

Программист решил протестировать, что получается:

#!/usr/bin/perl

use strict;
use warnings;

use Config;

my $conf = Config->load(file => "conf.yaml");

my $parallel_level = $conf->get("parallel-level");
$conf->set("parallel-level", $parallel_level * 2);

$conf->write(file => "conf2.yaml");

Но скрипт не заработал. Совсем.

Почему?

Можно скачать исходный код: модуль Config.pm, скрипт script.pl, пример данных conf.yaml.

Подсказка

Показать

Например, на компьютере одного из авторов скрипт завершается с сообщением

Goto undefined subroutine &Config::load at /usr/lib/perl/5.14/Config_heavy.pl line 1361.

Подсказка-2

Показать

Впрочем, если переместить файл Config.pm в другой каталог и подключить его с помщью use lib, скрипт начинает работать.

Разоблачение

Показать

Дело в том, что в стандартной поставке perl с 1996 года присутствует модуль Config, предназначенный для чтения конфигурационных переменных perl’а.

Текущий каталог (.) находится в самом конце массива @INC, поэтому при загрузке модуля через use Config perl находит стандартный модуль раньше, чем модуль из текущего каталога.

Изменить порядок поиска модулей можно с помощью прагмы lib. Результатом use lib является добавление каталога в начало массива @INC, поэтому модули оттуда будут находиться раньше стандартных.

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

Примечания

Историю модуля Config в разных версиях perl см., например, здесь. Кстати, посмотрите и на остальные стандартные модули.

Смотрим порядок обхода каталогов при загрузке модулей:

> perl -le 'use lib "/tmp"; print join "\n", @INC'
/tmp
/etc/perl
/usr/local/lib/perl/5.14.2
/usr/local/share/perl/5.14.2
/usr/lib/perl5
/usr/share/perl5
/usr/lib/perl/5.14
/usr/share/perl/5.14
/usr/local/lib/site_perl
.

К слову, use lib "/some/dir" – это практически то же самое, что BEGIN { unshift(@INC, "/some/dir") }. О небольшой разнице см. perldoc lib.

Также признаемся, что на составление этой задачи нас вдохновил топик на StackOverflow: What is the error in this object oriented program?.