Perl умирает. В этом почти нет сомнений. Но так как он это делает уже много лет, отказываться от него преждевременно. В этой статье пойдёт речь о том, как немного облегчить себе жизнь при написании перловых программ.
Некоторое время назад перловое сообщество начало оправляться после диверсии perl6 и взялось за модернизацию perl5. Ниже то, что мне приглянулось из нововведений именно на практике. Под практикой я понимаю написание программ для перла, входящего в стандартный набор Debian Jessie (5.20), так как последние версии перла и малосовместимы со сторонним софтом типа Eclipse EPIC, и требуют ручной установки.
perl5i::2
perl5i::2 — набор модулей, которые меняют стандартное поведение и привносят новые возможности. Среди них отмена автовивификации (autovivification) — автоматического создания несуществующих ключей в хэше; добавление к обычным объектам новой функциональности — например
#!perl
$string->trim;
12->is_integer;
, utf8::all — включение всех utf8 возможностей (feature); добавление Try::Tiny; включение use strict, use warnings, given/when/say; превращение time() в объект DateTime, подключение Modern::Perl.
вивификация (vivification) — оживление, создание пустого хэша если его нет :)))
Превращение time() в объект DateTime
Потенциально опасная возможность, так как после этого time() становится несовместимым со старым кодом и производит некоторые трудноотлавливаемые ошибки. Я отключаю эту возможность:
#!perl
use perl5i::2 -skip => ['time'];
Так можно отключить другие фичи. Среди них есть некоторые сомнительные, такие как возможность определения параметров функций, которые несовместимы с синтаксическими анализаторами (EPIC, perltidy):
#!perl
func hello($place) {
say "Hello, $place!\n";
}
Правда, (Perl-Tidy-Sweetened)[https://github.com/mvgrimes/perl-tidy-sweetened] с ними может и справиться.
Try::Tiny — замена eval
Try::Tiny позволяет писать в стиле
#!perl
try {
$dbh->do("delete from all_tables->cascade");
$dbh->commit();
say("Вот и всё, ребята!");
}
catch {
say $_; # $_ - то что было в $@ (почти)
$dbh->rollback();
}
finally {
};
Есть более-менее приемлемое решение для return из try-catch.
Try::Tiny автоматически подключается при использовании perl5i:2
Log::Log4perl
Log::Log4perl приводит в порядок логи. Это копия Log4j из Java мира. Многие модули, для которых актуально создание логов, интегрированы с ним, например стандартная система логирования в Mojolicious может быть заменена на MojoX::Log::Log4perl. Наверно, самая мощная система конфигурирования логов.
К примеру, возможен такой трюк: при обработке нескольких объектов удобно логи для каждого объекта выводить в отдельный файл. Например так:
#!perl
sub logger_file_switch {
my $appender_name = shift;
my $id_or_file = shift;
my ($filename_old, $filename_new);
my $appender = Log::Log4perl->appender_by_name($appender_name);
if (UNIVERSAL::isa($appender, 'Log::Log4perl::Appender::File')) {
$filename_old = $filename_new = $appender->filename();
if ($id_or_file =~ /^\d+$/o) {
$filename_new =~ s{/[^/]*$}{/$id_or_file.log};
}
else{
$filename_new = $id_or_file; # это файл, на который переключиться
}
$appender->file_switch($filename_new);
}
else {
die("Appender с указанным именем '$appender_name' не найден");
}
return ($filename_old, $filename_new) if wantarray;
return [$filename_old, $filename_new];
}
my ($filename_old , $filename_new);
try {
# Переключаемся
($filename_old , $filename_new) = $self->logger_file_switch('any_appender', $id);
}
finally {
# Возвращаем обратно
$self->logger_file_switch('any_appender', $filename_old)
};
Try::Tiny и Log::Log4perl вместе
Try::Tiny маскирует точку возникновения ошибки. Некоторую компенсацию этого предлагает метод wrapper_register:
#!perl
Log::Log4perl->init_once(\$logger_conf);
Log::Log4perl->wrapper_register("Try::Tiny");
use feature ‘say’
say не делает ничего отличного от print, кроме добавления «\n» в конце строки.
say автоматически подключается при использовании perl5i:2
use feature ‘switch’
Разрешает использование конструкции given/when
#!perl
for ($var) {
$abc = 1 when /^abc/;
$def = 1 when /^def/;
default { $nothing = 1 }
}
switch автоматически подключается при использовании perl5i:2
DateTime
DateTime поддерживает объекты, хранящие дату и время, интервалы между временами, операции типа добавления/вычитания и так далее. Rose::DB::Object и DBIx::Class для работы с временными типами используют именно его. Позволяет работать с широким диапазоном дат (unix timestamp — даты от 1970 до 2037 ). Есть куча парсеров форматов дат, возвращающих объект DateTime, например DateTime::Format::Builder, позволяющий быстро написать свой парсер.
DateTime автоматически подключается при использовании perl5i:2, но, кроме этого, есть побочный эффект — Превращение time() в объект DateTime. Эффект отключаемый.
У DateTime есть одна очень неприятная особенность — он медленный. Time::Moment имеет меньшую функциональность, но на реальном тесте показывает в десятки раз большую производительность. На синтетических тестах он вообще неимоверно крут — 12743% от DateTime.
Mail::Box
Для работы с почтой есть огромное количество модулей. Однажды Mark Overmeer получил небольшой грант и довёл до ума Mail::Box. Почти довёл, так как грант кончился на IMAP, который остался недоделан (точнее, не учитывает всех многочисленных реализаций, так-то он более-менее рабочий).
LockFile::Simple — блокирование запуска второй копии программы
Есть разные способы и модули, но этот старый и проверенный. Кроме того, его автор знает об NFS, чего нельзя сказать об авторах некоторых других похожих модулей.
Типовое использование (есть куча других авриантов):
use LockFile::Simple;
my $lockformat = "lalala-%f.lock";
# _lock возвратит 1 если удастся создать блокировку ресурса с именем 'foo'
# и 0 если он уже заблокирован
sub _lock('foo') {
my $name = shift;
# для того, чтобы установить флаг -stale => 1 , создаём менеджер.
# -stale => 1 заставит удалять старые файлы блокировки,
# процессы для которых уже померли без разблокировки.
# -autoclean => 1 автоматически снимет блокировку при завершении программы.
# это полезно, так как -stale=>1 вызовет дополнительные затраты ресурсов.
my $lockmgr = LockFile::Simple->make(
-stale => 1,
-format => $lockformat,
-autoclean =>1,
);
# trylock() либо блокирует, либо сообщает что уже заблокировано.
# более сложный алоритм позволяет пытаться заблокировать какое-то время,
# производя несколько попыток.
if ($lockmgr->trylock($name)) {
warn "Lock $name Ok";
return 1;
}
warn "Lock $name failed"
return 0;
}
# _unlock('foo') снимет блокировку с ресурса 'foo'
# если не снять блокировку, то она автоматически будет снята при завершении процесса
# при указании -autoclean=>1 или при указании -stale=>1 будет снята при попытке заблокировать
# новым процессом тот же ресурс
sub _unlock {
my $name = shuft;
my $lockmgr = LockFile::Simple->make();
my $lockfile = $lockmgr->lockfile($name, $locktemplate);
$lockmgr->unlock($lockfile);
warn "Unlock $lockfile";
}
Perl-Tidy-Sweetened
Modern Perl плавно поменял свой синтаксис, а perltidy этого не заметил. Есть замена-дополнение Perl-Tidy-Sweetened, штатными для perltidy методами немного исправляюший ситуацию.
Leave a Reply
You must be logged in to post a comment.