Web - Cайт с помощью HTML::Mason - PRCY⮭net
Введение

Из-за сложности сопровождения, недостаточной гибкости и ограниченной функциональности человечество давно уже отказалось от статических HTML-страниц. В настоящее время принято отделять дизайн от программного кода. Например, в случае использования Perl, одного из самых распространенных языков web-программирования, дело сводится к генерации страниц с помощью CGI-скриптов, причем сам HTML-код находился в отдельных файлах-шаблонах, состоящих из набора тегов, переменных и управляющих операторов. Данные, полученные в результате работы скриптов, передаются шаблону, который подставляет их вместо переменных. Существует огромное количество модулей, умеющих работать с шаблонами. Некоторые из них позволяют включать код Perl, некоторые используют свой язык программирования. Подробнее о применении различных модулей можно узнать из статьи [3].
Необходимость каждый раз загружать интерпретатор Рerl, дополнительные модули, обрабатывать шаблонные файлы существенно нагружает web-сервер. При большой популярности ресурса это может привести к заметному замедлению работы. Описанных недостатков лишен модуль mod_perl для Apache. К основным достоинствам mod_perl относятся:

* привязка интерпретатора Perl к каждому процессу Apache;
* API к Apache:
* возможность написания собственных обработчиков стадий выполнения запроса – хэндлеров (handlers);
* возможность конфигурирования Apache;
* кэширование Perl-модулей.

Выбор модуля

Модуль-шаблонизатор, работающий под mod_perl, является мощным "движком" для сайтов любой сложности. Для того чтобы он стал обработчиком стадии запроса, отвечающей за генерацию контента страницы, необходимо определить его в настройках Apache как PerlHandler. Это позволит сделать шаблоны (далее компоненты) запускающимися, а сам обработчик будет вызываться незаметно (по аналогии с PHP, JSP и ASP) при каждом обращении к web-серверу.
Выбор автора пал на HTML::Mason. Его возможности довольно широки, он позволяет смешивать код Perl с HTML и вносить изменения в обработчик, а также прекрасно интегрируется с mod_perl.
Идея написания всего кода сайта вперемешку с HTML также имеет ряд недостатков. Mason позволяет достичь компромисса, предоставляя возможность хранения основного кода в модулях (классах). При этом упрощается сопровождение проекта, а использование ООП благоприятно сказывается на масштабируемости. Еще один плюс такой организации - возможность многократного применения кода в любых проектах на Perl, в том числе использующих интерфейс CGI. Далее в статье будут рассмотрены основные возможности HTML::Mason и приведен пример небольшого сайта, созданного на этом "движке".
Установка HTML::Mason

Взять модуль можно с CPAN или с www.masonhq.com. Любой желающий может поучаствовать в разработке и предложить свой вариант дополнений.

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

PerlModule HTML::Mason::ApacheHandler

Это лучше сделать в httpd.conf, а не в .htaccess, так как в этом случае загрузка модуля произойдет при запуске web-сервера, в основном процессе Apache, а все порожденные процессы будут использовать разделяемую память. Это позволит более экономно расходовать память (Mason - не самый маленький модуль), а также существенно сократить время загрузки компонента при первом обращении. В противном случае Mason будет загружаться каждым процессом отдельно.
Далее следует добавить в конфигурационный файл две переменные:

PerlSetVar MasonCompRoot /usr/local/www/data
PerlSetVar MasonDataDir /usr/local/www/mason

Первая переменная определяет корневую директорию компонентов. Вторая - директорию размещения служебных файлов. При первом запуске Mason создает там несколько директорий. Сюда складываются также преобразованные компоненты и кэшированные данные.
Теперь осталось прописать хэндлер. Договоримся, что расширение компонентов будет именно .html. Настроим Apache таким образом, чтобы файлы, отличные от *.html, *.txt и файлов без расширений, не обрабатывались HTML::Mason:

<FilesMatch "(.html|.txt|[^.]+)$">
SetHandler perl-script
PerlHandler HTML::Mason
</FilesMatch>

Основы "движка" Mason

Как уже было сказано, Mason позволяет встраивать Perl-код в сам документ. Это можно сделать несколькими способами, каждый из которых удобен в разных ситуациях.
Так как значения глобальных переменных не уничтожаются после завершения программы (особенность mod_perl), то все используемые переменные необходимо объявлять как лексические, за исключением определенных случаев, о которых будет рассказано позже. В противном случае компонент Mason не пройдет компиляцию, так как весь код проверяется прагмой strict.
Теперь об основах синтаксиса Mason.
1. Строка, начинающаяся с символа %, воспринимается, как Perl-код. Эта конструкция очень удобна в управляющих операторах. Пример:

% if ($browser =~ /msie/i) {
<h3> Вы используете Internet Explorer </h3>
% } else {
<h3> У вас, скорее всего, Netscape </h3>
% }

Минимум синтаксиса, максимум удобства.
2. Блок <% EXPR %> заменяется результатом вычисления выражения EXPR. Обычно применяется для вывода переменных. Пример:

Hello, <% $name %>
<% ($name ? "Hello, $name" : "") %>

В блоке предусмотрена возможность форматирования результата:

<% $html_tags |h %> - заменяет все '<' на '&lt;' и т.д.
<% $string |u %> - преобразовывает строку в urlencoded (пример: ':' заменяется на '%3A')

3. Большие Perl-блоки можно вставлять между тегами <%perl> ... </%perl>
4. В компонентах доступны два глобальных объекта. Первый - $r, объект модуля Apache, из него можно вызывать все методы этого модуля, второй - $m, объект Mason. Объект $m, обеспечивает доступ к очень полезным методам, полное описание которых можно найти в документации к Mason (perldoc HTML::Mason::Request). Пример использования объекта $r:

% my $ua = $r->header_in('User-Agent');
% my $host = $r->get_remote_host;

5. Mason не был бы так популярен, если бы в нем нельзя было подключать другие компоненты. Сложный документ бывает удобно разбить на несколько файлов. Одинаковые компоненты можно использовать в разных страницах, передавая им соответствующие параметры. Есть два способа вызова компонентов. Первый предназначен для вставки в HTML, второй представляет собой метод объекта $m. Пример:

1) <& /path/comp_name, par1 => $value1, par2 => $value2 &>
2) $m->comp('/path/comp_name',par1 => $value1, par2 => $value2 );

Путь вычисляется относительно MasonCompRoot. В качестве параметров можно передавать ссылки на массивы и хэши. Остальные переменные в вызванном компоненте недоступны.
6. Если есть возможность передать данные, должен быть способ их извлечь. Здесь Mason опять предлагает несколько способов. Причем неважно, передаются ли эти параметры через запрос GET (POST) или же при вызове компонента.

Первый способ

Хэш %ARGS содержит все переданные параметры. Если имена параметров повторяются (например, при использовании checkbox), то соответствующий ключ хэша содержит ссылку на массив. С одной стороны, хэш удобен для разработчика, он выделяет переменные, к которым необходимо относится с большой осторожностью, так как они передаются от пользователя. С другой, записи выглядят очень громоздко. Mason предлагает альтернативный вариант получения параметров.

Второй способ

<%args>
$par1 => undef
@par2 => undef
</%args>

Здесь все понятно, undef после => задает значение по умолчанию, если во входных данных параметр будет отсутствовать. Повторяющиеся параметры попадают в массив.
7. Секции <%init> ... </%init> и <%cleanup> ... </%cleanup> определяют код инициализации и завершения соответственно.
Создание сайта

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

Создание дизайна опустим и перейдем к программной части. Так как дизайн сайта будет одинаковым для всех страниц, выделим статическую и динамическую части. В простейшем случае это три элемента: верхняя часть страницы (header), тело страницы (body) и нижняя часть (footer) – см. рис. 1. Header и footer (статическая часть сайта) будут общими для всех страниц. Поэтому их можно вынести в отдельные компоненты и подключать во всех страницах. Но прописывать один и тот же код во всех компонентах (в динамической части) - занятие неблагодарное. Программисты на (PHP/ASP) обходят это неудобство, организуя вывод всего содержимого сайта из одной страницы. Передаваемый параметр, определяет, какой документ нужно выдать. Но это не самый лучший вариант, и при сложной структуре сайта порождает ряд проблем.

Mason предлагает более удобный способ обхода данного неудобства - через компонент autohandler. Mason перед обработкой вызванного компонента проверяет в текущей и вышележащих директориях наличие файла autohandler. Если он найден, управление сначала передается ему. Итак, пришло время написания первого Mason-документа. Файл autohandler:

<html>
<title> @ Test page @</title>

<style type="text/css">
<!--
body {background-color: <% $BG %>}
-->
</style>

<body text=<% $FN %>>

<table width=800 align=center border=0 cellpadding=0 cellspacing=0>
<tr><td align=right colspan=5><% scalar localtime() %></td></tr>
<tr><td colspan=5>Test page</td></tr>
<tr><td width=150 valign=top>
<!-- Левая колонка -->
</td>
<td width=1 background="img/bg.jpg">
<img src="img/bg.jpg" border=0>
</td>
<td width=498>

<!-- Тело документа -->
<% $m->call_next %>
<!-- Конец тела документа -->

</td>
<td width=1 background="img/bg.jpg">
<img src="img/bg.jpg" border=0>
</td>

<td width=150 valign=top><br>
<!-- Правое меню -->
</td>

</tr>

</table></body>
</html>

<%init>
my $BG;
my $FN;
$BG = $cookie{'BG'} || "#006633";
$FN = $cookie{'FN'} || "#cccc66";
</%init>

Для экономии места описания стилей опущены. Полную версию тестового сайта можно взять по адресу [4]. В autohandler находятся и header и footer. Разберемся, как все работает. Пусть запрашивается файл index.html. Mason сначала находит autohandler, выполняет блок <%init> и выдает пользователю все, что предшествует $m->call_next. Это и есть наш header. Метод call_next передает управление запрошенному компоненту (index.html). После обработки и выдачи результатов работы index.html выводится оставшаяся часть autohandler.

Стремясь сделать сайт максимально удобным, предоставим посетителю возможность настраивать цвет текста и фона (хотя параметров можно выбрать больше, но для демонстрации ограничимся двумя). Вы, наверное, заметили глобальный хэш %cookie. Этот хэш содержит переданные браузером cookies. Пока не будем вдаваться в подробности занесения cookies в этот хэш.

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

Теперь можно сосредоточиться на программировании более важных вещей. Далее рассмотрим файл index.html (рис. 2). Допустим, он выводит последние 10 новостей, сортируя по убыванию дат. Все новости хранятся в MySQL в таблице news.

<table>
% while (my ($date,$desrc,$link) = $select->fetchrow()) {
<tr><td>
<% $date %> - <% $descr %> ...<a href="<% $link %>">Подробнее</a>
</td></tr>
% }
</table>

<%init>
my $select = $dbh->prepare(qq(SELECT * FROM news ORDER BY ndate DESC LIMIT 10));
$select->execute;
</%init>

<%cleanup>
$select->finish;
</%cleanup>

Автор не сторонник того, чтобы хранить SQL-код в компонентах. При изменении таблицы можно просто не вспомнить, в каких документах осуществляется выборка. Оптимальным вариантом может быть создание отдельного модуля с константами или использование собственных процедур.

В начале статьи говорилось, что модуль Mason позволяет вносить изменения в обработчик, а точнее использовать свой. Если есть возможность, лучше использовать именно его. В дистрибутиве модуля есть "скелет" этого обработчика - файл handler.pl. Конечно же, вам не придется писать с нуля свой handler. Достаточно лишь дописать свой код в уже имеющийся. В handler.pl подключается HTML::Mason, он-то и выполняет всю работу. В этом файле создаются глобальные хэш %cookie и объект $dbh. Не забудьте заменить строку PerlModule HTML::Mason::ApacheHandler на PerlRequire /path/handler.pl в httpd.conf.

В handler.pl есть два блока, в которые можно вносить изменения. В первом подключаются модули. Очень удобно подключать в этом блоке все используемые модули и работать с ними в компонентах через глобальные объекты. Во втором (метод handler) описывается весь код. Пример файла

handler.pl:
package HTML::Mason;
use HTML::Mason;
use HTML::Mason::ApacheHandler;
use strict;
{
# блок подключения модулей
package HTML::Mason::Commands;
# глобальные переменные
use vars qw(%cookie $dbh);
# используемые модули
use CGI::Cookie;
use DBI;
}
# создание объектов mason
my $parser = new HTML::Mason::Parser;
my $interp = new HTML::Mason::Interp (parser => $parser, comp_root => '/usr/local/www/mason', data_dir => '/usr/local/www/data');
my $ah = new HTML::Mason::ApacheHandler (interp => $interp);
chown (Apache->server->uid, Apache->server->gid, $interp->files_written);
sub handler
{
my ($r) = @_;
my %c = parse CGI::Cookie($r->header_in('Cookie'));
# удаляем предъыдущее значение
%HTML::Mason::Commands::cookie = ();
foreach my $temp (keys %c) {
# добавляем значения в хэш
$HTML::Mason::Commands::cookie{$temp} = $c{$temp}->value();
}
# подключаемся к БД
$HTML::Mason::Commands::dbh = DBI->connect("DBI:mysql:database=mason;host=host", "user","pass");
my $status = $ah->handle_request($r);
$HTML::Mason::Commands::dbh->disconnect();
return $status;
}
Компонент с формой настройки дизайна сайта выглядит следующим образом.
<%def .outmesg>
<% $ARGS{'MESG'}%>
</%def>

Настройка сайта

<form action="tunning.html">
Цвет фона: <input name=BG>
Цвет шрифта: <input name=FN>
<input type=submit name=GO>
</form>

<%init>
if ($ARGS{'GO'}) {

# проверка введенных полей
if ($ARGS{'BG'} !~ /^[dw]+$/ || $ARGS{'FN'} !~ /^[dw]+$/) {
$m->comp(".outmesg", MESG => "Неправильно введены данные");
}
else {
$r->headers_out->add('Set-Cookie',"BG=$ARGS{'BG'}");
$r->headers_out->add('Set-Cookie',"FN=$ARGS{'FN'}");
$m->comp(".outmesg",MESG => "Установки сохранены");
}
}
</%init>

Тег <%def> описывает компонент в компоненте.

Рисунок 1. index.html

Рисунок 2. Структура простого сайта
Последние штрихи

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

PerlModule Apache::Constants
<FilesMatch "^_|autohandler">
SetHandler perl-script
PerlInitHandler Apache::Constants::NOT_FOUND
</FilesMatch>

Это защитит внутренние компоненты от нежелательного вызова из браузера. Но в этом случае все внутренние компоненты должны начинаться с символа “_”.
Заключение

Более подробную информацию можно узнать из документации к модулю (perldoc HTML::Mason::Devel, perldoc HTML::Mason::Admin).
О HTML::Mason в статье сказано далеко не все. Но приведенной информации уже достаточно для того, чтобы оценить достоинства этого модуля. Старания разработчиков не прошли даром. Этот модуль уже успел занять далеко не самое последнее место в списке популярных web-технологий.
Information
  • Posted on 27.04.2013 15:10
  • Просмотры: 1769