четверг, 10 декабря 2009 г.

Постепенное применение стандартов кодирования с помощью Test::Perl::Critic::Progressive

Введение

Применение стандартов кодирования (Perl Best Practices, если быть более точным) к существующему коду - задача не из простых. По началу кажется, что для того чтобы код прошёл проверку Perl::Critic-ом необходимо переписать практически всё. Однако в большинстве случаев такой подход является либо нерациональным, либо просто невозможным. Именно для таких ситуаций Jeffrey Thalhammer и разработал модуль Test::Perl::Critic::Progressive. Как следует из названия - это тестовый модуль. Он прогоняет код через Perl::Critic практически так же как это делает модуль Test::Perl::Critic, но с одним исключением: Test::Perl::Critic::Progressive не ругается на уже существующие нарушения в коде, но в то же время препятствует появлению новых. Кроме того, замена одних нарушений другими также пресекается. Это как раз то, что нужно для поддержки уже существующих программных проектов.

Принцип работы модуля достаточно прост: во время первого запуска тестов Test::Perl::Critic::Progressive сканирует код и сохраняет все обнаруженные в нём нарушения в так-называемом history-файле. Фактически, это просто текстовый файл, в котором записано какие нарушения и в каком количестве были обнаружены. Во время последующих запусков Test::Perl::Critic::Progressive сверяет данные об обнаруженных нарушениях с тем, что сохранено в history-файле: если нарушений хотя бы одного типа стало больше - то тест будет провален.

Пример

Допустим имеется программный проект следующей структуры:
legacy-project/
|-- lib
| `-- SomeModule.pm
`-- t
|-- perl-critic-progressive.t
`-- perlcritic-profile

2 directories, 3 files

SomeModule.pm:
package SomeModule;

#===============================================================================
# REVISION: $Id:$
# AUTHORS: Alexander Simakov, <xdr (dot) box (at) Google Mail>
# CREATED: 2009-12-10 20:48:19
# DESCRIPTION: This module worked for us for many years!
#===============================================================================

use strict;

# Oops! we don't use warnings
# Oops! we have no our $VERSION!

sub do_something($$) { # Oops! We use prototypes
my $name = shift;

# Oops! We use "" for literal string instead of ''
my $result = "Hello " . $name;

return $result;
}

1;


В этом модуле, по мнению Perl::Critic-а, имеется ряд нарушений. Для удобства все они отмечены комментариями "Oops!". Предположим, что это и есть те самые застарелые проблемы (которых в реальной ситуации будет конечно больше), которые в настоящий момент устранять не хочется.

perl-critic-progressive.t:
#!/usr/bin/perl

#===============================================================================
# REVISION: $Id:$
# AUTHORS: Alexander Simakov, <xdr (dot) box (at) Google Mail>
# CREATED: 2009-12-10 20:59:28
# DESCRIPTION: Test everything in lib/ using Test::Perl::Critic::Progressive
#===============================================================================

use strict;
use warnings;

use Test::Perl::Critic::Progressive qw(:all);

# For building relative paths
use FindBin qw($Bin);
FindBin::again();

use Readonly; # Instead of "use constant" pragma

Readonly my $ROOT => "$Bin/../lib";
Readonly my $PROFILE => "$Bin/perlcritic-profile";
Readonly my $HISTORY => "$Bin/perlcritic-history";

{
# Set file for storing history data
set_history_file($HISTORY);

# Setup Perl::Critic parameters
set_critic_args( -profile => $PROFILE );

# Critique files starting from $ROOT
progressive_critic_ok($ROOT);
}


Этот тест прогоняет весь Perl-код начиная с указанной директории через Perl::Critic. Конфигурация самого Perl::Critic-а задается в отдельном профиле (см. ниже). Также следует обратить внимание на history-файл, о котором шла речь выше. Если файла ещё нет, то он будет создан при первом запуске. Наличие же данного файла является сигналом Test::Perl::Critic::Progressive-у заступить на охрану вашего кода от нарушений.

perlcritic-profile:
severity = brutal
verbose = 8
profile-strictness = fatal

Итак, всё готово для запуска тестов:
prove t/

t/perl-critic-progressive....ok
All tests successful.
Files=1, Tests=1, 2 wallclock secs ( 1.85 cusr + 0.09 csys = 1.94 CPU)

Как видите, тест прошёл успешно, в время как perlcritic высказал бы своё недовольство по поводу данного кода:

perlcritic -profile t/perlcritic-profile lib/SomeModule.pm

[Modules::RequireVersionVar] No "VERSION" variable found at line 1, column 1. (Severity: 2)
[Subroutines::ProhibitSubroutinePrototypes] Subroutine prototypes used at line 15, column 1. (Severity: 5)
[TestingAndDebugging::RequireUseWarnings] Code before warnings are enabled at line 15, column 1. (Severity: 4)
[ValuesAndExpressions::ProhibitInterpolationOfLiterals] Useless interpolation of literal string at line 19, column 18. (Severity: 1)

Вот как выглядит history-файл (для наглядности большая часть нулевых счётчиков не показа):
$VAR1 = [
{
'Perl::Critic::Policy::ValuesAndExpressions::RequireUpperCaseHeredocTerminator' => 0,
'Perl::Critic::Policy::References::ProhibitDoubleSigils' => 0,
...
'Perl::Critic::Policy::Modules::RequireVersionVar' => 1,
'Perl::Critic::Policy::TestingAndDebugging::RequireUseWarnings' => 1,
...
'Perl::Critic::Policy::Subroutines::ProhibitSubroutinePrototypes' => 1,
...
'Perl::Critic::Policy::ValuesAndExpressions::ProhibitInterpolationOfLiterals' => 1,
...
'Perl::Critic::Policy::BuiltinFunctions::ProhibitVoidGrep' => 0,
'Perl::Critic::Policy::InputOutput::RequireBriefOpen' => 0
}
];

Теперь, предположим что файл SomeModule.pm претерпел изменения и теперь он выглядит следующим образом:

package SomeModule;

#===============================================================================
# REVISION: $Id:$
# AUTHORS: Alexander Simakov, <xdr (dot) box (at) Google Mail>
# CREATED: 2009-12-10 20:48:19
# DESCRIPTION: This module worked for us for many years!
#===============================================================================

use strict;
use warnings; # Yes! we use warnings now: -1 violation

# Oops! we have no our $VERSION!

sub do_something($$) { # Oops! We use prototypes
my $name = shift;

# Oops! We use "" for literal string instead of ''
my $result = "Hello " . $name;

return $result;
}

sub camelStyle($) { # Camel style, prototypes: +2 violations
my $x = shift;

return $x**2;
}

1;

Обратите внимание, что нарушений одного типа стало меньше, а других типов - больше. Но даже если бы общая сумма нарушений и уменьшилась, увеличение хотя бы одного из счётчиков не позволило бы тесту пройти успешно:

prove t/

t/perl-critic-progressive....# NamingConventions::ProhibitMixedCaseSubs: Got 1 violation(s). Expected no more than 0.
# Subroutines::ProhibitSubroutinePrototypes: Got 2 violation(s). Expected no more than 1.
# Too many Perl::Critic violations...
# Got a total of 5. Expected no more than 4.
t/perl-critic-progressive....NOK 1/1
# Failed test 'Test::Perl::Critic::Progressive'
# at t/perl-critic-progressive.t line 33.
# Looks like you failed 1 test of 1.
t/perl-critic-progressive....dubious
Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 1
Failed 1/1 tests, 0.00% okay
Failed Test Stat Wstat Total Fail List of Failed
-------------------------------------------------------------------------------
t/perl-critic-progressive.t 1 256 1 1 1
Failed 1/1 test scripts. 1/1 subtests failed.
Files=1, Tests=1, 3 wallclock secs ( 2.08 cusr + 0.09 csys = 2.17 CPU)
Failed 1/1 test programs. 1/1 subtests failed.


Недостатки

Как можно было понять из вывода команды prove и содержимого history-файла - Test::Perl::Critic::Progressive не запоминает в каких файлах и в каком месте были обнаружены нарушения. Фиксируется только факт нарушения и соответствующий счётчик. Если информация о файлах необходима, то соответствующую логику придётся реализовывать самостоятельно.

Ссылки

четверг, 3 декабря 2009 г.

Using Perl::Critic in Subversion hook-script

Overview

A few weeks ago I wrote subversion hook-script that checks Perl code using Perl::Critic module. The script is called perlcritic-checker.pl If the code has violations then the entire commit will be rejected. The appropriate error message (colored and sorted by severity level) will be sent back to the client. This way you can employ consistent coding rules in your team.

The script can be configured to apply different Perl::Critic policy profiles for different paths in the repository. For example, you can be very strict with brand new projects, make an indulgence for legacy projects and completely disable Perl::Critic for auto-generated and third-party code.

The code and configuration file examples can be downloaded from here.

Discussion thread on PerlMonks

Who is who

Damian Conway - the author of "Perl Best Practices". The book on which Perl::Critic is mostly based. Jeffrey Thalhammer - the author of Perl::Critic module.

See also

понедельник, 30 ноября 2009 г.

Object Oriented Perl

Object Oriented Perl
Damian Conway

Одна из ранних работ Конвея по методам ООП в языке Perl. Книга была издана в 2000 году и поэтому материал местами сильно устарел, тем не менее, книгу стоит почитать хотя бы для сравнения с одним из последующих трудов Конвея - Perl Best Practices. Интересно отметить, что в последствии автор пересмотрел ряд своих убеждений. К примеру, в Object Oriented Perl Конвей рекомендует псевдо-хеши как одну из альтернатив для создания инфраструктуры объектов. В Perl Best Practices он признает, что это было ошибкой. Кроме того, псевдо-хеши в последствии были объявлены как Deprecated, а начиная с Perl 5.9 вообще исключены из языка.

В последующих своих работах Конвей активно продвигает технологию известную как Inside-Out Objects. Она позволяет строить большие, надежные и масштабируемые иерархии объектов. Говоря про Inside-Out объекты, следует упомянуть про великолепный модуль Конвея Class::Std, который позволяет автоматизировать их создание и поддержку. В общем, всем поклонникам творчества Дамиана Конвея рекомендуется!

Оценка по шкале ИМХО: 4

Perl Best Practices

Perl Best Practices
Damian Conway

В процессе написания программного кода мы постоянно принимаем бесчисленное количество решений: как назвать переменную, выделить ли фрагмент кода в отдельную функцию, какой тип цикла использовать, как построить логическое условие, использовать ООП или нет. Дамиан Конвей утверждает, что чаще всего программисты принимают эти мини-решения либо по интуиции либо по привычке. Как правило, эти решения оказываются ошибочными и с ростом проекта и увеличением количества разработчиков (у каждого из которых, разумеется, свои привычки программирования) это дает о себе знать. В конечном итоге множество мелких шероховатостей и небрежностей нарастает как снежный ком превращая программный продукт в неповоротливую аморфную массу. Для языка Perl, который славится своей возможностью делать одно и то же различными способами, это имеет особое значение. Вспомните лозунг Perl: TIMTOWTDI - There Is More Than One Way To Do It.

Дамиан Конвей пользуется заслуженным авторитетом в сообществе Perl. На базе своего богатого опыта он собрал лучше практики программирования на Perl и оформил их в виде 256 четких рекомендаций: как нужно делать и как делать не нужно. Каждый совет в обязательном порядке демонстрируется на примере и анти-примере. Также автор приводит веские аргументы в пользу каждого из своих доводов.

Название книги, однако, несколько обманчиво. Может показаться, что на 400-х страницах будут обсуждаться только лишь советы по именованию переменных и выбору стиля форматирования кода. Это совершенно не так! В этой книге сосредоточен целый пласт знаний и опыта профессиональных Perl-программистов. Рекомендую эту книгу каждому кто изучает Perl всерьез.

Оценка по шкале ИМХО: 5

суббота, 3 октября 2009 г.

Создание кросс-платформенных графических интерфейсов на wxPerl

Введение

wxPerl - это Perl-интерфейс к C++ библиотеке wxWidgets. wxWidgets, в свою очередь, является OpenSource тулкитом для постоения кросс-платформенных графических интерфейсов. Лейтмотив wxWidgets - "Native Look and Feel". Достигается это за счет использования стандартных виджетов той платформы, на которой в данный момент работает программа. К примеру, wx-приложение под Windows будет выглядеть так же как и остальные Windows-приложения, а в Mac OS X - как другие приложения Mac OS X. К слову, для некоторых платформ существует сразу несколько портов wxWidgets. Так в Linux, wxWidgets может работать "поверх" GTK+, X11 или Motif. Первый вариант, однако, наиболее распространен. Не смотря на то, что wxWidgets является надстройкой над другими графическими тулкитами, разработчики уверяют, что накладные расходы будут минимальны.

Ещё одна примечательная черта wxWidgets - удобный механизм для создания специализированных виджетов на базе существующих. Сам механизм чрезвычайно прост: достаточно отнаследоваться от базового класса и добавить к нему необходимый функционал. Разумеется, подобные возможности предоставляют и другие тулкиты, такие как Tk или GTK+, но в wxWidgets это делается проще чем в Tk и документировано лучше чем в GTK+. К слову, widget subclassing является стандартной идиомой при программировании на wxWidgets.

Обзор wxWidgets

Для того чтобы лучше понять wxPerl полезно представлять себе окружающий его контекст, прежде всего, проект wxWidgets. Первоначальным автором wxWidgets является Джулиан Смарт (Julian Smart). Джулиан начал работать над wxWidgets (раньше wxWidgets назывался wxWindows) в 1992 году, будучи студентом Artificial Intelligence Applications Institute в Эдинбурге. Изначально, целью его работы было создание специализированного CASE-инструмента, который, по задумке, должен был работать как в Windows так и в UNIX-системах. Спустя некоторое время, к проекту начали присоединяться и другие энтузиасты, которые, общими усилиями и превратили wxWidgets в то, чем он является сегодня. В настоящий момент над wxWidgets трудится целая команда программистов, включая и Джулиана. Примечательно, что три наиболее активных разработчика руководят собственными консалтинговыми компаниями, специализирующимися на wxWidgets.

Из приятных вещей стоит отметить наличие хорошей документации, предкомпилированных версий wxWidgets для разных платформ, а также наличие соответствующих RPM и DEB пакетов в большинстве Linux-дистрибутивов. Есть и достаточно популярные приложения, написанные на wxWidgets: например, аудио редактор Audacity и медиа-плеер VLC. Подборку скриншотов различных wx-приложений можно посмотреть тут.

Обзор wxPerl

Если вы программируете на C++, то можете "разговаривать" с wxWidgets без переводчика: этот язык является для него родным. В противном случае придется воспользоваться библиотеками-обёртками: для языка Perl - это wxPerl. Автором проекта wxPerl является Маттиа Бэрбон (Mattia Barbon). Проект стартовал в 2001 году и продолжает развиваться. Так последний релиз wxPerl (0.93) датируется 24 сентября 2009. С предкомпилированными версиями wxPerl не всё так гладко: иногда они отстают от последней версии wxPerl, особенно это относится к Windows. В самых свежих Linux- и BSD-дистрибутивах ситуация несколько лучше: так пакет с wxPerl версии 0.93 есть в Mandriva 2010, в портах OpenBSD, в Debian unstable (sid). В любом случае, собрать wxPerl в UNIX намного проще чем в Windows. Более подробно об установке wxPerl читайте в следующих разделах.

Из неприятных моментов следует отметить документацию по wxPerl, а точнее её отсутствие. На сайте wxPerl рекомендуется использовать документацию по wxWidgets и мысленно транслировать её на Perl, руководствуясь рядом правил. Также имеется несколько tutorial-ов и wxPerl Wiki. Последняя встречает посетителей следующим сообщением:

The previous wxPerl wiki was not only spammed, but someone managed to entirely erase everything on disk that had to do with the kwiki-wiki. It's now reinstalled and recovered as far as possible.

So...

please feel free to, create an account and start adding information!

Cheers.

Страница со скриншотами wxPerl также давно не обновлялась: на ней представлен wxPerl версий от 0.14 (апрель 2003) до 0.21 (декабрь 2004). Ну и, наконец, обидно, что по wxPerl нет книги, хотя по тому-же wxPython книга есть.

Установка в Linux

Если вам повезло и в вашем дистрибутиве оказался соответствующий пакет, то можете смело пропустить этот раздел. Если нет, то придется собрать wxPerl вручную из CPAN. Для начала необходимо установить саму библиотеку wxWidgets с заголовочными файлами. К примеру, в Mandriva Linux 2008 необходимые пакеты называются libwxgtku2.8 и libwxgtku2.8-devel соответственно. Обратите внимание на букву "u" в названиях пакетов. Она означает, что wxWidgets скомпилирован с поддержкой Unicode. После того как библиотеки поставлены, установите следующие Perl-модули: Wx, Alien::wxWidgets и ExtUtils::XSpp.

Установка в Windows

Прежде чем приступить к установке wxPerl, необходимо скачать и проинсталлировать библиотеку wxWidgets. Почти наверняка этот шаг не вызовет никаких сложностей. Далее следует установить wxPerl. Если вы пользуетесь дистрибутивом ActivePerl, то проще всего подключить PPM-репозиторий, который поддерживает Марк Дутсон (Mark Dootson), и установить модуль Wx отдуда. Однако, необходимо иметь в виду две вещи: во-первых, в репозитории может быть не самая последняя версия Wx (на момент написания этих строк - 0.89.1), а во-вторых, необходимо установить правильную версию пакета Alien-wxWidgets, от которого зависит Wx. На момент написания этих строк в репозитории находятся две версии Alien-wxWidgets: 0.39 и 0.44 По умолчанию, разрешая зависимости программа PPM выберет самую свежую версию - 0.44. Проблема в том, Alien-wxWidgets версии 0.44 не будет работать с Wx версии 0.89.1! Для того чтобы обойти эту неприятность следует явно установить Alien-wxWidgets версии 0.39, а затем уже установить Wx. На выяснение этого "очевидного" факта ушло более часа поисков. Впрочем, возможно на момент прочтения этих строк данная проблема будет уже устранена.



Минимальное приложение на wxPerl

Теперь, когда wxPerl установлен, можно опробовать его в действии. Тестовое приложение состоит из двух файлов (файлы должны находиться в одной директории).

wx-minimal.pl:
#!/usr/bin/perl

#
# Александр Симаков, <xdr (тчк) box на Google Mail>
# http://alexander-simakov.blogspot.com/
#
# Простейшая программа использующая wxPerl
#

use strict;
use warnings;

use FindBin;
use lib "$FindBin::Bin";

use Wx;
use MinimalApp;

sub main() {
# Создаём экземпляр приложения и ...
my $minimal_app = MinimalApp->new();
# ... запускаем цикл обработки событий.
$minimal_app->MainLoop();
}

main();

MinimalApp.pm:
package MinimalApp;

#
# Александр Симаков, <xdr (тчк) box на Google Mail>
# http://alexander-simakov.blogspot.com/
#
# Минимальный класс приложения WxPerl
#

use strict;
use warnings;

use utf8;
use encoding 'utf8';

use Wx;
use base qw{ Wx::App };

sub OnInit {
# Создаём окно
my $frame = Wx::Frame->new(
undef, # Родительское окно
-1, # ID окна (-1 значит сгенерировать автоматически)
'Минимальное приложение на wxPerl', # Заголовок окна
[ -1, -1 ], # Позиция окна (значение по умолчанию)
[ 450, 100 ], # Размер окна
);

# Отображаем его на экране
$frame->Show( 1 );
}

1;

Вот как выглядит результат запуска этой программы:

Вид в Linux


Вид в Windows

Wx::Demo

Для изучения wxPerl очень полезно ознакомится с подборкой демонстрационных программ. Все программы объединены под одним интерфейсом: сразу можно посмотреть как на внешний вид, так и на исходный текст приложения. Соответствующий модуль называется Wx::Demo. Пользователи Windows могут установить этот модуль из упомянутого выше PPM-репозитория. На этот раз никаких сюрпризов нет. Запускной файл называется wxperl_demo.pl

Wx::Demo даёт хорошее представление о возможностях wxPerl, но также обнажает и некоторые недоработки. Сравните виджет wxComboCtrl в Linux и в Windows:

Вид в Linux


Вид в Windows

А вот ещё пара примеров: при масштабировании окна виджеты "наезжают" друг на друга:

Вид в Linux


Вид в Windows

В целом, идея использования виджетов хост-системы очень логична. Однако, встречаются виджеты, специфичные для какой-то конкретной платформы. К таким виджетам, например, относится GtkExpander. Обратите внимание как выглядит этот виджет в Linux (напомню, в Linux wxWidgets базируется на GTK+) и в Windows:

Вид в Linux


Вид в Windows

Выводы

В целом, wxPerl достаточно интересный и перспективный проект. Вместе с тем, стоит признать, что пока уровень поддержки тулкита wx в языке Perl существенно скромнее чем, скажем, в Python или C++. Таким образом, остановив свой выбор на wxPerl будьте готовы приложить дополнительные усилия на установку и настройку своего окружения, а также на поиски документации и дргугих справочных материалов.

Ссылки
Для интереса, вот ссылки на биндинги тулкита wx для других языков программирования:
Статья опубликована на портале CITForum

суббота, 19 сентября 2009 г.

Создание кросс-платформенных графических интерфейсов на Perl/Tk

Введение

История графического тулкита Tk неразрывно связана с языком программирования Tcl и его создателем Джоном Остераутом (John Ousterhout). Первая версия Tcl вышла в свет в далёком 1987 году. Четыре года спустя Остераут представил и первый релиз графического тулкита Tk.

Поддержка тулкита Tk в языке Perl, пусть и в виде альфа версии, появилась уже в 1994 году - гораздо раньше чем GTK+, Qt или WxWidgets. Автором этого проекта был Малкольм Бетти (Malcolm Beattie) из Оксфорда. Чуть позже к нему присоединился Ник Инг-Симмонс (Nick Ing-Simmons) из Texas Instruments, который в последствии и продолжил разработку Perl/Tk.

На сегодняшний день существует несколько модулей для работы с Tk (да, TIMTOWTDI в Perl ещё никто не отменял). Основное отличие между ними заключается в способе взаимодействия с библиотекой Tk: или через XS-интерфейс, или через язык Tcl. Первый подход более трудоёмок для разработки и поддержки, но обладает большей производительностью, кроме того, он является стандартной идиомой для языка Perl. Второй подход представляет из себя тонкую прослойку, транслирующую Perl-код для работы с интерфейсом в команды Tcl/Tk, которые затем передаются на исполнение интерпретатору Tcl.

Оба подхода имеют как плюсы так и минусы. Модуль, о котором говорилось в начале статьи относится к первой категории. Чуть позже мы также рассмотрим наиболее популярный модуль из второй категории.

Установка в Linux/BSD


В UNIX-системах проблем с установкой Perl/Tk возникнуть не должно. К примеру, в Mandriva Linux соответствующий пакет называется perl-Tk, а в OpenBSD - p5-Tk. Корректность установки можно проверить простым однострочником:
perl -MTk -e "MainWindow->new(); MainLoop();"
Если всё установлено правильно, то на экране должно появиться пустое окошко:


Установка в Windows

Под Windows Perl/Tk также устанавливается без проблем: ActiveState предоставляет PPM пакет Tk для своего дистрибутива ActivePerl. Проверить корректность установки можно тем же самым однострочником. Вот как выглядит результат его запуска под Windows:


Более сложный пример на Perl/Tk


Для того чтобы продемонстрировать некоторые особенности Perl/Tk напишем чуть более сложное приложение с несколькими стандартными элементами пользовательского интерфейса:

#!/usr/bin/perl

#
# Александр Симаков, <xdr (тчк) box на Google Mail>
# http://alexander-simakov.blogspot.com/
#
# Демонстрационная программа на Perl/Tk
#

use strict;
use warnings;

use Tk;

sub main() {
# Создаём главное окно
my $mw = MainWindow->new();

# Фрейм для группировки Radiobutton-ов
my $rb_frame = $mw->Frame()->pack( -side => "top" );

# В этой переменной будет сохраняться -value
# выбранного Radiobutton-а. При изменении значения
# $rb_variable извне, интерфейс будет обновлён
# автоматически.
my $rb_variable = "foo";

foreach my $name ( qw{ foo bar baz } ) {
my $rb = $rb_frame->Radiobutton(
-text => "Radiobutton $name",
-value => $name,
-variable => \$rb_variable,
);
$rb->pack( -side => "left" );
}

# Создаём Checkbutton. Его состояние сохраняется
# в переменной $cb_variable: при её изменении
# извне изменится и внешний вид Checkbutton-а.
my $cb_variable = "on";
my $cb = $mw->Checkbutton(
-text => "Checkbutton foobar",
-onvalue => "on",
-offvalue => "off",
-variable => \$cb_variable,
);
$cb->pack( -side => "top" );

# Создаём кнопку с обработчиком. При нажатии
# будет выведен выбранный Radiobutton и текущее
# состояние Checkbutton-а
my $b = $mw->Button(
-text => "Show status",
-command => sub {
print "Selected Radiobutton: '$rb_variable'\n";
print "Checkbutton state: '$cb_variable'\n";
}
);
$b->pack( -side => "top" );

# Запускаем главный цикл обработки событий
MainLoop();
}

main();

Вид в Linux


Вид в Windows

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

Также стоит отметить, что модуль Perl/Tk базируется на библиотеке Tk версии 8.4. Интерфейс, написанный на Tk 8.5, выглядит иначе: как именно - смотрите в следующем разделе.

Модуль Tkx от ActiveState

Модуль Tkx, разрабатываемый при поддержке ActiveState, реализует своеобразный "мост" между Perl и Tcl. В результате, код для работы с интерфейсом транслируется в команды Tcl/Tk. Плюс такого подхода в том, что можно всегда использовать самую последнюю версию Tk без необходимости существенной переработки модуля. Минус - меньшая производительность по сравнению с XS-версией.

Стоит отметить, что последние версии ActivePerl поставляются с предустановленным модулем Tkx. Это объясняется тем, что Tkx активно используется в программах, входящих в состав PDK (ActiveState Perl Dev Kit). В частности, Tkx используется графической версией менеджера пакетов PPM: для того чтобы посмотреть его в действии достаточно набрать команду ppm. Проверить работоспособность Tkx можно и следующим однострочником:
perl -MTkx -e "Tkx::MainLoop();"
Теперь перепишем предыдущий пример с использованием модуля Tkx и сравним результаты:

#!/usr/bin/perl

#
# Александр Симаков, <xdr (тчк) box на Google Mail>
# http://alexander-simakov.blogspot.com/
#
# Демонстрационная программа на Perl/Tkx
#

use strict;
use warnings;

use Tkx;

sub main() {
# Создаём главное окно
my $mw = Tkx::widget->new( "." );

# Фрейм для группировки Radiobutton-ов
my $rb_frame = $mw->new_frame();
$rb_frame->g_pack( -side => "top" );

# В этой переменной будет сохраняться -value
# выбранного Radiobutton-а. При изменении значения
# $rb_variable извне, интерфейс будет обновлён
# автоматически.
my $rb_variable = "foo";
foreach my $name ( qw{ foo bar baz } ) {
my $rb = $rb_frame->new_radiobutton(
-text => "Radiobutton $name",
-value => $name,
-variable => \$rb_variable,
);
$rb->g_pack( -side => "left" );
}

# Создаём Checkbutton. Его состояние сохраняется
# в переменной $cb_variable: при её изменении
# извне изменится и внешний вид Checkbutton-а.
my $cb_variable = "on";
my $cb = $mw->new_checkbutton(
-text => "Checkbutton foobar",
-onvalue => "on",
-offvalue => "off",
-variable => \$cb_variable,
);
$cb->g_pack( -side => "top" );

# Создаём кнопку с обработчиком. При нажатии
# будет выведен выбранный Radiobutton и текущее
# состояние Checkbutton-а
my $b = $mw->new_button(
-text => "Show status",
-command => sub {
print "Selected Radiobutton: '$rb_variable'\n";
print "Checkbutton state: '$cb_variable'\n";
}
);
$b->g_pack( -side => "top" );

# Запускаем главный цикл обработки событий
Tkx::MainLoop();
}

main();

Изменения в коде не значительные, чего не скажешь о внешнем виде:

Вид в Linux


Вид в Windows

Под Windows на таком простом примере различия не видны, но в более сложных приложениях они также будут заметны.

Не смотря на то, что у Tkx открытая лицензиия, широкого распространения в UNIX-системах он пока не получил. Если в вашей UNIX-системе нет соответствующего пакета, потребуется установить по крайней мере два модуля из CPAN: Tkx и Tcl.

В завершение стоит упомянуть про модуль Tcl::Tk, который также работает по принципу "моста".

Выводы

Итак, мы рассмотрели два Perl-модуля для работы с тулкитом Tk: одноименный модуль Tk и Tkx. К плюсам модуля Tk можно отнести его распространенность и хорошую переносимость. К минусам - некоторую архаичность внешнего вида и привязку к устаревшей версии тулкита Tk. В актив Tkx можно записать современный внешний вид и значительно меньшую зависимость от версии тулкита Tk. С другой стороны, "из коробки" Tkx работает только в ActivePerl, хотя возможно в будущем ситуация и изменится. Также следует учитывать, что добавление ещё одного промежуточного слоя - Tcl - негативно сказывается скорости работы и усложняет и без того не тривиальный API.

В целом, тулкит Tk обладает очень гибким API, но в силу своей оригинальности потребуется какое-то время чтобы к нему привыкнуть. В особенности это относится к фреймворку MegaWidgets для создания собственных виджетов на базе существующих. К странностям почти всех модулей для работы с Tk можно отнести невнятный и непоследовательный способ именования виджетов, методов и классов. Также стоит подчеркнуть обилие качественной документации как по Tcl/Tk так и по соответствующим Perl-модулям. К слову, Tk - единственный графический тулкит, чей Perl-интерфейс удостоился отдельной книги: Mastering Perl/Tk.

Ссылки
Статья опубликована на портале CITForum

пятница, 21 августа 2009 г.

sudo и регулярные выражения

Как известно, sudo(1) позволяет администратору разрешить некоторым пользователям запускать определенные команды от имени root-а или другого пользователя. Если файл /etc/sudoers - сетевой, то можно дополнительно ограничить и список хостов, с которых разрешено выполнение той или иной команды. Всё это описано в руководстве.

Для большей гибкости, в sudo предусмотрена поддержка метасимволов * и ?, а также диапазонов [...] и [!...]. Глядя на них может возникнуть желание написать что-то вроде
jonh_smith  ALL=/bin/rm /only/below/this/dir/*
или
jonh_smith    ALL=/bin/mkdir /only/below/this/dir/[a-zA-Z0-9_-]*
даже не взирая на то в руководстве sudoers(4) четко написано:
sudo allows shell-style wildcards ... Note that these are not regular expressions.
Таким образом, sudo не будет возражать против команды
sudo rm /only/below/this/dir/../../other/dir/*
поскольку формально звёздочка "съест" все символы включая / и .. Также у sudo не возникнет вопросов по поводу пары команд
sudo mkdir /only/below/this/dir/a
sudo mkdir /only/below/this/dir/a/../../../another@dir
т.к. фактически на принадлежность к диапазону проверяется только первый символ.

Если регулярные выражения всё же нужны, можно воспользоваться известным приёмом: написать скрипт-обёртку, вызывамый через sudo с произвольными аргументами, который уже сам средствами языка программирования выполнит сколь угодно сложные проверки и только затем запустит потенциально опасную команду. Вот пример на Perl:

Содержимое /etc/sudoers:
jonh_smith  ALL=/usr/local/bin/safe_rm.pl
Исходный код /usr/local/bin/safe_rm.pl:
#!/usr/bin/perl

use strict;
use warnings;

sub main() {
foreach my $arg ( @ARGV ) {
if ( $arg !~ qr{^/only/below/this/dir/[a-zA-Z0-9_-]+$} ) {
die "Unsafe path: '$arg'\n";
}
}

if ( @ARGV > 0 ) {
return system( "/bin/rm", @ARGV );
} else {
die "Empty path list\n";
}
}

main();

среда, 19 августа 2009 г.

Аналог конструкции "grep -v grep"

Для определения того, запущен ли процесс часто используют команду такого вида:
ps aux | grep foo | grep -v grep
Второй grep при этом убирает из списка процессов саму команду grep foo, которая иногда успевает в него просочиться. Сэкономить один grep можно при помощи такой конструкции:
ps aux | grep [f]oo
Смысл в том, что регулярное выражение [f]oo совпадет со строкой foo в списке процессов, но не совпадет со строкой grep [f]oo, которая может появиться в этом списке. Кроме того, на мой взгляд, вариант без grep -v grep выглядит опрятнее.

Если интересует только PID процесса, то удобнее использовать pgrep(1) или pidof(8). Если вдруг этих программ под рукой не окажется, то подойдет такой вариант:
ps aux | grep [f]oo | awk -F' ' '{ print $2 }'

среда, 12 августа 2009 г.

Создание кросс-платформенных графических интерфейсов на Perl/GTK+

Введение

В этой статье приводится обзор библиотеки GTK+ и её интерфейса к языку Perl - Gtk2-Perl. Поскольку GUI-приложения написанные на Gtk2-Perl довольно большая редкость, собирать информацию приходится по крупицам. Цель статьи - постараться дать объективную оценку связке Perl/GTK+: отметить сильные и слабые стороны, предупредить о возможных сложностях и проблемах.

Библиотека GTK+

Изначально библиотека GTK+ была разработана Питером Матисом (Peter Mattis) и Спенсером Кимбелом (Spencer Kimball) для нужд графического редактора GIMP (GNU Image Manipulation Program), но потом была выделена в отдельный проект. Сама библиотека GTK+ написана на C, но существуют языковые привязки (language bindings) и для многих других языков программирования, в их числе и Perl.

Судя по графику релизов и количеству баг-фиксов проект GTK+ развивается достаточно активными темпами. За время существования проекта вокруг него сформировалось внушительное сообщество, что также является положительным моментом. В настоящее время команда ключевых разработчиков состоит из десяти человек, представляющих такие компании как Red Hat, Novell и Intel Open Source Technology Center.

Следует отметить хорошую поддержку механизмов интернационализации и локализации: обратите внимание на использование кодировки UTF-8 в примере.

Документация по библиотекам, входящим в состав GTK+, поддерживается в актуальном состоянии, однако тут есть один нюанс. Разработчики языковых привязок зачастую считают документацию по своим интерфейсам "производной" от документации по оригинальным библиотекам GTK+ и поэтому не всегда поддерживают её в актуальном состоянии. Именно такая ситуация наблюдается с Gtk2-Perl.

Установка в Linux/BSD

Поскольку GTK+ родом из страны UNIX, проблем с её установкой в UNIX-подобных системах обычно не возникает. Вполне может быть, что все необходимые библиотеки и модули у вас уже установлены. Если нет, то наверняка поставщик вашего дистрибутива подготовил пакет со всем необходимым. К примеру, в Mandriva Linux соответствующий пакет называется perl-Gtk2, а в OpenBSD - p5-Gtk2.

Установка в Windows


В Windows всё несколько сложнее. Поскольку Windows-машина с установленным компилятором языка C и необходимым для сборки окружением скорее исключение чем правило, надеяться приходится только на то, что кто-то заботливо скомпилировал для нас все необходимые библиотеки и языковые привязки к ним. Самое интересное, что на момент написания этих строк в ActivePerl, самом популярном Perl-дистрибутиве для Windows, такого пакета нет! Компиляция этого пакета со всеми зависимостями под Windows - нетривиальная и достаточно хлопотная процедура. На сайте Gtk-perl в разделе Win32 Support даются ссылки на альтернативные PPM-репозитории для ActivePerl, однако это тоже не помогло. Что же делать? Обратите внимание на проект Camelbox. Это Perl-дистрибутив под Windows, в состав которого уже включена поддержка самой последней версии GTK+. Если вы хотите использовать ActivePerl, придется приложить дополнительные усилия.

Минимальное приложение


Продемонстрируем работу Gtk2-Perl на простом примере: окне с кнопкой. Этот пример дает первоначальное представление об API библиотеки и служит своего рода тестом: если программа запустилась, значит все необходимые библиотеки установлены и работают правильно.

Приведенный пример запускается без модификаций как минимум на трёх платформах: Linux, OpenBSD и Windows. Как правило и с более сложными приложениями проблем не возникает. Итак, исходный код:

#!/usr/bin/perl

#
# Александр Симаков, <xdr (тчк) box на Google Mail>
# http://alexander-simakov.blogspot.com/
#
# Простейшая программа использующая Gtk2-Perl
#

use strict;
use warnings;

# Включаем поддержку Unicode
use utf8;
use encoding 'utf8';

# Загрузка и инициализация библиотеки Gkt2. Инициализация
# библиотеки (параметр -init) должна выполняться ровно
# один раз.
use Gtk2 -init;

sub main() {
# Создаем главное окно
my $window = Gtk2::Window->new('toplevel');

# Устанавливаем обработчик сигнала закрытия окна
$window->signal_connect( delete_event => sub { Gtk2->main_quit } );

# Создаем кнопку
my $button = Gtk2::Button->new('Тест');

# Устанавливаем обработчик на кнопку
$button->signal_connect( clicked => sub {
print("Тест Gtk2-Perl\n");
});

# Помещаем кнопку на окно
$window->add($button);

# Делаем окно, а также все дочерние виджеты видимыми
$window->show_all();

# Запускаем цикл обработки событий
Gtk2->main();
}

main();

Вид приложения в Linux:

Вид приложения в OpenBSD:


Вид приложения в Windows:

Выводы

В целом, приложения, написанные с использованием GTK+, выглядят довольно современно и красиво. Проект GTK+ имеет давнюю историю и продолжает развиваться. Также стоит отметить хорошую поддержку интернационализации и локализации.

Несколько расстраивают сложности с установкой библиотек и языковых привязок Gtk2-Perl под Windows. Также нужно учитывать, что в отличии от оригинальной документации по GTK+, документация по Gtk2-perl не всегда актуальна и точна.

Еще один пробел - это отсутствие добротной книги по Gtk2-Perl. Из за отсутствия книги, информацию приходится собирать по крупицам из самых разных источников: оригинальная документация по GTK+, различные tutorial-ы и HOW-TO, сообщения в форумах и т.д.

Ссылки
Статья опубликована на портале CITForum