Template Toolkit: Руководство: Директивы

Template Toolkit

(русская редакция)

[ Пособия ] [ Руководство ] [ Модули ] [ Библиотеки ] [ Утилиты ] [ Вопросы ] [ Релиз ] [ Perl-ресурсы ] Форум ]
 
Поиск
Template Toolkit | Руководство | Директивы

Директивы

[ ◄ Синтаксис ] [ Переменные ► ]
Директивы шаблонов

Оглавление

ОПИСАНИЕ

Индекс ] [ Руководство ] [ Наверх ]

Раздел содержит справку о всех директивах Template Toolkit с примерами использования.

Получение и обновление переменных шаблона

  • GET

    Директива GET directive получает и выводит значение указанной переменной.

        [% GET foo %]

    Ключевое слово GET можно опустить. Можно просто указать имя переменной внутри тегов директивы.

        [% foo %]

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

        [% foo %]
        [% bar.baz %]
        [% biz.baz(10) %]
        ... и т.д. ...

    Более подробное описание переменных шаблона дается в документе Template::Manual::Variables.

    Вы также можете использовать выражения, построенные с помощью логических (and, or, not, ?:) и математических (+ - * / % mod div) операторов.

        [% template.title or default.title %]
        [% score * 100 %]
        [% order.nitems ? checkout(order.total) : 'no items' %]

    Оператор 'div' возвращает целую часть результата деления. Оба оператора '%' и 'mod' возвращают остаток от деления. 'mod' является синонимом для '%' и используется для обратной совместимости с версией 1.

        [% 15 / 6 %]	    # 2.5
        [% 15 div 6 %]	    # 2
        [% 15 mod 6 %]	    # 3
  • CALL

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

        [% CALL dbi.disconnect %]
        [% CALL inc_page_counter(page_count) %]
  • SET

    Директива SET позволяет присваивать существующим переменным новые значения или создавать новые временные переменные.

        [% SET title = 'Hello World' %]

    Ключевое слово SET можно опустить.

        [% title = 'Hello World' %]

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

        [% foo  = 'Foo'  %]               # литерал 'Foo'
        [% bar  =  foo   %]               # значение переменной 'foo'
        [% cost = '$100' %]               # литерал '$100'
        [% item = "$bar: ${cost}.00" %]   # значение интерполяции строки "Foo: $100.00"

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

        [% foo  = 'Foo'
           bar  = foo
           cost = '$100'
           item = "$bar: ${cost}.00"
        %]

    Как и в GET можно использовать простые выражения.

        [% ten    = 10
           twenty = 20
           thirty = twenty + ten
           forty  = 2 * twenty
           fifty  = 100 div 2
           six    = twenty mod 7
        %]

    Конкатенция (склеивание) строк выполняется с помощью оператора ' _ '. В Perl 5 для склейки строк используется '.', однако в Perl 6 '.' будет использована как оператор вызова метода, а для конкатенции, как и в Template Toolkit, будет использоваться ' _ '. Обратите внимание, что оператор должен указываться с пробелами вокруг, что, как говорит Ларри, трактуется как особенность:

        [% copyright = '(C) Copyright' _ year _ ' ' _ author %]

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

        [% copyright = "(C) Copyright $year $author" %]
  • DEFAULT

    Директива DEFAULT аналогична SET, но устанавливает только те переменные, которые в текущий момент не определены, либо не являются "истинными" (в том смысле, в каком это понимается в Perl).

        [% DEFAULT
           name = 'John Doe'
           id   = 'jdoe'
        %]

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

        [% DEFAULT
           title = 'Hello World'
           bgcol = '#ffffff'
        %]
        <html>
        <head>
        <title>[% title %]</title>
        </head>
        <body bgcolor="[% bgcol %]">

Обработка других файлов шаблонов и блоков

  • INSERT

    Директива INSERT используется для вставки содержимого внешнего файла в текущую позицию.

        [% INSERT myfile %]

    Файл при этом парсером не обрабатывается. Текст, который может содержать директивы шаблонов, загружается нетронутым.

    Имя файла должно указываться относительно одного из каталогов, указанных в INCLUDE_PATH. Абсолютные (т.е. начинающиеся с '/') и относительные (т.е. начинающиеся с '.') пути к файлам можно использовать если установлены опции ABSOLUTE и RELATIVE соответственно. По умолчанию обе опции отключены.

        my $template = Template->new({
    	INCLUDE_PATH => '/here:/there',
        });
        $template->process('myfile');

    'myfile':

        [% INSERT foo %]		# вначале ищется /here/foo, затем /there/foo
        [% INSERT /etc/passwd %]	# файловая ошибка: ABSOLUTE не установлена
        [% INSERT ../secret %]	# файловая ошибка: RELATIVE не установлена

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

        [% INSERT misc/legalese.txt            %]
        [% INSERT 'dos98/Program Files/stupid' %]

    Для того чтобы использовать в качестве имени файла (или его части) значение переменной, необходимо использовать префикс '$' или использовать интерполяцию строки в двойных кавычках.

        [% language = 'en'
           legalese = 'misc/legalese.txt'
        %]
        [% INSERT $legalese %]		# 'misc/legalese.txt'
        [% INSERT "$language/$legalese" %]  # 'en/misc/legalese.txt'

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

        [% INSERT legalese.txt + warning.txt %]
        [% INSERT  "$legalese" + warning.txt %]  # требуются двойные кавычки
  • INCLUDE

    Директива INCLUDE используется для обработки и включения выходного потока другого шаблона или блока.

        [% INCLUDE header %]

    Если BLOCK с указанными именем определен в том же файле, или в файле из которого текущий шаблон был вызван (т.н. родительский шаблон), то будет использован он, а не е любой другой файлом с тем же именем.

        [% INCLUDE table %]		    # используется определенный ниже BLOCK
        [% BLOCK table %]
           <table>
           ...
           </table>
        [% END %]

    Если определение BLOCK недоступно, то имя шаблона будет интерпретироваться как имя файла относительно одного из каталогов, определенных в INCLUDE_PATH, или как абсолютное или относительное имя файла, если соответствующие опции ABSOLUTE/RELATIVE разрешены. Директива INCLUDE автоматически заключает в кавычки указаное имя файла, аналогично тому как это описано выше для INSERT. Когда переменная содержит имя шаблона для директивы INCLUDE она должна явно обозначаться с помощью префикса '$' или заклили заключаться в двойные кавычки.

        [% myheader = 'my/misc/header' %]
        [% INCLUDE   myheader  %]            # 'myheader'
        [% INCLUDE  $myheader  %]		 # 'my/misc/header'
        [% INCLUDE "$myheader" %]		 # 'my/misc/header'

    Любые директивы шаблона, содержащиеся внутри файла будут соответствующим образом обработаны. Все определенные в текущий момент переменные будут видимы и доступны во включаемом шаблоне.

        [% title = 'Hello World' %]
        [% INCLUDE header %]
        <body>
        ...

    'header':

        <html>
        <title>[% title %]</title>

    На выходе:

        <html>
        <title>Hello World</title>
        <body>
        ...

    После имени шаблона можно указать локальные переменные, которые временно скрывают (маскируют) все существующие переменные с такими же именами. Лишние пробельные символы внутри директивы игнорируются, поэтому вы можете добавить определения переменных на той же строке, следующей строке или разбить на несколько строк, сопроводив их комментариями, если это предпочтительней.

        [% INCLUDE table %]
        [% INCLUDE table title="Active Projects" %]
        [% INCLUDE table
    	 title   = "Active Projects"
             bgcolor = "#80ff00"	# бледно-зеленный
    	 border  = 2
        %]

    Директива INCLUDE локализует (т.е. создает локальные копии) всех переменных перед обработкой шаблона. Все изменения внутри включаемого шаблона не окажут влияния на переменные в родительском шаблоне.

        [% foo = 10 %]
        foo изначально [% foo %]
        [% INCLUDE bar %]
        foo опять [% foo %]
        [% BLOCK bar %]
           foo было [% foo %]
           [% foo = 20 %]
           foo стало [% foo %]
        [% END %]

    На выходе получаем: foo изначально 10 foo было 10 foo стало 20 foo опять 10

    Техническое примечание: локализация хранилища переменных (stash) (процесс, посредством которого переменные копируются перед INCLUDE, чтобы предотвратить перезапись) является поверхностной. Копируется хэш пространства имен переменных верхнего уровня, но глубокого копирования других структур (хэши, массивы, объекты и т.д.) не проводится. Следовательно, указатель на хэш 'foo' будет скопирован в новую переменную 'foo', но указывать она будет на тот же самый хэш. Таким образом, обновление составных переменных (например, foo.bar) приведет к изменению оригинального значения, несмотря на локализацию хранилища. Если вас не заботит сохраность начальных значений, или вы уверены в надежности включаемых шаблонов, вы можете использовать директиву PROCESS, которая работает быстрее из-за того, что не выполняет никакой локализации.

    Начиная с версии 2.04, вы можете указывать составные переменные в качестве "локальных" переменных в директиве INCLUDE. Тем не менее, избегайте этого из-за описанных недостатков (если вы пропустили техническое описание, данное выше, вы можете вернуться и прочитать его, либо пропустить и этот раздел тоже) - переменные могут не быть в действительности "локальными". Если первый элемент имени переменной является ссылкой на хэш, то изменение переменной приведет к изменению оригинального значения.

        [% foo = {
               bar = 'Baz'
           }
        %]
    
        [% INCLUDE somefile foo.bar='Boz' %]
        [% foo.bar %]	    # Boz

    Такое поведение немного непредсказуемо (и может будет сильно улучшено в будущих версиях). Если вы знаете, что делаете, и уверены, что сомнительные переменные определены (или нет) как положено, то вы можете смело использовать эту возможность для реализации мощных методов по разделению "глобальных" переменных. С другой стороны, вы можете придерживаться ясного четкого правила и передавать в качестве параметров директиве INCLUDE и другим схожим директивам только простые (не составные) переменные.

    Если вы хотите обработать несколько шаблонов за один проход, вы можете указать через '+' их имена (в кавычках или без, переменные только в двойных кавычках). Директива INCLUDE обработает их по порядку.

        [% INCLUDE html/header + "site/$header" + site/menu
             title = "My Groovy Web Site"
        %]

    Хранилище переменных (stash) локализуется один раз и затем указанные шаблоны обрабатываются по порядку в общем окружении переменных. Это дает небольшой выигрыш по времени исполнения по сравнению с раздельным выполнением директив INCLUDE (хранилище переменных копируется только один раз), но не совсем "безопасно", поскольку любое изменение переменной в первом файле отразится во втором, третьем и т.д. Возможно, конечно, что это именно то, что вам нужно, но, опять-таки, возможно, что и нет.

  • PROCESS

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

        [% foo = 10 %]
        foo - [% foo %]
        [% PROCESS bar %]
        foo - [% foo %]
        [% BLOCK bar %]
           [% foo = 20 %]
           меняем foo на [% foo %]
        [% END %]

    На выходе:

        foo - 10
           меняем foo на 20
        foo - 20

    Директиве PROCESS также можно передавать параметры, но изменения и этих переменных тоже отразятся на текущих значениях переменных в родительском шаблоне.

        [% foo = 10 %]
        foo - [% foo %]
        [% PROCESS bar
           foo = 20
        %]
        foo - [% foo %]
        [% BLOCK bar %]
           это bar, foo - [% foo %]
        [% END %]

    На выходе:

        foo - 10
           это bar, foo - 20
        foo - 20

    Директива PROCESS работает немного быстрей, чем INCLUDE, потому что ей не требуется локализовать (т.е. копировать) хранилище переменных перед обработкой шаблона. Как и для INSERT и INCLUDE нет необходимости заключать в кавычки первый параметр, если он содержит только латинские символы, цифры, подчеркивания, точки и прямые слэши. Префикс '$' можно использовать, чтобы явно указать, что имя шаблона следует получить из переменной:

        [% myheader = 'my/misc/header' %]
        [% PROCESS  myheader %]              # 'myheader'
        [% PROCESS $myheader %]		 # 'my/misc/header'

    Как и в INCLUDE, можно указать несколько шаблонов, разделенных '+', и они будут обработаны по порядку.

        [% PROCESS html/header + my/header %]
  • WRAPPER

    Часто приходится сталкиваться с задачей добавления общих заголовков и подвалов на страницах или в подразделах внутри страницы. Что-то вроде этого:

        [% INCLUDE section/header
           title = 'Quantum Mechanics'
        %]
           Quantum mechanics is a very interesting subject wish
           should prove easy for the layman to fully comprehend.
        [% INCLUDE section/footer %]
        [% INCLUDE section/header
           title = 'Desktop Nuclear Fusion for under $50'
        %]
           This describes a simple device which generates significant
           sustainable electrical power from common tap water by process
           of nuclear fusion.
        [% INCLUDE section/footer %]

    Сами включаемые компоненты-шаблоны могут выглядеть, например, так:

    section/header:

        <p>
        <h2>[% title %]</h2>

    section/footer:

        </p>

    Директива WRAPPER позволяет это немного упростить. Она выделяет блок до соответствующей директивы END. В первую очередь обрабатывается содержимое этого блока, а затем выходной поток (в виде переменной 'content') передается шаблону или директиве BLOCK, чье имя указано в качестве первого параметра WRAPPER.

        [% WRAPPER section
           title = 'Quantum Mechanics'
        %]
           Quantum mechanics is a very interesting subject wish
           should prove easy for the layman to fully comprehend.
        [% END %]
        [% WRAPPER section
           title = 'Desktop Nuclear Fusion for under $50'
        %]
           This describes a simple device which generates significant
           sustainable electrical power from common tap water by process
           of nuclear fusion.
        [% END %]

    Простой шаблон 'section' можно определить следующим образом:

        <p>
        <h2>[% title %]</h2>
        [% content %]
        </p>

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

        [% INSERT legalese.txt WRAPPER big_bold_table %]

    Кроме того можно передать директиве WRAPPER несколько шаблонов. Порядок имен шаблонов определяет порядок применения оберток от внешних к внутренним. Например, если определены следующие шаблоны-блоки:

        [% BLOCK bold   %]<b>[% content %]</b>[% END %]
        [% BLOCK italic %]<i>[% content %]</i>[% END %]

    директива

        [% WRAPPER bold+italic %]Hello World[% END %]

    сгенерирует следующий вывод:

        <b><i>Hello World</i></b>
     
  • BLOCK

    Конструкция BLOCK ... END может быть использована для определния шаблона компонента, который может быть обработан директивами INCLUDE, PROCESS и WRAPPER.

        [% BLOCK tabrow %]
        <tr><td>[% name %]<td><td>[% email %]</td></tr>
        [% END %]
        <table>
        [% PROCESS tabrow  name='Fred'  email='fred@nowhere.com' %]
        [% PROCESS tabrow  name='Alan'  email='alan@nowhere.com' %]
        </table>

    Определение BLOCK можно использовать до того, как оно будет явно определенно, если определение находится в том же файле. Само определение блока не генерирует никакого вывода.

        [% PROCESS tmpblk %]
        [% BLOCK tmpblk %] This is OK [% END %]

    Анонимный BLOCK можно использовать для захвата вывода фрагмента шаблона.

        [% julius = BLOCK %]
           And Caesar's spirit, ranging for revenge,
           With Ate by his side come hot from hell,
           Shall in these confines with a monarch's voice
           Cry  'Havoc', and let slip the dogs of war;
           That this foul deed shall smell above the earth
           With carrion men, groaning for burial.
        [% END %]

    Как и именованный блок, он может содержать любые директивы шаблона, которые обрабатываются в момент определения блока. Вывод генерируется в момент присвоения переменной 'julius'.

    Анонимные BLOCK можно также использовать для определения блочных макросов. Выделенный блок обрабатывается каждый раз, когда вызывается макрос.

        [% MACRO locate BLOCK %]
           The [% animal %] sat on the [% place %].
        [% END %]
        [% locate(animal='cat', place='mat') %]    # The cat sat on the mat
        [% locate(animal='dog', place='log') %]    # The dog sat on the log

Условная обработка

  • IF / UNLESS / ELSIF / ELSE

    Директивы IF и UNLESS позволяют обрабатывать (или пропускать) блоки на основании выполнения некоторого условия.

        [% IF frames %]
           [% INCLUDE frameset %]
        [% END %]
        [% UNLESS text_mode %]
            INCLUDE biglogo %]
        [% END %]

    Несколько условий можно связать с помощью блоков ELSIF и/или ELSE.

        [% IF age < 10 %]
           Hello [% name %], does your mother know you're
           using her AOL account?
        [% ELSIF age < 18 %]
           Sorry, you'rre not old enough to enter
           (and too dumb to lie about your age)
        [% ELSE %]
           Welcome [% name %].
        [% END %]

    Можно использовать следующие условные и логические операторы:

        == != < <= > >= && || ! and or not

    Обратите внимание, что операторы 'and', 'or' и 'not' являются синонимами для '&&', '||' и '!', соответственно.

    Условия могут быть сть составными и вычисляются с такими же приоритетами, как и в Perl. Для точного определения порядка вычисления используются скобки.

        # надуманный пример составного условия
        [% IF (name == 'admin' || uid <= 0) && mode == 'debug' %]
           I'm confused.
        [% ELSIF more > less %]
           That's more or less correct.
        [% END %]
  • SWITCH / CASE

    Конструкцию SWITCH / CASE можно использовать для выбора из множества вариантов. В директиве SWITCH указывается выражение, которое вначале вычисляется, а затем результат сравнивается по очереди с выражениями в CASE. Каждая директива CASE может содержать одно или список выражений, с которыми будет проводиться сравнение. Также можно оставить CASE пустой, либо записать [% CASE DEFAULT %] для того, чтобы определить выбор по умолчанию. Обрабатывается только одно совпадение CASE. Сквозной проход через все выражения CASE не поддерживается.

        [% SWITCH myvar %]
        [% CASE value1 %]
           ...
        [% CASE [ value2 value3 ] %]   # множественные значения
           ...
        [% CASE myhash.keys %]         # то же самое
           ...
        [% CASE %]                     # по умолчанию
           ...
        [% END %]
      

Циклическая обработка

  • FOREACH

    Директива FOREACH циклически перебирает все значения массива, обрабатывая блок для каждого из них.

        my $vars = {
    	foo   => 'Foo',
      	items => [ 'one', 'two', 'three' ],
        };

    Шаблон:

        Things:
        [% FOREACH thing = [ foo 'Bar' "$foo Baz" ] %]
           * [% thing %]
        [% END %]
    
        Items:
        [% FOREACH i = items %]
           * [% i %]
        [% END %]
    
        Stuff:
        [% stuff = [ foo "$foo Bar" ] %]
        [% FOREACH s = stuff %]
           * [% s %]
        [% END %]

    Вывод:

        Things:
          * Foo
          * Bar
          * Foo Baz
    
        Items:
          * one
          * two
          * three
    
        Stuff:
          * Foo
          * Foo Bar

    Вместо '=' можно использовать 'IN'.

        [% FOREACH crook IN government %]

    Когда директива FOREACH используется без указания переменной цикла, все обрабатываемые элементы, являющиеся ссылками на хэш, будут автоматически импортированы.

        [% userlist = [
    	  { id => 'tom',   name => 'Thomas'  },
    	  { id => 'dick',  name => 'Richard'  },
    	  { id => 'larry', name => 'Lawrence' },
           ]
        %]
        [% FOREACH user IN userlist %]
           [% user.id %] [% user.name %]
        [% END %]

    Краткая запись:

        [% FOREACH userlist %]
           [% id %] [% name %]
        [% END %]

    Имейте в виду, что этот особый способ использования создает локализованное окружение переменных, чтобы избежать случайного перезаписывания существующих переменных значениями импортированных ключей хэша. Все импортированные определения и все другие переменные, определенные в таком цикле FOREACH, будут потеряны в конце цикла, когда будут восстановлены предыдущие окружение и значения переменных.

    Несмотря на это, в случае обычной операции, переменная цикла остается в текущей области действия переменных после завершения цикла FOREACH (предостережение: перезаписывая при этом любую существовавшую ранее одноименную переменную). Это может пригодится, так как переменная цикла является объектом-итератором (смотри ниже) и может быть использована для анализа последнего элемента обработанного циклом.

    Директива FOREACH также может быть использована для перебора элементов хэша. Каждый элемент хэша возвращается по порядку (определяемому сортировкой по ключам хэша) в виде хэша с элементами 'key' и 'value'.

        [% users = {
    	 tom   => 'Thomas',
    	 dick  => 'Richard',
             larry => 'Lawrence',
           }
        %]
        [% FOREACH u IN users %]
           * [% u.key %] : [% u.value %]
        [% END %]

    Выход:

           * dick : Richard
           * larry : Lawrence
           * tom : Thomas      

    Директива NEXT начинает следующий проход цикла FOREACH.

        [% FOREACH user IN userlist %]
           [% NEXT IF user.isguest %]
           Name: [% user.name %]    Email: [% user.email %]
        [% END %]

    Директива LAST используется для преждевременного завершения цикла. BREAK используется в качестве синонима LAST.

        [% FOREACH match IN results.nsort('score').reverse %]
           [% LAST IF match.score < 50 %]
           [% match.score %] : [% match.url %]
        [% END %]

    Директива FOREACH реализована с помощью модуля Template::Iterator. Ссылка на объект-итератор для директивы FOREACH неявно доступна через переменную 'loop'. С помощью итератора 'loop' можно вызвать следующие методы:

        size()	количество элементов в массиве
        max()       индекс последнего элемента массива (size - 1)
        index()     индекс текущей итерации от 0 до max()
        count()     счетчик итераций от 1 до size() (т.е. index() + 1)
        first()     истина, если текущая итерация - первая в цикле
        last()      истина, если текущая итерация - последняя в цикле
        prev()      возвращает предыдущий элемент массива
        next()      возвращает следующий элемент массива

    Подробнее смотрите Template::Iterator.

    Пример:

        [% FOREACH item IN [ 'foo', 'bar', 'baz' ] -%]
           [%- "<ul>\n" IF loop.first %]
           <li>[% loop.count %]/[% loop.size %]: [% item %]
           [%- "</ul>\n" IF loop.last %]
        [% END %]

    Вывод:

        <ul>
        <li>1/3: foo
        <li>2/3: bar
        <li>3/3: baz
        </ul>

    Обратите внимание, что в качестве синонима для count() поддерживается метод number(). Это сделано для обратной совместимости, но может быть исключено в будущих версиях.

    Вложенные циклы будут работать как ожидается, переменная 'loop' корректно ссылается на итератор текущего цикла и после его завершения восстанавливается в прежнее состояние (т.е. в состояние внешнего цикла).

        [% FOREACH group IN grouplist;
    	   # loop => group iterator
               "Groups:\n" IF loop.first;
               FOREACH user IN group.userlist;
                   # loop => user iterator
    	       "$loop.count: $user.name\n";
    	   END;
    	   # loop => group iterator
               "End of Groups\n" IF loop.last;
           END
        %]

    Для явного создания объекта итератора можно использовать плагин 'iterator'. Это может понадобиться внутри вложенных циклов, в которых требуется сохранить ссылку на внешний итератор. Плагин iterator позволяет создавать итераторы с именем, отличным от 'loop'. Подробнее смотрите Template::Plugin::Iterator.

        [% USE giter = iterator(grouplist) %]
        [% FOREACH group IN giter %]
           [% FOREACH user IN group.userlist %]
                 user #[% loop.count %] in
                 group [% giter.count %] is
                 named [% user.name %]
           [% END %]
        [% END %]
  • WHILE

    Директиву WHILE можно использовать для повторного выполнения блока-шаблона до тех пор, пока выполняется (т.е. истинно) условное выражение. Как и в IF / UNLESS, выражение может быть составным.

        [% WHILE total < 100 %]
           ...
           [% total = calculate_new_total %]
        [% END %]

    Для вычисления присваиваемого значения, операцию присвоения можно взять в скобки.

        [% WHILE (user = get_next_user_record) %]
           [% user.name %]
        [% END %]

    Так же как в FOREACH, для начала следующей итерации можно использовать директиву NEXT, а для завершения цикла - BREAK.

    Template Toolkit использует надежный счетчик, для того чтобы избежать зацикливания циклов WHILE. Если цикл превысит 1000 итераций, будет сгенерировано исключение со следующим сообщением об ошибке:

        WHILE loop terminated (> 1000 iterations)

    При необходимости можно установить большее значение переменной $Template::Directive::WHILE_MAX, которая позволяет контролировать этот механизм.

Фильтры, плагины, макросы и Perl

  • FILTER

    Директива FILTER может использоваться для пост-обработки вывода блока. Ряд стандартных фильтров поставляется в составе Template Toolkit. Например, фильтр 'html' преобразует специальные символы '<', '>' и '&' в сущности HTML, чтобы предотвратить их интерпретацию как тегов HTML и маркера сущности.

        [% FILTER html %]
           HTML text may have < and > characters embedded
           which you want converted to the correct HTML entities.
        [% END %]

    Вывод:

           HTML text may have &lt; and &gt;p;gt; characters embedded
           which you want converted to the correct HTML entities.

    Директива FILTER может следовать за некоторыми неблочными директивами. Например:

        [% INCLUDE mytext FILTER html %]

    В качестве синонима 'FILTER' можно использоввать символ '|'.

        [% INCLUDE mytext | html %]

    Несколько фильтров можно связать в цепочку. Вызываться они будут по порядку.

        [% INCLUDE mytext FILTER html FILTER html_para %]

    или

        [% INCLUDE mytext | html | html_para %]

    Существует два вида фильтров: 'статические' или 'динамические'. Статический фильтр это обычная функция, которая принимает текст в качестве единственного параметра и возвращает модифицированный текст. Примером статического фильтра служит фильтр 'html', реализованный следующим образом:

        sub html_filter {
    	my $text =text = shift;
        	for ($text) {
    	    s/&/&amp;/g;
    	    s/</&lt;/g;
    	    s/>/&gt;/g;
        	}
    	return $text;
        }

    Динамические фильтры могут принимать дополнительные аргументы, которые указываются при вызове в шаблоне. Примером такого фильтра является 'repeat', которому передается числовой аргумент аргумент, указывающий сколько раз требуется повторить входной текст.

        [% FILTER repeat(3) %]blah [% END %]

    Выход:

        blah blah blah

    Это реализовано с помощью 'фабрик' фильтров (filter 'factories'). Производящей функции (фабрике) передается ссылка на текущий объект Template::Context вместе со всеми дополнительными аргументами. Функция должна вернуть ссылку на функцию, которая реализует фильтр. Фабрика для фильтра 'repeat' может быть реализована следующим образом:

        sub repeat_filter_factory {
    	my ($context, $iter) = @_;
    	$iter = 1 unless defined $iter;
    	return sub {
    	    my $text = shift;
    	    $text = '' unless defined $text;
    	    return join('\n', $text) x $iter;
    	}
        }

    Опция FILTERS, описанная в Template::Manual::Config, позволяет определять собственные фильтры в момент создания объекта Template. Метод Template::Context define_filter() позволяет определять дополнительные фильтры в любое время.

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

        [% FILTER echo = repeat(2) %]
        Is there anybody out there?
        [% END %]
        [% FILTER echo %]
        Mother, should I build a wall?
        [% END %]

    Выход:

        Is there anybody out there?
        Is there anybody out there?
        Mother, should I build a wall?
        Mother, should I build a wall?

    Директива FILTER автоматически заключает в кавычки название фильтра. Как и в случае с INCLUDE, также можно указать имя через переменную с префиксом '$'.

        [% myfilter = 'html' %]
        [% FILTER $myfilter %]	# same as [% FILTER html %]
             ...
        [% END %]

    Переменные шаблона также можно использовать для определения функций статических фильтров. Template Toolkit автоматически вызовет связанную с переменной функцию и будет использовать возвращенный результат. Таким образом, приведенный выше пример можно реализовать следующим образом:

        my $vars = {
    	myfilter => sub { return 'html' },
        };

    Шаблон:

        [% FILTER $myfilter %]	# то же самое что [% FILTER html %]
           ...
        [% END %]

    Чтобы определить переменную шаблона, которая определяет ссылку на функцию, которая может быть использпользована директивой FILTER, необходимо создать такую функцию, которая вернет Template Toolkit ссылку на другую функцию, которая затем будет использоваться в качестве фильтра. Имейте в виду, что таким образом можно реализовать только статические фильтры.

        my $vars = {
    	myfilter => sub { \&my_filter_sub },
        };
        sub my_filter_sub {
    	my $text = shift;
    	# do something
            return $text;
        }

    Шаблон:

        [% FILTER $myfilter %]
           ...
        [% END %]

    Другой способ - это связывание (bless) ссылки на функцию с классом (подойдет любой класс), для того чтобы заставить Template Toolkit считать, что он имеет дело с объектом, а не с функцией. Этот трюк позволяет чудесным образом обойти автоматическое "вызвать-функцию-получить-значение"

        my $vars = {
    	myfilter => bless(\&my_filter_sub, 'anything_you_like'),
        };

    Шаблон:

        [% FILTER $myfilter %]
           ...
        [% END %]

    Фильтры, связанные с переменными шаблона, остаются локальными в том окружении переменных, в котором они были определены. То есть, если вы определите фильтр в блоке PERL внутри шаблона, который загружается через INCLUDE, то определение фильтра будет действовать только до конца этого шаблона, до того момента, когда хранилище (stash) будет делокализовано и востановлено предыдущее состояние переменных. Если вы хотите создать фильтр, живущий все время работы процессора, или определить дополнительные фабрики динамических фильтров, вам нужно вызвать метод define_filter() текущего объекта Template::Context.

    Полный список доступных фильтров, их описания и примеры использования приведены в Template::Manual::Filters.

  • USE

    Директива USE используется для загрузки и инициализации модулей расширения ("плагинов").

        [% USE myplugin %]

    Плагин - это обычный модуль Perl, соответсвующий специальному объектно-ориентированному интерфейсу, позволяющему Template Toolkit автоматически его загружать и использовать. Детальное описание и информация о написании плагинов содержатся в Template::Plugin.

    Имена плагинов чувствительны к регистру и должны быть добавлены к значению PLUGIN_BASE (по умолчанию: 'Template::Plugin'), для того чтобы составить полное имя модуля. Все точки, '.', в имени будут преобразованы в '::'.

        [% USE MyPlugin %]     #  => Template::Plugin::MyPlugin
        [% USE Foo.Bar  %]     #  => Template::Plugin::Foo::Bar

    Некоторые стандартные плагины включены в пакет Template Toolkit (смотрите ниже и Template::Manual::Plugins). Их можно указывать в нижнем регистре, поскольку они отображаются на соответствующие имена.

        [% USE cgi   %]        # => Template::Plugin::CGI
        [% USE table %]        # => Template::Plugin::Table

    Дополнительные параметры указанные в скобках после имени плагина передаются конструктору new(). В качестве первого параметра всегда передается ссылка на текущий объект Template::Context.

        [% USE MyPlugin('foo', 123) %]

    эквивалентно:

        Template::Plugin::MyPlugin->new($context, 'foo', 123);

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

        [% USE url('/cgi-bin/foo', mode='submit', debug=1) %]

    эквивалентно:

        Template::Plugin::URL->new($context, '/cgi-bin/foo'
    			       { mode => 'submit', debug => 1 });

    Плагин может представлять любой тип данных; простую переменную, хэш, массив или ссылку на код, но в общем случае это ссылка на объект. У объекта можно обычным способом вызывать методы (или получать соответствующие элементы специфического типа данных):

        [% USE table(mydata, rows=3) %]
        [% FOREACH row = table.rows %]
           <tr>
           [% FOREACH item = row %]
    	  <td>[% item %]</td>
           [% END %]
           </tr>
        [% END %]

    Плагину можно указать альтернативное имя, по которому потом можно на него ссылаться:

        [% USE scores = table(myscores, cols=5) %]
        [% FOREACH row = scores.rows %]
           ...
        [% END %]

    Этот подход можно использовать, чтобы создать несколько объектов плагина с различными конфигурациями. Следующий пример показывает, как с помощью плагина 'format' создать функции, связанные с переменными, для форматирования текста согласно printf().

        [% USE bold = format('<b>%s</b>') %]
        [% USE ital = format('<i>%s</i>') %]
        [% bold('This is bold')   %]
        [% ital('This is italic') %]

    Вывод:

        <b>This is bold</b>
        <i>This is italic</i>

    Следующий пример показывает, как использовать плагин URL для создания динамических URL из базовой части и необязательных параметров запроса.

        [% USE mycgi = URL('/cgi-bin/foo.pl', debug=1) %]
        <a href="[% mycgi %]">...
        <a href="[% mycgi(mode='submit') %]"...

    Вывод:

        <a href="/cgi-bin/foo.pl?debug=1">...
        <a href="/cgi-bin/foo.pl?mode=submit&debug=1">...

    Плагин CGI является примером плагина, делегирующего методы другого модуля Perl. В данном случае, модуля Линкольна Стейна (Lincoln Stein) CGI.pm. Все методы, предоставляемые CGI.pm, доступны через плагин.

        [% USE CGI %]
        [% CGI.start_form %]
        [% CGI.checkbox_group(name   =>   'colours',
                              values => [ 'red' 'green' 'blue' ])
        %]
        [% CGI.popup_menu(name   =>   'items',
                          values => [ 'foo' 'bar' 'baz' ])
        %]
        [% CGI.end_form %]

    Симон Мэттьюс (Simon Matthews) написал плагин DBI, который предоставляет интерфейс к модулю DBI Тима Банса (Tim Bunce), который доступен на CPAN. Ниже короткий пример:

        [% USE DBI('DBI:mSQL:mydbname') %]
        [% FOREACH user = DBI.query('SELECT * FROM users') %]
           [% user.id %] [% user.name %] [% user.etc.etc %]
        [% END %]

    Более подробная информация о плагинах, поставляемых в составе пакета или доступных на CPAN, приводится в Template::Manual::Plugins.

    Опция LOAD_PERL (запрещенная по умолчанию) предоставляет еще один способ загрузки внешнего модуля Perl. Если обычный модуль Perl (т.е. не Template::Plugin::* или другой модуль, находящийся в дереве одного из путей, указанных в PLUGIN_BASE) поддерживает объектно-ориентированный интерфейс и конструктор new(), то можно автоматически его загрузить и инициализировать (создать экземпляр объекта). Следующий простейший пример показывает как можно использовать модуль IO::File.

        [% USE file = IO.File('/tmp/mydata') %]
        [% WHILE (line = file.getline) %]
           <!-- [% line %] -->
        [% END %]
  • MACRO

    Директива MACRO позволяет определить директиву или блок директив, которые будут вычисляться при вызове макроса.

        [% MACRO header INCLUDE header %]

    После этого вызов макроса:

        [% header %]

    будет эквивалентен:

        [% INCLUDE header %]

    Макросам можно передавать при вызове именованные параметры. Значения этих параметров будут локальными внутри макроса.

        [% header(title='Hello World') %]  

    эквивалентно:

        [% INCLUDE header title='Hello World' %]

    В определении MACRO можно указать имена параметров. Значения, передаваемые макросу, затем связываются с этими локальными переменными. Следом можно указать другие именованные параметры.

        [% MACRO header(title) INCLUDE header %]
        [% header('Hello World') %]
        [% header('Hello World', bgcol='#123456') %]

    эквивалентно:

        [% INCLUDE header title='Hello World' %]
        [% INCLUDE header title='Hello World' bgcol='#123456' %]

    Ниже другой пример определения макроса для вывода номеров группами по 3 цифры, разделенных запятой, с использованием виртуальных методов chunk и join.

        [% MACRO number(n) GET n.chunk(-3).join(',') %]
        [% number(1234567) %]    # 1,234,567

    MACRO может предшествовать любой директиве и должна соответствовать структуре директивы.

        [% MACRO header IF frames %]
           [% INCLUDE frames/header %]
        [% ELSE %]
           [% INCLUDE header %]
        [% END %]
        [% header %]

    Можно определить MACRO как анонимный BLOCK. Этот блок будет вычисляться каждый раз при вызове макроса.

        [% MACRO header BLOCK %]
           ...content...
        [% END %]
        [% header %]

    Если включена опция EVAL_PERL, можно даже определить MACRO как блок PERL (смотрите ниже):

        [% MACRO triple(n) PERL %]
             my $n = $stash->get('n');
             print $n * 3;
        [% END -%]
  • PERL

    (для продвинутых читателей)

    Директива PERL используется для выделения блока, содержащего код Perl для выполнения. Для того чтобы код исполнялся, опция EVAL_PERL должна быть установлена, в противном случае будет возбуждено исключение 'perl' с сообщением 'EVAL_PERL not set'.

    Код Perl вычисляется в пакете Template::Perl. Переменная пакета $context содержит ссылку на текущий объект Template::Context. Это можно использовать для получения возможностей Template Toolkit по обработке других шаблонов, загрузки плагинов, фильтров и т.д. Более подробно смотрите Template::Context.

        [% PERL %]
           print $context->include('myfile');
        [% END %]

    Переменная $stash содержит ссылку на объект-хранилище верхнего уровня, который управляет переменными шаблона. Через него можно получать и устанавливать значения переменных. Более подробно смотрите Template::Stash.

        [% PERL %]
           $stash->set(foo => 'bar');
           print "foo value: ", $stash->get('foo');
        [% END %]

    Вывод: foo value: bar

    Вывод генерируется из блока PERL посредством вызова print(). Имейте в виду, что вместо STDOUT используется дескриптор Template::Perl::PERLOUT (связанный с буфером вывода).

        [% PERL %]
           print "foo\n";	                        # OK
           print PERLOUT "bar\n";                   # OK, тоже самое
           print Template::Perl::PERLOUT "baz\n";   # OK, тоже самое
           print STDOUT "qux\n";			# НЕПРАВИЛЬНО!
        [% END %]

    Блок PERL может содержать другие директивы шаблонов. Они вычисляются до того, как исполняется код Perl.

        [% name = 'Fred Smith' %]
        [% PERL %]
           print "[% name %]\n";
        [% END %]

    Следовательно, код в приведенном выше примере исполняется в таком виде:

        print "Fred Smith\n";

    Внутри блоков PERL можно генерировать исключения с помощью die() и они будут корректно перехвачены окружающими блоками TRY.

        [% TRY %]
           [% PERL %]
              die "nothing to live for\n";
           [% END %]
        [% CATCH %]
           error: [% error.info %]
        [% END %]

    Вывод: error: nothing to live for

  • RAWPERL

    (для продвинутых читателей)

    Парсер Template Toolkit разбирает исходный текст шаблона и генерирует в виде текста функцию Perl. Затем он использует eval() для преобразования текста в ссылку на функцию. Затем при обработке шаблона эта функция вызывается. В качестве параметра ей передается текущий объект Template::Context, через который можно получить доступ ко всем возможностям Template Toolkit. Ссылка на функцию может быть закеширована, что позволяет обрабатывать шаблон несколько раз без необходимости повторного разбора.

    Например, такой шаблон:

        [% PROCESS header %]
        The [% animal %] sat on the [% location %]
        [% PROCESS footer %]

    преобразуется в следущее определение функции Perl:

        sub {
        	my $context = shift;
        	my $stash   = $context->stash;
        	my $output  = '';
        	my $error;
    
        	eval { BLOCK: {
        	    $output .=  $context->process('header');
        	    $output .=  "The ";
        	    $output .=  $stash->get('animal');
        	    $output .=  " sat on the ";
        	    $output .=  $stash->get('location');
        	    $output .=  $context->process('footer');
        	    $output .=  "\n";
        	} };
        	if ($@) {
        	    $error = $context->catch($@, \$output);
        	    die $error unless $error->type eq 'return';
        	}
    
        	return $output;
        }

    Для изучения сгенерированного кода Perl, как в приведенном выше примере, установите переменную пакета $Template::Parser::DEBUG в любое истинное значение. Также можно установить в истинное значение переменную $Template::Directive::PRETTY, чтобы код был отформатирован в удобном для чтения человеком виде. При компиляции (т.е. при первом использовании шаблона) исходный код каждой сгенерированной функции будет отправлен на STDERR.

        $Template::Parser::DEBUG = 1;
        $Template::Directive::PRETTY = 1;
        ...
        $template->process($file, $vars)
    	|| die $template->error(), "\n";

    Конструкция PERL ... END позволяет внедрять код Perl в шаблон (при установленной опции EVAL_PERL), но он вычисляется во "время исполнения", используя eval() каждый раз, когда вызывается функция шаблона. Это, в общем, гибко, но не настолько эффективно, как могло бы быть, особенно при использовании в постоянной серверной среде, где шаблон может быть обработан много раз.

    Директива RAWPERL позволяет писать код Perl, который будет прямо интегрирован в текст сгенерированной функции Perl. Он вычисляется один раз во время компиляции и сохраняется в закешированном виде как часть скомпилированной функции шаблона. Это делает блоки RAWPERL более эффективными по сравнению с блоками PERL.

    Обратная сторона этого заключается в том, что вам нужно писать код гораздо ближе к основе. Внутри блоков PERL, можно использовать print() для генерации вывода. Блоки RAWPERL не предоставляют таких удобств. Код вставляется непосредственно в текст сгенерированной функции и должен следовать соглашению о добавлении к переменной '$output'.

        [% PROCESS  header %]
        [% RAWPERL %]
           $output .= "Some output\n";
           ...
           $output .= "Some more output\n";
        [% END %]

    Критическая часть сгенерированной для этого примера функции будет выглядеть примерно так:

        ...
        eval { BLOCK: {
    	$output .=  $context->process('header');
            $output .=  "\n";
            $output .= "Some output\n";
    	...
            $output .= "Some more output\n";
            $output .=  "\n";
        } };
        ...

    Как и для блоков PERL, ссылки $context и $stash предопределены и доступны для использования внутри кода RAWPERL.

Обработка исключений и управление потоками

  • TRY / THROW / CATCH / FINAL

    (для углубленного изучения)

    Template Toolkit поддерживает полнофункциональную, многоуровневую обработку исключений. Директива TRY открывает блок обработки исключений, который продолжается до соответствующей директивы END. Любые ошибки, произошедшие внутри этого блока, будут перехвачены, и могут быть обработаны одним из определенных блоков CATCH.

        [% TRY %]
           ...blah...blah...
           [% CALL somecode %]
           ...etc...
           [% INCLUDE someblock %]
           ...and so on...
        [% CATCH %]
           An error occurred!
        [% END %]

    Ошибки возбуждаются как исключения (объекты класса Template::Exception) и содержат два поля: 'type' и 'info'. Исключение 'type' может быть любой строкой, содержащей латинские буквы, числа, '_' или '.', и используется для определения типа произошедшей ошибки. Поле 'info' содержит сообщение об ошибке, обозначающее, что на самом деле произошло не так. Внутри блока-перехватчика (CATCH), объект исключения доступен как переменная 'error'. Вы можете обратиться к полям 'type' и 'info' напрямую.

        [% mydsn = 'dbi:MySQL:foobar' %]
        ...
        [% TRY %]
           [% USE DBI(mydsn) %]
        [% CATCH %]
           ERROR! Type: [% error.type %]
                  Info: [% error.info %]
        [% END %]

    Вывод (предполагается, что несуществующая база данных называется 'foobar'):

        ERROR!  Type: DBI
                Info: Unknown database "foobar"

    Также можно использовать саму переменную 'error' и на выходе получить строку вида "$type error - $info".

        ...
        [% CATCH %]
        ERROR: [% error %]
        [% END %]

    Вывод:

        ERROR: DBI error - Unknown database "foobar"

    Каждому блоку CATCH можно указать определенный тип исключения, который он будет обрабатывать. Для обработки различных типов исключений, которые могут быть возбуждены в блоке TRY, можно использовать несколько блоков CATCH. Блок CATCH без указания типа, как в предыдущем примере, является обработчиком по умолчанию, и будет перехватывать все неперехваченные другими блоками исключения. Можно также определить этот блок как [% CATCH DEFAULT %].

        [% TRY %]
           [% INCLUDE myfile %]
           [% USE DBI(mydsn) %]
           [% CALL somecode %]
           ...
        [% CATCH file %]
           File Error! [% error.info %]
        [% CATCH DBI %]
           [% INCLUDE database/error.html %]
        [% CATCH %]
           [% error %]
        [% END %]

    Не забывайте, что вы можете записывать несколько директив внутри одной пары тегов, разделяя их ';'. Таким образом, простые блоки CATCH можно записать более кратко:

        [% TRY %]
           ...
        [% CATCH file; "File Error! $error.info" %]
        [% CATCH DBI;  INCLUDE database/error.html %]
        [% CATCH; error %]
        [% END %]

    или даже:

        [% TRY %]
           ...
        [% CATCH file ;
               "File Error! $error.info" ;
           CATCH DBI ;
    	   INCLUDE database/error.html ;
           CATCH ;
    	   error ;
           END
        %]

    Как вы уже догадались, плагин DBI возбуждает исключения типа 'DBI'. Другие определенные исключения, перехватываемые в примере, относятся к типу 'file'.

    Ошибка 'file' автоматически генерируется Template Toolkit, когда он не может найти, загрузить, разобрать или обработать файл, который был запрошен через директивы INCLUDE, PROCESS, INSERT или WRAPPER. Если в приведенном выше примере 'myfile' не может быть найден директива [% INCLUDE myfile %] возбудит исключение 'file', которое затем перехватывается блоком [% CATCH file %], генерируя следующий вывод:

        File Error! myfile: not found

    Имейте в виду, что опция DEFAULT (запрещенная по умолчанию) позволяет определить файл, который будет использоваться вместо любого не найденного шаблона. Это предотвратит возбуждение исключения 'file' при запросе несуществующего файла (разумеется, за исключением случая, когда не существует самого шаблона DEFAULT). Другие встретившиеся ошибки (например, ошибка чтения, ошибка разбора шаблона) возбудят исключение 'file' как и обычно.

    Неперехваченные исключения (например, если блок TRY не имеет перехватчика этого типа или перехватчика по умолчанию) могут быть обработаны окружающими блоками TRY, которые могут иметь неограниченную вложенность в пределах нескольких шаблонов. Если ошибка не перехвачена ни на одном из уровней, обработка шаблона завершается и метод Template process() возвращает ложное значение. Вызов метода error() возвращает подходящий объект Template::Exception.

        [% TRY %]
           ...
           [% TRY %]
              [% INCLUDE $user.header %]
           [% CATCH file %]
    	  [% INCLUDE header %]
           [% END %]
           ...
        [% CATCH DBI %]
           [% INCLUDE database/error.html %]
        [% END %]

    В этом примере внутренний блок TRY используется, чтобы гарантировать правильную работу первой директивы INCLUDE. Мы используем переменную user.header для определения имени включаемого шаблона, и она может содержать имя несуществующего файла, или, возможно, этот шаблон содержит неправильные директивы. Если INCLUDE выполняется неудачно с ошибкой 'file', то CATCH во внутреннем блоке ее перехватит, и включит вместо ошибочного шаблона файл по умолчанию 'header'. Любая ошибка DBI внутри внешнего блока TRY будет перехвачена в соответсвующем блоке CATCH, и вызовет обработку шаблона 'database/error.html'. Учтите, что включаемые шаблоны наследуют все определенные в данный момент переменные, следовательно эти файлы ошибок вполне спокойно могут получить информацию о только что перехваченном исключении через переменную 'error'. Например:

    'database/error.html':

        <h2>Database Error</h2>
        A database error has occurred: [% error.info %]

    Дополнительно вы можете определить блок FINAL. Он всегда обрабатывается вне зависимости от результат обработки блоков TRY и/или CATCH. Если исключение не перехвачено, блок FINAL обрабатывается до перехода в окуржающий блок или возврата в вызывающую процедуру.

        [% TRY %]
           ...
        [% CATCH this %]
           ...
        [% CATCH that %]
           ...
        [% FINAL %]
           All done!
        [% END %]

    Вывод блока TRY остается невредимым до точки, где произошло исключение. Например, этот шаблон:

        [% TRY %]
           This gets printed
           [% THROW food 'carrots' %]
           This doesn't
        [% CATCH food %]
           culinary delights: [% error.info %]
        [% END %]    

    сгенерирует следующий вывод:

        This gets printed
        culinary delights: carrots

    Директиву CLEAR можно использовать в блоках CATCH или FINAL для очистки всего вывода, созданного блоком TRY.

        [% TRY %]
           This gets printed
           [% THROW food 'carrots' %]
           This doesn't
        [% CATCH food %]
           [% CLEAR %]
           culinary delights: [% error.info %]
        [% END %]    

    Вывод:

        culinary delights: carrots

    Типы исключений имеют иерархическую структуру, каждый уровень отделяется с помощью знакомого оператора-точки '.'. Исключение 'DBI.connect' - это более специфичный вид ошибки 'DBI'. Аналогично, 'myown.error.barf' - это более определенный вид типа 'myown.error', который, в свою очередь, является подвидом ошибки 'myown'. Обработчик CATCH, указывающий на более общий тип исключения (такой как 'DBI' или 'myown.error'), будет также перехватывать соответствующие подвиды, пока для них не будет определен свой обработчик. Имейте в виду, что порядок определения обработчиков CATCH не имеет значения; более специфичный перехватчик всегда будет иметь приоритет перед более общим и перехватчиком по умолчанию.

        [% TRY %]
           ...
        [% CATCH DBI ;
             INCLUDE database/error.html ;
           CATCH DBI.connect ;
             INCLUDE database/connect.html ;
           CATCH ;
             INCLUDE error.html ;
           END
        %]

    В этом примере ошибка 'DBI.connect' имеет собственный обработчик, более общий блок 'DBI' используется для всех остальных DBI или DBI.* ошибок, и перехватчик по умолчанию обрабатывает все остальное.

    Возбудить исключение внутри шаблона можно с помощью директивы THROW. Первый параметр - тип исключения, который не нужно заключать в кавычки (но можно, как и в INCLUDE), следом идет соответствующее сообщение об ошибке, которое может быть любым допустимым выражением: строкой в кавычках, переменной и т.д.

        [% THROW food "Missing ingredients: $recipe.error" %]
        [% THROW user.login 'no user id: please login' %]
        [% THROW $myerror.type "My Error: $myerror.info" %]

    Кроме того директиве THROW можно передать дополнительные или именованные параметры, если вы хотите передать через поле ошибки 'info' больше, чем простое текстовое сообщение.

        [% THROW food 'eggs' 'flour' msg='Missing Ingredients' %]

    В этом случае поле ошибки 'info' будет хешем, содержащим именнованные параметры, в нашем случае 'msg' => 'Missing Ingredients', и элемент 'args', содержащий список дополнительных аргументов, в нашем случае 'eggs' и 'flour'. Поле ошибки 'type' остается без изменений, здесь оно устанавливается в 'food'.

        [% CATCH food %]
           [% error.info.msg %]
           [% FOREACH item = error.info.args %]
              * [% item %]
           [% END %]
        [% END %]

    Полученный вывод:

           Missing Ingredients
             * eggs
             * flour

    В дополнение к определению дополнительных аргументов как элементов массива args [% error.info.args.n %], хеш 'info' содержит в качестве удобных ссылок ключи, прямо указывающие на дополнительные аргументы.

        [% error.info.0 %]   # тоже самое что и [% error.info.args.0 %]

    Исключения также можно возбуждать из кода Perl, который связан с переменными шаблона или определен как плагин или другое раширение. Для того чтобы возбудить исключение, вызовите die() с ссылкой на объект Template::Exception в качестве аргумента. Затем это исключение будет перехвачено любым из блоков TRY, окружающих место, в котором был вызван этот код.

        use Template::Exception;
        ...
        my $vars = {
    	foo => sub {
    	    # ... do something ...
    	    die Template::Exception->new('myerr.naughty',
    					 'Bad, bad error');
        	},
        };

    Шаблон:

        [% TRY %]
           ...
           [% foo %]
           ...
        [% CATCH myerr ;
             "Error: $error" ;
           END
        %]

    Вывод:

        Error: myerr.naughty error - Bad, bad error

    При необходимости можно также определить поле 'info' как ссылку на другой объект или структуру данных.

        die Template::Exception->new('myerror', {
    	module => 'foo.pl',
    	errors => [ 'bad permissions', 'naughty boy' ],
        });

    В шаблоне:

        [% TRY %]
           ...
        [% CATCH myerror %]
           [% error.info.errors.size or 'no';
              error.info.errors.size == 1 ? ' error' : ' errors' %]
           in [% error.info.module %]:
              [% error.info.errors.join(', ') %].
        [% END %]

    Сгенерированный вывод:

           2 errors in foo.pl:
              bad permissions, naughty boy.

    Можно вызвать die() с одной строкой как обычно делается в большей части существующего кода Perl. Это будет автоматически преобразовано в исключение типа 'undef' (это строка 'undef', а не неопределенное значение). Если строка не завершается символом новой строки, Perl добавит знакомое сообщение " at $file line $line".

        sub foo {
    	# ... do something ...
    	die "I'm sorry, Dave, I can't do that\n";
        }

    Если вы пишете плагин или какое-нибудь расширение, которое имеет в области видимости текущий объект Template::Context (вы можете спокойно пропустить этот раздел, если это вам ни о чем не говорит), то вы также можете возбудить исключение при помощи вызова метода throw() этого объекта. В качестве аргументов вызова можно передать ссылку на объект Template::Exception, пару параметров ($type, $info), или просто строку $info, чтобы создать исключение типа 'undef'.

        $context->throw($e);	    # объект исключения
        $context->throw('Denied');	    # тип 'undef'
        $context->throw('user.passwd', 'Bad Password');
  • NEXT

    Директива NEXT используется для начала следующей итерации цикла FOREACH или WHILE.

        [% FOREACH user = userlist %]
           [% NEXT IF user.isguest %]
           Name: [% user.name %]    Email: [% user.email %]
        [% END %]
  • LAST

    Директива LAST используется для преждевременного выхода из цикла FOREACH или WHILE.

        [% FOREACH user = userlist %]
           Name: [% user.name %]    Email: [% user.email %]
           [% LAST IF some.condition %]
        [% END %]

    В качестве синонима LAST можно использовать директиву BREAK.

  • RETURN

    Директива RETURN используется для остановки обработки текущего шаблона и возврата в шаблон, из которого он был вызван, с возобновлением обработки с позиции, следующей за директивой INCLUDE, PROCESS или WRAPPER. Если вызывающего шаблона нет, метод Template process() передаст управление обратно в код, из которого он был вызван и вернет истинное значение.

        Before
        [% INCLUDE half_wit %]
        After
    
        [% BLOCK half_wit %]
        This is just half...
        [% RETURN %]
        ...a complete block
        [% END %]

    Вывод:

        Before
        This is just half...
        After
  • STOP

    Директива STOP используется для передачи процессору сигнала о необходимости остановки обработки любых шаблонов. Это плановый останов и метод Template process() вернет истинное значение в вызывающий код. Это означает, что шаблон был успешно обработан в соответствие с директивами в нем.

        [% IF something.terrible.happened %]
           [% INCLUDE fatal/error.html %]
           [% STOP %]
        [% END %]
        [% TRY %]
           [% USE DBI(mydsn) %]
           ...
        [% CATCH DBI.connect %]
           <p>Cannot connect to the database: [% error.info %]</p>
           <br>
           We apologise for the inconvenience.  The cleaning lady
           has removed the server power to plug in her vacuum cleaner.
           Please try again later.
           </p>
           [% INCLUDE footer %]
           [% STOP %]
        [% END %]
  • CLEAR

    Директива CLEAR используется для очистки буфера вывода текущего блока. Чаще всего она используется для очистки вывода, сгенерированного внутри блока TRY до момента возникновения исключения.

        [% TRY %]
           blah blah blah            # все нормально - попадает в буфер вывода
           [% THROW some 'error' %]  # здесь исключение
           ...
        [% CATCH %]
           [% CLEAR %]               # чистка вывода TRY
           [% error %]               # печать сообщения об ошибке
        [% END %]

Разное

  • META

    Директива META позволяет определить простые метаданные внутри шаблона. Они вычисляются, когда шаблон разбирается и поэтому могут содержать только простые значения (например, нельзя использовать другие переменные при определении переменных META).

        [% META
           title   = 'The Cat in the Hat'
           author  = 'Dr. Seuss'
           version = 1.23
        %]

    Переменная 'template' содержит ссылку на главный обрабатываемый шаблон. Эти метаданные могут быть получены как атрибуты шаблона.

        <h1>[% template.title %]</h1>
        <h2>[% template.author %]</h2>

    Элементы метаданных 'name' и 'modtime' автоматически определяются для каждого шаблона и содержат имя шаблона и время его модификации в секундах, прошедших с ключевого момента (1 января 1970 года для UNIX-систем).

        [% USE date %]		# используем плагин Date для форматирования времени
        ...
        [% template.name %] last modified
        at [% date.format(template.modtime) %]

    Опции PRE_PROCESS и POST_PROCESS позволяют добавлять ко всем шаблонам простые шапки и подвалы. Ссылка 'template' правильно определена во время обработки этих шаблонов, что позволяет ссылаться на метаданные из главного шаблона внутри шапок и подвалов.

        $template = Template->new({
    	PRE_PROCESS  => 'header',
    	POST_PROCESS => 'footer',
        });
        $template->process('cat_in_hat');

    header:

        <html>
        <head>
        <title>[% template.title %]</title>
        </head>
        <body>

    cat_in_hat:

        [% META
           title   = 'The Cat in the Hat'
           author  = 'Dr. Seuss'
           version = 1.23
           year    = 2000
        %]
        The cat in the hat sat on the mat.

    footer:

        <hr>
        &copy; [% template.year %] [% template.author %]
        </body>
        </html>

    Вывод примера, приведенного выше:

        <html>
        <head>
        <title>The Cat in the Hat</title>
        </head>
        <body>
        The cat in the hat sat on the mat.
        <hr>
        &copy; 2000 Dr. Seuss
        </body>
        </html>
  • TAGS

    Директива TAGS используется для установки значений опций START_TAG и END_TAG на уровне шаблона.

        [% TAGS <+ +> %]
        <+ INCLUDE header +>

    Директиву TAGS также можно использовать для установки предопределенного TAG_STYLE.

        [% TAGS html %]
        <!-- INCLUDE header -->

    Более подробно смотрите описание опций конфигурации TAGS и TAG_STYLE.

  • DEBUG

    Директива DEBUG используется для разрешения или запрета вывода отладочной информации директив внутри шаблона. Чтобы директивы DEBUG давали эффект, опция конфигурации DEBUG должна содержать установленный флаг DEBUG_DIRS. Если этот флаг не установлен, парсер проигнорирует и удалит любые директивы DEBUG.

    Директива DEBUG может использоваться с одним из двух параметров 'on' или 'off', разрешающим или запрещающим отладку с данной точки и дальше. При разрешении отладки, вывод каждой директивы в сгенерированном выводе будет предваряться комментарием, содержащим файл, номер строки и оригинальный текст директивы.

        [% DEBUG on %]
        directive debugging is on (assuming DEBUG option is set true)
        [% DEBUG off %]
        directive debugging is off

    Параметр 'format' можно использовать для изменения формата отладочного сообщения.

        [% DEBUG format '<!-- $file line $line : [% $text %] -->' %]

АВТОР

Индекс ] [ Руководство ] [ Наверх ]

Энди Уардли (Andy Wardley <abw@andywardley.com>)

http://www.andywardley.com/

ВЕРСИЯ

Индекс ] [ Руководство ] [ Наверх ]

Template Toolkit версия 2.14, дата релиза - 4 октября 2004.

АВТОРСКИЕ ПРАВА

Индекс ] [ Руководство ] [ Наверх ]

  Copyright (C) 1996-2004 Andy Wardley.  All Rights Reserved.
  Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.

Этот модуль является свободно-распространяемым программным обеспечением; вы можете распространять и/или модифицировать его на тех же условиях, что и Perl.