Воскресенье, делать нечего. Скучно... Решил я все таки заняться модулем для отсылки сообщений на пейджер. Ну, perl как всегда не подвел - задача оказалась проще пареной репы. И die переопределилась с пол-пинка. Одно достает - убогая реализация эникея в самом пейджере. По всему видать, придется мне заняться сегодня еще и ею, что бы не прослыть в миру мистером "кривые руки".
Итак, модуль оказался простеньким - всего одна функция. За-то какая! package pager; use strict; use Socket; my ($port,$host) = (65000,'localhost'); use Exporter(); @pager::ISA = qw/Exporter/; @pager::EXPORT_OK = qw/&die/; #*CORE::GLOBAL::die = \¨ sub die{ select STDERR; print $_[0] if defined $_[0]; my ($nm,$sock_name); if (defined($_[0]) && ($nm = gethostbyname($host)) && ($sock_name = sockaddr_in($port,$nm))){ socket CONN,PF_INET,SOCK_STREAM,getprotobyname('tcp'); if (connect CONN,$sock_name){ select CONN; $|=1; print "$0: $_[0]"; }else{ print "Couldn't' connect to $host: $!\n"; } close CONN; }else{ print "Couldn't convert $host into an Internet address: $!\n"; } exit 1; }
Модуль (я назвал его pager.pm) содержит всего одну функцию, которая и представляет собой перекрытие встроенной die. В основе кода функции лежит код клиента (да, да, того самого, которым мы тестировали пейджер). Однако, по сравнению с клиентом, здесь более жесткие требования к обработке ошибочных ситуаций. Но, обо всем по порядку.
По определению, функция die выводит сообщение об ошибке в поток STDERR, после чего завершает свою работу. Здесь то же самое, по этому, первым делом (что бы сократить код) мы выбираем STDERR в качестве дефолтового потока вывода. Теперь любые сообщения об ошибках внутри функции будут направляться в STDERR. И это самое правильное, что мы можем сделать в данной ситуации, ведь куда-то сообщать надо, даже если не работает пейджер. А так, возможно кто-то работает с нашим STDERR. В случае ошибки, возможно нам несказанно повезет и этот кто-то вовремя настучит кому надо.
Далее. Мы не отказываемся от вывода полученного сообщения в поток STDERR. Мы просто расширяем возможности функции die, поэтому следующий оператор печатает пришедшее сообщение в STDERR.
Сообщение уходит на пейджер только в случае если нам удалось соединиться с сервером (я думаю это понятно). Посему, следующим условным оператором проверяется успешность инициализации адреса назначения. Далее - по плану: создаем сокет и, если удалось соединиться с сервером, отправляем сообщение. После всех этих нехитрых манипуляций программа завершается с кодом 1.
Теперь, давайте обратим внимание на начальные директивы модуля. Со стриктом и сокетом должно быть все понятно. Далее, определяются две переменные модуля: номер порта и имя хвоста, на которых работает пейджер. Зачем мы объявляем их здесь? А для того, что бы избежать лишних действий при инициализации модуля. И потом, представьте, что когда мы напишем несколько программ, юзающих пейджер, в случае чего, нам придется изменять код каждой из них. А так, только pager.pm подправить и готово.
Мы наследуемся от класса Exporter, об этом свидетельствуют следующие операторы. Это стандартный модуль, который позволяет легко и надежно выполнять экспорт имен, а нам как раз это и нужно - ведь мы должны распространить действие die на родительскую область имен. Мы используем список @EXPORT_OK, для того, чтобы определить возможность экспорта функции die. Однако, это не приведет к автоматическому экспорту сразу при подключении модуля. Если бы мы вместо @EXPORT_OK использовали @EXPORT, то да - функция экспортировалась бы автоматически. Но, как убеждает нас старина Ларри в своей документации, это дурной тон - принуждать кого-то к чему-то.
Ну вот, в принципе, все и готово. Теперь, для того, чтобы все сообщения направленные в функцию die из родительского модуля направлялись на пейджер нужно, добавить в программу всего одну строчку use pager qw/die/;
Заметьте, что это не приведет к глобальному перекрытию. Если вы подключите еще какой нибудь модуль (и.г. будете работать в области в которую не экспортировалась переопределенная функция die) и этот модуль при работе вызовет die, то это будет вызов оригинальной, а не перекрытой функции. Для того чтобы выполнить глобальной перекрытие необходимо раскомментировать оператор *CORE::GLOBAL::die = \¨
И все же, я рекомендую воздержаться от подобных действий. Вряд ли это оправдано, а запутать может.
Ну вот, в принципе, и делов то. Теперь давайте проверим как же легко работать с пейджером юзая наш новоиспеченный модуль. Для этого я написал самый простой пример #!C:/perl/bin/perl -w use strict; use pager qw/die/; open F,'C:/autoexec1.bat' or die "Failed!\n"; close F; Запустите пейджер, а затем - этот тест. Все должно работать как часы. Остается только заметить, что если вы все таки не вняли моим советам и выполнили глобальное перекрытие функции die, то экспортировать die из модуля не нужно. То есть оператор подключения модуля заменяется на более простой вариант: use pager; Пока это все. До встречи. Ну а я сейчас займусь эникеем и Log. |