АЛЕКСЕЙ МИЧУРИН
Обработка HTML-шаблонов off-line Возможности и ограничения
Сборке HTML-документов по шаблонам посвящено великое множество публикаций самого разного масштаба и качества: от небольших статей до детальных руководств и книг, от дилетантских до весьма профессиональных. Но подавляющее большинство авторов сосредоточивается на on-line-сборке. Ими рассматривается ситуация, когда на сервере лежат не статические документы, а шаблоны: заготовки и части документов. При запросе клиентом соответствующей страницы на сервере запускается некий механизм, собирающий веб-страницу «на лету» (в реальном масштабе времени) и отправляющий её клиенту. Для решения подобных задач разработано множество инструментов и средств, начиная с несложных и интегрированных глубоко в сервер (например, SSI) и заканчивая многофункциональными самостоятельными модулями и библиотеками с очень богатыми возможностями.
Тема on-line-обработки шаблонов действительно очень интересна и воистину неисчерпаема, поскольку в разных условиях оказываются уместны разные подходы. Неудивительно, что так много авторов обращается именно к этой теме. Но я хотел бы уделить немного внимания механизмам off-line-сборки. Возможно, не слишком распространённым термином «off-line-сборка» я буду называть процесс сборки шаблонов на локальной машине, в отсутствие серверного ПО и не для передачи клиенту. В результате такой сборки вы получаете набор статических документов (хотя никто не запрещает использовать, например, SSI-инструкции), готовых к размещению на сервере. Оказывается, концепция сборки документов по шаблону может быть весьма полезна не только при сборке документов «на лету», но и при сборке статических документов.
Прежде чем обсудить конкретную реализацию off-line-процессора шаблонов, нам необходимо сформулировать критерии, которым он должен соответствовать. Но перед этим давайте определимся, в каких ситуациях off-line-сборка может быть полезна (и для кого написана эта статья).
Когда применима off-line-сборка?
Ценность автоматизации сборки статических документов может показаться неочевидной, но это только на первый взгляд. Да, off-line-сборка позволит создавать только статические документы. Приёмы, которые мне предстоит описать, ни в коей мере не связаны с «on-line-движками». Тем не менее, разработка статических документов – неотъемлемый и трудоёмкий процесс, неизбежно сопровождающий создание и поддержку любого веб-ресурса. Облегчению этой задачи и посвящена настоящая статья. А прежде чем привести два примера, замечу, что техника, изложенная здесь, может применяться и для создания частей документов, которые далее будут использоваться как исходные данные для «on-line-движков».
Приведу два, как мне кажется, наиболее житейских примера.
Практически у любого ресурса есть страницы, которые обновляются редко, но время от времени обязательно обновляются. Это могут быть адреса торговых точек и филиалов, телефоны, расписание работы, информация о сотрудниках, подборки статей и многое другое. Эти страницы, скорее всего, должны быть оформлены в едином стиле с содержимым всего сервера, то есть включать стандартную шапку, элементы навигации и прочее. Невольно появляется мысль использовать для их создания технику сборки по единому шаблону. Вместе с тем, на практике эти страницы несут статическую информацию. Собирать их вновь при каждом запросе не очень разумно. Гораздо рациональней – собрать их единожды и разместить на сервере, а при необходимости вносить малые изменения в шаблоны, легко пересобирать необходимые страницы и размещать их на сервере. Возможно, необходимость проведения таких работ будет возникать раз в несколько лет, но даже редкую работу лучше делать просто, чем сложно.
Здесь возникает возражение: такой поход прожорлив до дискового пространства. Соглашусь с этим, но отвечу, что дисковое пространство стоит копейки и обходится гораздо дешевле процессорного времени, за одну только возможность использования коего обычно приходится дополнительно доплачивать. При отказе от on-line-сборки в пользу off-line-сборки мы проигрываем в дисковом пространстве, но объем трафика (гораздо более важная характеристика) не меняется, а процессорное время (тоже весьма важная характеристика) существенно экономится. Таким образом, суммарно мы, скорее всего, получим выигрыш. (Вопросы трудоёмкости загрузки большого объёма данных на сервер и другие вопросы я обязательно затрону, но ближе к концу статьи.)
Как видите, в описанной ситуации off-line-сборка вполне оправданна, а такая ситуация возникает при поддержке практически любого ресурса в Web.
Вторая немаловажная область для применения техники off-line-сборки – разработка. Веб-дизайнер[1] должен иметь возможность оперативно глянуть на дело рук своих. Если его работа полностью сосредоточена на разработке шаблонов, сборка которых произойдёт только на сервере, то ему (на локальной машине) затруднительно увидеть результат своих трудов. Сперва он вынужден разместить всё на сервере, а это не только трудно, но и потребует от дизайнера квалификации, не свойственной для него.
В этой ситуации было бы удобнее, если бы дизайнер имел под рукой процессор обработки шаблонов и мог быстро пересобрать весь набор документов, легко «играя» палитрами или компоновками материала.
То же самое относится и к верстальщику. Ему было бы нелишне иметь возможность легко оценить, как будет выглядеть его работа в рамках общего дизайна (не «съедут» ли иллюстрации и таблицы, уместны ли цвета и т. п.).
Разработка тем более упрощается, если программист, дизайнер и верстальщик пользуются одним набором инструментов и одним форматом шаблонов.
Итак, задачи намечены, наметим теперь пути их решения.
Требования к аппарату off-line-сборки
Самые общие требования
- Первое требование прозвучит почти комично: наша система сборки должна обеспечивать сборку. То есть она должна давать возможность описать, в какое место обрабатываемого файла содержимое какого файла будет вставляться.
Такие места вставки будем называть точками вставки. Получив возможность вставлять некую информацию во многие места многих файлов, мы получили возможность хранить одну информацию в одном месте. Это позволит и изменять (при необходимости) одну информацию в одном месте. То есть при последовательном проведении этой техники в жизнь вы сможете поменять название раздела в одном-единственном файле, одним касанием пересобрать документы, и название раздела изменится во всех оглавлениях, заголовках, элементах ... и во всех других местах, где встречалось это название. Так же можно будет менять цвета и другие элементы оформления. То есть ресурс становится предельно управляемым во всех отношениях.
- Второе требование: программный код (код, выполняющий сборку) должен быть полностью отделён от кода шаблонов.
Это классическое требование, которое всегда предъявляется к обработчикам шаблонов и нарушается с таким же постоянством. Иначе и быть не может. С одной стороны, процессор шаблонов должен быть управляемым, то есть программируемым, иначе он сможет собирать только один документ по одному шаблону и утратит всяческий смысл. Но, с другой стороны, программный код в шаблонах должен, как мы только что сказали, отсутствовать. Этот парадокс каждый из разработчиков решает согласно своим задачам. Наши задачи таковы, что в шаблонах должен быть минимум управляющих конструкций. Мы намеренно не будем реализовывать ни переменных, ни условных переходов. Естественно, у нас не будет ни циклов (частный случай условных переходов), ни вычислений (направленных на обработку переменных).
Такие строгие меры продиктованы жизненной необходимостью. Помните про первое применение – долгосрочную поддержку ресурса? Шаблоны должны быть таковы, чтобы можно было легко вспомнить, что тут к чему, даже после полного забвения, которое приходит обычно уже через пару месяцев после окончания активной фазы разработки. При этом мы не должны наивно рассчитывать, что кто-то станет документировать сбою работу. С шаблонами должен легко управляться и дизайнер, не имеющий никакого представления о программировании, переменных, управляющих конструкциях и прочем.
- Программа обработки шаблонов должна быть максимально переносима. Её работоспособность не должна зависеть от ОС, ПО, дополнительных модулей и расширений. У дизайнера и верстальщика должна быть возможность просто носить её на flash-носителе вместе с рабочими материалами. Установка на новую машину должна быть упрощена до полной незаметности.
- Аппарат сборки должен выдавать ясный и простой протокол работы, позволяющий легко понять (или восстановить в памяти) ход сборки и источники той или иной информации, а при необходимости легко локализовать ошибки.
- Конечно, шаблоны должны обрабатываться рекуррентно, допуская вставку шаблона в шаблон.
Требования к шаблонам
- Синтаксис оформления точек вставки должен допускать достаточную гибкость. Разработчик должен иметь возможность сделать точку вставки и заметной, и компактной, в зависимости от конкретной ситуации.
- Точки вставки не должны быть похожи на HTML-теги. Это позволит беспрепятственно применять программы проверки HTML-кода к отдельным шаблонам и легко локализовать ошибки типа забытых закрывающих тегов или неверных атрибутов.
Возможности управления ходом сборки
Пришло время компромиссов и разрешения парадокса, о котором я говорил. Я уже вижу, как взгрустнули программисты. Конечно, отказ от переменных, условных переходов, вычислений и циклов – тяжёлая утрата. Можно ли сочетать предельную простоту кода и достаточную управляемость? Думаю, что да.
Я бы назвал предлагаемое решение «условной вставкой». Каждый шаблон обрабатывается с определённым параметром. Фактически параметр – это единственная переменная. Но от того, что она одна, ей не нужно имя и синтаксис её использования становится предельно прост и понятен даже человеку, далёкому от программирования (особенно, если не называть её словом «переменная»). В зависимости от этого параметра в шаблон будет встраиваться содержимое того или иного файла.
Итак, ещё одно требование:
- Обработчик должен поддерживать сборку с параметром и условную вставку.
Пока, пожалуй, хватит требований, мелкие замечания и уточнения разберём на конкретном примере.
Пример реализации off-line-процессора
Давайте теперь рассмотрим программу, хоть и далёкую от совершенства и законченности, но компактно реализующую изложенные выше идеи и вполне работоспособную, в чём у нас будет возможность убедиться в следующем разделе (где будет создан небольшой сайт из шести страниц).
Выбор языка
При выборе языка программирования я остановился на вездесущем Perl, не устояв перед его широкими возможностями и богатством платформ, на которые он перенесён.
Кроме того, давайте не будем использовать в нашей программе внешних модулей. Такой код можно будет запустить под Windows, даже не устанавливая громоздкий ActivePerl, а воспользовавшись одним только perl.exe из коллекции DJGPP. Многие из них обладают существенными ограничениями, которые касаются сетевых возможностей, возможностей работы с базами данных, ограниченным набором модулей, но эти ограничения не повлияют на работоспособность кода, приводимого в настоящей статье. Неприятным сюрпризом может стать только то, что некоторые сборки для DOS не поддерживают длинных имён.). У пользователей UNIX проблем с установкой Perl вообще не возникнет, поскольку Perl является неотъемлемой частью подобных систем.
Можно было бы поступить ещё концептуальней: написать программу на чём-нибудь компилируемом, например, на C. Такая программа не требовала бы даже интерпретатора. Возможно, это хорошая идея. Я не пошёл по этому пути по двум причинам. Во-первых, разница не так принципиальна: избавились от интерпретатора – понадобился компилятор. Во-вторых, код получился бы не такой компактный, и приводить его в статье было бы не так удобно. Последнее обстоятельство, как вы понимаете, не должно останавливать вас.
Осмотр кода и формат сценария сборки
Давайте пробежим глазами строки кода, который у меня получился:
01: #!/usr/bin/perl -w
02:
03: #use strict;
04:
05: my $INPUT_PATH ="<input/";
06: my $OUTPUT_PATH=">output/";
07:
08: sub assemble_step {
09: my ($level, $file, $key)=@_;
10: print ((". "x$level).$file.":".$key." ");
11: local $/;
12: open FH, $INPUT_PATH.$file or die $file." : ".$!;
13: my $text=<FH>;
14: close FH;
15: $level++;
16: $text=~s{(##[#s]*([^#s]+)[#s]*##)}{
17: my ($fn, $k)=($1, $key);
18: $fn=~s/?/$key/g;
19: $fn=~s/(([^)]+))/($key eq $1)?"yes":"no"/ge;
20: ($fn, $k)=($1, $2) if ($fn=~m/^([^:]+):(.+)$/);
21: assemble_step($level, $fn, $k);
22: }ge;
23: return $text;
24: }
25:
26: sub assemble {
27: my ($output, $input_root, $init_key)=@_;
28: my $text=assemble_step(0, $input_root, $init_key);
29: open FH, $OUTPUT_PATH.$output or die $output." : ".$!;
30: print FH $text;
31: close FH;
32: }
33:
34: while (<>) { assemble(split) }
Я буду предполагать, что читатель знаком с Perl, и ограничусь только краткими пояснениями. Тело программы состоит из одной строки 34 (если не считать объявления двух глобальных переменных в строках 5 и 6). Здесь в цикле while одна за другой обрабатываются строки входного файла. Чтобы отличать его от шаблонов, давайте назовём его громким словом «сценарий сборки» или просто «сценарий».
Каждая строка сценария сборки разбивается на поля (разделители – любые пробельные символы в любом количестве). Значения этих полей передаются функции assemble, которая инициализирует и запускает процесс сборки шаблона, а результат сборки записывает в назначенный файл.
Первое поле каждой строки сценария задаёт имя собираемого файла (имя файла, в который будет помещён окончательный результат сборки). Второе поле – имя корневого шаблона, с которого начнётся сборка. Третье поле – параметр, с которым будет собираться корневой шаблон.
Функция assemble_step, выполняющая саму сборку, вызывается рекуррентно (вы видите, что она вызывается из самой себя в строке 21). Её аргументы таковы: первый – уровень вложенности (глубина рекурсии); второй – имя файла шаблона, который необходимо обработать; третий – параметр сборки, с которым необходимо обработать шаблон.
Первое, что делает функция assemble_step, – выдаёт строку протокола сборки (строка 10 листинга). Начальные символы строки задают отступ, показывая глубину рекурсии (чем больше уровень вложенности, тем больше отступ). Далее следует имя файла шаблона и параметр, с которым его предстоит обработать.
Далее (строки 11-15) в переменную $text читается шаблон, глубина вложенности увеличивается на единицу (это значение будет передано «дочерним» assemble_step) и начинается самое интересное – обработка шаблона.
Сердцем нашего процессора шаблонов, как вы уже успели догадаться, являются строки листинга с 16 по 22, представляющие собой просто одно выражение глобального поиска и замены. Здесь-то и происходит сборка.
Что же мы ищем и на что заменяем?
Точки вставки
Ищем мы, конечно, точки вставки. Как видите (строка 16), оформлены они у нас будут достаточно гибко: открывающая круглая скобка; два знака #; любое количество пробелов или знаков #; некоторое количество знаков, отличающихся от пробелов и #, они сохранятся в переменной $1 и будут использованы для определения, что именно необходимо вставить в данную точку; далее следует снова произвольное количество пробелов и символов #; и наконец завершающие два # и закрывающая скобка.
Я не навязываю читателю именно такой стиль, просто мне он кажется удобным. Знак # выбран потому, что его легко заметить в тексте. А формат позволяет оформлять точки вставки и компактно (полезно, когда их много):
(## NAME ##)
И громоздко (полезно, когда точек вставки мало и их надо выделить):
(###############
NAME
###############)
или
(## ##
## NAME ##
## ##)
Точки вставки, таким образом, можно оформлять в духе комментариев C с той лишь разницей, что начальный и конечный символы у нас разные и написание регулярного выражения для поиска точек вставки упрощается.
Вставка
Что мы будем заменять – теперь понятно, давайте разберёмся с тем, чем мы будем заменять.
В качестве имени файла, который будет вставляться в данную точку, будет использоваться наша переменная $1, но... после небольшой «доработки»:
- Первым делом (строка 18) все знаки ? в потенциальном имени файла заменятся на текущее значение параметра сборки.
То есть если код:
<html>(## text-for-? ##)</html>
собирается с параметром index, то между тегами и будет вставлен шаблон из файла text-for-index. Если же этот же шаблон собирается с параметром paper, то вместо (## text-for-? ##) будет вставлен шаблон из файла text-for-paper.
- Далее (в строке 19) в потенциальном имени файла отыскиваются все конструкции в круглых скобках. С ними производится следующая замена: если строка в скобках совпадает с текущим параметром сборки, то скобка заменяется на строку «yes», в противном случае она заменяется на строку «no».
Поясню на примере шаблона:
<html>(## is-i-home-(index).txt ##)</html>
Если этот шаблон собирается с параметром index, то будет использован файл is-i-home-yes.txt. Во всех других случаях, напротив, будет использован is-i-home-no.txt.
- Затем (строка 20) проверяется, содержит ли потенциальное имя файла двоеточие. Если ответ утвердительный, то часть имени слева от двоеточия используется в качестве имени файла, правая часть имени будет использована как новый параметр сборки.
Это даёт возможность «подменять» параметр сборки в процессе самой сборки и собирать разные части документа с разными параметрами. Обратите внимание: подмена носит локальный характер, шаблон-«родитель» о ней ничего не знает, его обработка продолжается с тем же параметром. Подмену замечает только шаблон-«потомок» (и его потомки, если не произойдёт ещё одной подмены).
- Наконец (строка 21) мы вызываем рекуррентно assemble_step, которая производит сборку того, что нам в результате оказалось нужно (и, возможно, с новыми параметрами), и помещаем результат в точку вставки.
Пример сборки ресурса
Давайте теперь рассмотрим пример создания простенького веб-ресурса с помощью нашей «кухни», и пусть его простота нас не смущает, ведь любой сложный ресурс всегда можно разбить на простые части (речь, конечно, по-прежнему идёт только о статических ресурсах).
На нашем сервере будет три страницы: index.html, contact.html и about.html. Будем называть их основными. У каждой из них будет ещё и версия для печати: index-print.html, contact-print.html и about-print.html, соответственно. Итого шесть страниц, все они показаны на рисунке с указаниями имён файлов.
Сценарий сборки будет таков:
01: index.html skeleton index
02: contact.html skeleton contact
03: about.html skeleton about
04: index-print.html skeleton-print index
05: contact-print.html skeleton-print contact
06: about-print.html skeleton-print about
Каждому собираемому html-файлу, как вы видите, здесь отвечает своя строка, в которой, кроме имени файла-мишени, указываются шаблон и параметр.
Как вы поняли, все основные станицы будут собраны по шаблону skeleton, а страницы для печати – по шаблону skeleton-print.
Сборка основных страниц
Рассмотрим сперва сборку основных страниц: index.html, contact.html и about.html. Согласно первым трём строчкам сценария сборки, все они собираются по общему шаблону skeleton, варьируется только параметр (исключительно для простоты чтения он тождественен имени соответствующего собираемого файла).
Этот раздел будет фактически посвящён рассмотрению шаблона skeleton:
01: (### body-open ###)
02: (### sep #########)
03: <table width="100%">
04: <tr>
05: <td><img src="../img/title.gif"></td>
06: <td align="right"><img src="../img/banner.gif"></td>
07: </tr>
08: </table>
09: (####### sep #######)
10: <table>
11: <tr valign="top">
12: <td>(## toc ##)</td>
13: <td>
14: <big><b>(## ?-head ##)</b></big>
15: <br>
16: <a href="(## ?-url-print ##)"><b><font size="1">
17: версия для печати</font></b></a>
18: <br>(## ?-text ##)
19: </td>
20: </tr>
21: </table>
22: (### sep ##########)
23: (### body-close ###)
В его первой строке находится точка вставки шаблона body-open:
01: <html>
02: <head>
03: <title>(## ?-head ##)</title>
04: </head>
05: <body>
В этом шаблоне есть тоже одна точка вставки. В неё вставляется информация одного из трёх файлов index-head, contact-head и about-head. При сборке index.html (с параметром index) используется первый – index-head, для contact.html и about.html – contact-head и about-head соответственно. В файлах *-head, как вы уже поняли, содержатся заголовки соответствующих страниц. Например, в index-head:
01: О нас
Во второй строке шаблона skeleton (мы продолжаем его рассмотрение) – точка вставки шаблона sep:
01: <table width="100%">
02: <tr bgcolor="#cccccc">
03: <td align="right">
04: <font size="1" color="#999999">СТЕЛЬКИ Inc.</font>
05: </td>
06: </tr>
07: </table>
Здесь нет ничего хитрого, это чисто декоративный элемент – горизонтальная серая полоса. На наших страницах их три на каждой (см. рисунок).
В строках 3-8 шаблона skeleton – код, отвечающий за логотип и баннер.
В строке 9 повторяется вставка декоративного разделителя sep.
В строках 10-21 описана таблица, несущая собственно тело страницы. В таблице только две ячейки (одна строка, два столбца). Левая содержит навигационное меню (шаблон toc, подключаемый в строке 12), к нему мы вернёмся чуть позже. В правой ячейке содержится несколько точек вставки. В первой из них подключается один из уже знакомых нам шаблонов *-head, соответствующий параметру сборки (строка 14). Это заголовок. В строке 16 создаётся ссылка на страницу с версией для печати. Адрес этой страницы берётся из файлов index-url-print, contact-url-print и about-url-print. Например, index-url-print содержит:
01: index-print.html
Единственное, о чём здесь следует упомянуть: файлы *-url-print не должны содержать ничего, кроме адресов. То есть в них не должно быть пробелов, табуляций, символов LF и CR и других невидимых символов.
И наконец в строке 18 шаблона skeleton мы подключаем текст страницы из файлов (которые могут оказаться, в свою очередь, шаблонами) index-text, contact-text и about-text. Так, например, в файл index.html вставляется текст из файла index-text:
01: Корпорация «СТЕЛЬКИ Inc.» производит
02: недорогие, но высококачественные корпоративные стельки.
03: Мы всегда делаем упор на имиджевые элементы, но
04: предлагаем и универсальные готовые решения...
Шаблон skeleton заканчивается вставкой ещё одного декоративного разделителя sep и закрывающими тегами, вынесенными в отдельный файл body-close.
Сборка страниц для печати
Ещё проще устроена сборка страниц для печати. Как вы помните, для них используется общий базовый шаблон skeleton-print:
01: (### body-open ###)
02: <hr>
03: <big>(## ?-head ##)</big>
04: <hr>
05: <small>© СТЕЛЬКИ Inc.</small>
06: <hr>
07: (##############
08: ### ?-text ###
09: ##############)
10: <hr>
11: <small>адрес ресурса
12: <u>http://www.stelki.biz/(## ?-url ##)</u></small>
13: <hr>
14: <small><a href="(## ?-url ##)">назад</a></small>
15: (### body-close ###)
В начале и в конце этого шаблона мы видим уже знакомые body-open и body-close. Текст заголовка берётся всё из тех же файлов *-head, а текст – из файлов *-text (обратите внимание, как выделена точка вставки текста). Единственные новые шаблоны, с которыми мы тут столкнулись, содержат адреса возврата на исходные страницы (не предназначенные для печати). Я говорю о шаблонах index-url, contact-url и about-url (они фигурируют в строках 12 и 14). Понятно, что каждый из них содержит одну строчку. Например, index-url содержит:
01: index.html
Перейдём теперь к самому интересному.
Сборка оглавления
Вернёмся к шаблону toc, отвечающему за навигационное меню. Как вы уже видели на рисунке, оглавление у нас сделано в лучших традициях гипертекста: ни одна страница нигде не ссылается на саму себя. Для этого нам понадобился довольно мудрёный шаблон toc:
01: <table>
02: (## toc-(index):index ##)
03: (## toc-(about):about ##)
04: (## toc-(contact):contact ##)
05: </table>
Попробуйте ответить на вопрос, сколько шаблонов используется шаблоном toc? Правильный ответ: два. Да! Нам понадобится всего два шаблона.
Первый toc-yes:
01: <tr>
02: <td nowrap bgcolor="#cccccc"><b>•
03: (## ?-head ##)</b></td>
04: </tr>
Второй toc-no:
01: <tr>
02: <td nowrap><b>•
03: <a href="(## ?-url ##)">(## ?-head ##)</a>
04: </b></td>
05: </tr>
Шаблон toc-yes описывает одну строку таблицы, в единственной ячейке которой находится название соответствующего раздела. Шаблон toc-no описывает такую же конструкцию, но ячейка уже не подкрашена, а текст является ссылкой.
Я думаю, читатель уже начал догадываться, как всё это работает.
Например, при сборке документа about.html шаблон toc вызывается с параметром about. Во второй его строке будет подключён файл toc-no, так как параметр about не совпал с круглой скобкой (index). Этот шаблон, в свою очередь, вызывается с подменой параметра на index, и в нём будут подставлены файлы index-rul и index-head. То есть строка с названием первой страницы станет ссылкой на первую страницу. Во второй строке шаблона toc будет производиться вставка шаблона toc-yes, который не формирует ссылки. То есть мы добились своего – страница не ссылается на саму себя. Третья вставка (в четвёртой строке шаблона toc) пройдёт аналогично первой.
Окончательно разобраться в деталях происходящего нам поможет протокол сборки.
Протокол работы
Мы ещё ничего не сказали о протоколе (необходимость коего была оговорена в начале статьи), который выдаётся нашей системой сборки. Я не буду приводить его весь. Вот часть, отвечающая за сборку документа index.html:
01: skeleton:index
02: . body-open:index
03: . . index-head:index
04: . sep:index
05: . sep:index
06: . toc:index
07: . . toc-yes:index
08: . . . index-head:index
09: . . toc-no:about
10: . . . about-url:about
11: . . . about-head:about
12: . . toc-no:contact
13: . . . contact-url:contact
14: . . . contact-head:contact
15: . index-head:index
16: . index-url-print:index
17: . index-text:index
18: . sep:index
19: . body-close:index
Если вы внимательно читали весь предыдущий текст, то он будет вам знаком и понятен. Если же у вас осталось некоторое недопонимание, то он поможет разобраться.
Результаты
Достижения
Итак, что нам удалось сделать? Перечислю кратко основное.
- Мы написали и успешно применили обработчик шаблонов, синтаксис которых настолько прост, что может быть освоен даже абсолютно не знакомым с программированием человеком, каковыми обычно являются веб-верстальщики и веб-дизайнеры. Вместе с тем, наш аппарат позволяет весьма гибко манипулировать шаблонами.
Даже в нашем маленьком примере вставка шаблона index-head происходит семь раз. Это значит, что если бы мы не использовали шаблоны и захотели бы изменить название раздела, то нам пришлось бы вносить исправления в семи местах. Наш механизм в семь раз облегчит задачу.
Обработчик позволяет делать очень многое и не уступает большинству подобных инструментов. Фактически любое изменение стиля или содержания сайта выполняется редактированием всего одного файла.
Эта управляемость идёт на пользу не только отдельным разработчикам, но и облегчает взаимодействие в команде. Так, дизайнер мог вести разработку так, как это делали мы. Но программист перед окончательным размещением документов на сервере может просто поменять в одном файле (skeleton) код, отвечающий за баннер, и одним движением подключить баннерную систему на всех страницах ресурса.
- Шаблоны допускают весьма гибкое форматирование точек вставки, которое может обеспечить и наглядную, и компактную запись.
- Обработчик выдаёт протокол сборки, позволяющий легко понять коллегу или себя.
- Обработчик компактен и элементарно переносится на любую машину, работающую под управлением любой ОС.
Но не будем долго распространяться о мощи шаблонного подхода. Давайте перейдём к недостаткам.
Слабые стороны подхода
Когда вы принимаете решение использовать или не использовать некий подход (в частности, описываемый в этой статье), важно знать не только достоинства и потенциальные возможности, но и недостатки подхода. Итак, какие есть недостатки? Какие из них можно преодолеть, а какие – нельзя?
Относительно работы обработчика
Приведённая здесь реализация практически не содержит механизмов обработки нештатных ситуаций. Естественно, было бы полезно добавить следующее:
- Конечно, наш код допускает множество очевидных косметических усовершенствований: ограничение глубины рекурсии и защиту от зацикливания, проверку правильности сценария сборки... Но сосредоточимся на более существенных моментах.
- Было бы полезно реализовать проверку правильности конструкций, описывающих имя файла в точках вставки. Реакция программы на нештатные ситуации типа вложенных скобок или множественных двоеточий должна быть разумна.
- Программа должна корректно себя вести, если требуемый файл не существует. Как? Решать вам. Может быть, вы предпочтёте аварийную остановку (сейчас сделано именно так) или захотите, чтобы эта ситуация была бы проигнорирована.
Возможно, было бы полезно реализовать поиск входных файлов по нескольким директориям, организовать обработку переменной среды, аналогичной PATH, или вынести информацию о возможных путях к файлам в сценарий сборки.
Плодотворной может оказаться идея «аварийного шаблона», который вставляется в те точки, которые не были корректно обработаны.
- Было бы очень уместно сделать кэширование считанных файлов. Тем более, что это совсем не трудно.
- После того как вы сделаете кэширование, можно очень легко сделать предопределённые шаблоны. Например, шаблон DATE может содержать дату сборки.
- Очень полезной была бы и возможность регламентировать дополнительные обработки (или не обработки) файлов. Например, можно сделать, чтобы файлы с расширениями .file не обрабатывались как шаблоны, а их содержимое вставлялось «как есть». И наоборот, файлы с расширениями .quot проходили бы дополнительную обработку механизмом HTML-квотирования, заменяющим «&» на «&» и так далее. Здесь тоже возможны варианты: должен ли, например, подвергаться квотированию шаблон, вставляемый в квотируемый шаблон?
- Было бы заманчиво хранить информацию с разными именами в одном файле, чтобы не заводить множество маленьких (как это получилось у нас с файлами заголовков и адресов). Это, правда, потребует некоторого усложнения «адресации» данных. Обойтись просто именем файла, как сделали мы, будет уже нельзя. Все ли в вашей команде одобрят подобные нововведения?
Относительно загрузки на сервер
Я обещал вернуться к вопросам загрузки. Действительно, идея собрать все документы одним махом весьма заманчива, пока вы не столкнётесь с необходимостью загрузить их на сервер. Что здесь можно сказать?
Во-первых, для части администраторов эта проблема не будет так остра, потому что большинство изменений всё-таки не будут затрагивать все документы и, конечно, не будут производиться слишком часто (иначе вам действительно следует выбрать для сопровождения вашего ресурса другой инструментарий, реализующий on-line-сборку).
Во-вторых, если вам придётся всё-таки решать эту задачу, то у вас будет по крайней мере два пути решения. Можно написать CGI-сценарий, принимающий файлы в сжатом виде и распаковывающий их на сервере. Так вы сократите трафик в несколько раз. С другой стороны, можно написать сценарий, производящий сборку и размещение на сервере, а передавать ему надо будет только шаблоны. Это может очень сильно помочь сократить трафик, но создание такого механизма потребует от вас нешуточных усилий по обеспечению безопасности.
Усовершенствования, которые сложно сделать
- Когда начинаешь заботиться о «самодокументируемости» подобного сборщика, может появиться мысль: пусть он дополняет документы HTML-комментариями. Сделать это корректно гораздо сложнее, чем кажется на первый взгляд. Вы видели сами, что мы вставляем шаблоны и внутрь тегов. Если включить аппарат автоматической вставки комментариев, то код:
<a href="(## url ##)">
после обработки может превратиться в:
<a href="<!— /file "url" —>index.html<!— file "url"/ —>">
что, естественно, не будет работать, как:
<a href="index.html">
- Процесс сборки, на первый взгляд, напоминает работу утилиты make. Возникает искушение реализовать нечто подобное, избежав ненужных действий при внесении в шаблоны небольших изменений. Это тоже трудно сделать, так как для определения того, какие файлы нуждаются в повторной обработке, надо знать всё «дерево сборки» – все зависимости файлов. В явном виде они нигде не содержатся. Так что, если не сборка, то хотя бы просмотр всех файлов-шаблонов мне представляется неизбежным.
- Неразумным кажется и то, что мы всегда собираем все файлы проекта. Но отказ от этой концепции потребует усложнения синтаксиса командной строки. Или нам придётся придумать какой-то другой способ указать, что именно нам надо. Мой опыт показывает, что веб-дизайнеры этим не пользуются, предпочитая подождать полсекунды. Иногда я думаю, что они в чём-то правы. Кроме того, появление подобной возможности резко сузит возможности создания предопределённых шаблонов, о которых говорилось выше.
Усовершенствования, которые не следует делать
И наконец, я хотел бы обозначить ряд усовершенствований, которые, на мой взгляд, не следует вносить, несмотря на их кажущуюся необходимость и заманчивую простоту.
- Вам может показаться, что не хватает возможности подставлять не файл, а сам параметр сборки. Задумайтесь, перед тем как реализовывать и эксплуатировать эту идею! Поверьте, на этой дороге вы не найдёте счастья. У информации должно быть имя и должно быть значение, нельзя объединять эти две материи.
- Не следует усложнять функциональность – вы потеряете прозрачность и читаемость и усложните код, внедрённый в ваши шаблоны.
Если же вам понадобились циклы или условные переходы, лучше воспользуйтесь готовым решением, трезво взвесив все «за» и «против». Вероятно, новый инструмент потребует от вас установки сервера на машину дизайнера (или на все машины дизайнеров), не исключено, что ваш дизайнер может воспринять в штыки всё это богатство возможностей.
В любом случае это будет другой обработчик шаблонов и уже совсем другая история.
Рисунок 1
Facebook
Мой мир
Вконтакте
Одноклассники
Google+
|