Perl Template Toolkit - предварительная обработка шаблонов обратно в Perl-скрипт

Простите меня, если этот вопрос уже был задан и / или ответил где-то еще.

Прежде чем я начну, я должен указать, что природа этого запроса может не требовать добавления кода в эту ветку, скорее, ответом может быть просто ссылка на документацию модуля Template Toolkit с комментарием «ПРОЧИТАЙТЕ ЭТО СНОВА» :). Чтобы быть еще более ясным, я бы согласился на краткий ответ ДА ​​или НЕТ.

Скажем, у меня есть рабочий Perl-скрипт, который без проблем обрабатывает шаблоны Template :: Toolkit.

Но один из моих шаблонов содержит «имена» (имена людей, имена серверов, в конце концов, это действительно не имеет значения: просто «имена»). Эти имена используются в дальнейшем для создания ФАЙЛОВ, содержащих обработанные данные.

Сегодня у меня это работает с помощью следующего псевдокода:

$template->process('names.tt','output.txt');

Затем я беру сгенерированный список «имен» в «output.txt» и помещаю их в массив в том же исполняемом сценарии Perl. Этот список затем используется итеративным образом для вывода данных, указанных в другом шаблоне, и дает имя каждому результирующему файлу на основе исходного «имени»:

foreach(@names){
   my $filename = $_ . '.txt';
   $template->process('profile_doc.tt',$filename)
}

Как я уже сказал, все работает нормально (на самом деле идеально). Моя проблема в том, что он без нужды раздувает мой код (нужно управлять открытыми FH и т. Д.). Должен быть лучший способ, учитывая необычайные способности TT.

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

Мой настоящий вопрос (опять же, ДА или НЕТ подойдет как ответ с точки зрения того, возможно это или нет):

Возможно ли, чтобы сценарий Perl прочитал шаблон, скажем, содержащий объект HASH, хранящийся в Template-Toolkit, и создать новый HASH, который можно использовать на самом деле? Сценарий Perl (например: вне шаблона)?

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

foreach(sort keys $derivedhash{obj}){
   $filename = $_->{name} . '.txt';
   $template->process('profile_doc.tt',$_->{name});
}

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

ОБНОВЛЕНИЕ

В ответ пользователю ikegami:

Во-первых, спасибо за ответ.

Хорошо, давайте начнем с одного из ваших комментариев: «... что не имеет смысла, поскольку TT не хранит хеши» ... Хм ... Если мы не говорим о двух разных вещах, это неверно, поскольку я делаю это сегодня ... например, вот объект, размещенный в шаблоне, который содержит элементы начального хеша, о котором я говорил:

[%- SERVERS = [
      {entry={
              NAME => 'Server1'
              SERIAL => '1234567890'
              DESC => 'A file server'
      }}
      {entry={
              NAME => 'Server2'
              SERIAL => '0987654321'
              DESC => 'An account server'
      }}
]-%]

Еще раз, вышеперечисленное работает, и оно ДЕЙСТВИТЕЛЬНО существует в TT ........... и, если я не ошибаюсь, вышеперечисленное, безусловно, выглядит, ощущается и на вкус как хеш ... Даю вам, тем не менее, что он заключен в массив ... так что, возможно, это не 100% -ный "чистый объект HASH", но я, безусловно, ДЕЙСТВИТЕЛЬНО использую его как хеш (и довольно красиво).

Прежде чем вы что-то скажете, я уже понимаю, что мне, скорее всего, придется обновить имена ключей, чтобы они были УНИКАЛЬНЫМИ для упрощения анализа (хеш-ключи сами по себе должны быть уникальными), но это отдельная задача. .

Вы были правы, когда сказали: «Очевидно, вы хотели спросить не об этом». Позвольте мне прояснить последний вопрос, как вы и просили:

Может Template Toolkit из исполняемого сценария Perl прочитать «Шаблон 1», который содержит единственный объект HASH (как описано выше), и прочитать указанную структуру HASH в новом хэш-объект, который существует только в указанном исполняемом скрипте (в отличие от самого шаблона)? Этот «новый хеш» в конечном итоге будет использоваться для имен файлов, созданных «шаблоном 2».

Наконец, чтобы удовлетворить вашу последнюю просьбу:

Вышеприведенный отрывок из входных данных обеспечивает «входные данные», которые вы запрашивали. Я хочу получить истинный хеш Perl, полученный из данных внутри вызываемого шаблона.

Итак, как эквивалент Perl (желаемый ВЫХОД):

%newhash = (
    entry => {
        NAME => "Server1",
        SERIAL => "1234567890",
        DESC => "A file server"
    },
    .... other entries ....
);

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

Я надеюсь, что это проясняет путаницу ... еще раз спасибо ikegami ...


person verteron    schedule 18.04.2016    source источник
comment
Вы можете указать ссылку на подпрограмму в качестве 3-го аргумента для process(), и сгенерированный вывод будет передан как параметр; затем вы можете записать вывод и одновременно проанализировать его, не читая файл снова. Однако это все еще кажется довольно окольным.   -  person ThisSuitIsBlackNot    schedule 18.04.2016
comment
Почему вы используете TT для создания списка имен? Похоже, что это лучше сделать на Perl; TT очень гибок, но будьте осторожны, пытаясь сделать слишком много с помощью движка шаблонов.   -  person ThisSuitIsBlackNot    schedule 18.04.2016
comment
Похоже, что все, что вы написали для Template Toolkit в names.tt, должно быть написано на Perl. Perl может делать все, что умеет TT, и многое другое. Похоже, что шаблон не принимает параметров и поэтому просто создает имена, комбинируя буквальные строки; это правильно?   -  person Borodin    schedule 18.04.2016
comment
Ваши дополнительные примечания только еще больше убеждают меня в том, что это должно быть написано на Perl, а не на разметке TT. Как данные вообще попали в файл шаблона TT? Есть ли что-нибудь еще в файле шаблона, кроме хеша, который вы показываете? Должен ли он быть в файле шаблона для любого другого процесса?   -  person Borodin    schedule 18.04.2016
comment
@ThisSuitIsBlackNot - спасибо за ваш ответ. Я отвечу на ваш в указанном порядке. 1.) Спасибо ... однако я должен спросить, что более окольным образом ... мое исходное (текущее исправление) или ваша идея? Почему-то мне кажется, что мой хуже: D 2.) Потому что сами названные имена хранятся в объекте, размещенном в TT (см. Мой более поздний ответ на ikegami) ... Однако я серьезно отношусь к вашему комментарию (остерегайтесь попыток сделать слишком много с шаблонизатор) ... возможно, однажды мы сможем хранить указанные объекты в чем-то другом (БД?) ... у этого есть плюсы / минусы, но это уже возникало раньше. Спасибо!   -  person verteron    schedule 18.04.2016
comment
@Borodin, спасибо за ответ - так все и началось. С одной стороны, да, мы могли бы сохранить эти данные в базе данных (или аналогичной), но мы хотели сохранить как можно меньше отдельных компонентов, по крайней мере, для этого Proof-of-Concept. Это не значит, что я не открыт для такой идеи в ближайшем будущем. ;-). Чтобы ответить на ваш второй вопрос, в этих шаблонах данных, содержащих хэш, больше ничего не существует. Никаких подписок и т. Д., Только простой текстовый хэш / массив obj :). Наконец, ваш третий вопрос, обязательно ли это в файле шаблона? Нет, и я открыт для других средств ввода. Спасибо o /   -  person verteron    schedule 18.04.2016
comment
@verteron: Возможно, есть другие файлы шаблонов TT, которые используют INCLUDE для получения информации из names.tt и других файлов? Мне кажется, вы должны использовать файл JSON для хранения такой информации. Он расшифровывается как объектная нотация JavaScript, и хотя он возник как формат для литералов данных на языке JavaScript, он стал полезным форматом для передачи данных между языками. Существуют библиотеки Perl, Python и Ruby для его чтения, и вам следует поискать модуль Perl JSON   -  person Borodin    schedule 18.04.2016
comment
@verteron: это означает, что любые шаблоны TT, которым нужны данные, должны передавать их им при вызове process вместо возможности INCLUDE напрямую, но я все еще не понимаю, делаете ли вы это когда-либо   -  person Borodin    schedule 18.04.2016
comment
Спасибо @Borodin. По иронии судьбы, JSON - это один из форматов, который мы при желании выводим в нисходящем направлении :). Однако я не рассматривал возможность использования JSON в качестве среды ввода. Спасибо за отличную идею.   -  person verteron    schedule 18.04.2016
comment
Хранить необработанные данные в шаблоне - не лучшая идея. Одним из самых больших преимуществ шаблонов является то, что они позволяют отделить логику отображения от бизнес-логики. Встраивая все свои данные в шаблон, вы заставляете себя писать сложную бизнес-логику для анализа результатов логики отображения, просто чтобы получить необработанные данные! Вы также привязываете себя к одному инструменту (TT) и одному языку (Perl); вам следует использовать общий формат сериализации данных, такой как XML, YAML или JSON (лично я предпочитаю JSON).   -  person ThisSuitIsBlackNot    schedule 18.04.2016
comment
@verteron: Что касается вашего комментария под вашим ответом ниже, я не предлагал вам публиковать его как комментарий. Под вашим вопросом есть ссылка изменить, на которую также есть ссылка в моем комментарии. Любая важная дополнительная информация, которая не является просто ответом на комментарий, должна быть добавлена ​​путем редактирования вашего вопроса. Все, что размещено как ответ на Stack Exchange, должно быть решением исходной проблемы.   -  person Borodin    schedule 18.04.2016
comment
@Borodin, спасибо и извините за неудобства.   -  person verteron    schedule 18.04.2016
comment
Не рекомендуется хранить исходные данные в шаблоне @ThisSuitIsBlackNot - спасибо. Я начинаю понимать, что это общее мнение, которого придерживаются многие участники. Хотя, честно говоря, это все еще то, что я бы назвал Proof-of-Concept, и я должен сказать, что его ценность была намного выше, чем предполагалось изначально (не буду утомлять вас подробностями). Тем не менее, его полностью стоит переработать, и я начинаю видеть в этом ценность. Я считаю эту беседу очень полезной и считаю, что у меня есть то, что мне нужно. Спасибо всем !   -  person verteron    schedule 18.04.2016


Ответы (2)


TT производит текст. Но если бы TT создавал текстовое представление хэша, было бы легко создать из него хеш.

Шаблон:

[%- USE Template.Plugin.JSON -%]
[%- SERVERS = [ ... ] -%]
[%- SERVERS.json -%]

Код:

use JSON qw( from_json );

$template->process('template.tt', {}, \my $json)
   or die(...);

my $servers = from_json($json);

Я использовал JSON, но подойдут и любые другие средства сериализации и десериализации структуры данных.


Тем не менее, я совершенно не понимаю, зачем вам это нужно. Таким образом вы даже не сможете воспользоваться преимуществами TT!

Если бы цель заключалась в том, чтобы воспользоваться преимуществами функций TT для изменения структуры данных, тогда создание самого шаблона JSON имело бы гораздо больший смысл. Например,

[
   [% IF devel %]
      {
         "entry": {
            "NAME":   "Server1-devel",
            "SERIAL": "1234567890",
            "DESC":   "Development server"
         }
      },
   [% ELSE %]
      {
         "entry": {
            "NAME":   "Server1-prod",
            "SERIAL": "1234567891",
            "DESC":   "Production server"
         }
      },
   [% END %]
   ...
]

use JSON qw( from_json );

my %vars = (
   devel => ...,
);

$template->process('template.tt', \%vars, \my $json)
   or die(...);

my $servers = from_json($json);

Кстати, если у вас есть хеш с единственным постоянным ключом, вы что-то делаете не так.

[
   { entry => { NAME => "Server1", SERIAL => 1234567890, DESC => "A file server" } },
   { entry => { NAME => "Server2", SERIAL => "0987654321", DESC => "An account server" } },
]

должно быть

[
   { NAME => "Server1", SERIAL => 1234567890, DESC => "A file server" },
   { NAME => "Server2", SERIAL => "0987654321", DESC => "An account server" },
]

or

{
   "Server1" => { SERIAL => 1234567890, DESC => "A file server" },
   "Server2" => { SERIAL => "0987654321", DESC => "An account server" },
}
person ikegami    schedule 18.04.2016
comment
Привет еще раз. Я могу прояснить пару вещей. 1-й помните, что это просто stage1: собрать список имен для создания файлов. когда начинается stage2, мы полностью задействуем силу TT, я вам обещаю :). Шаблоны очень сложные, служат многим целям и прекрасно работают. Во-вторых, я знаю, что это кажется странным, но есть хороший (длинный) ответ на вопрос, почему мой объект структурирован таким образом. НО, я отмечу, что из-за этой и других не связанных проблем, кажется, мы можем вообще использовать другой бэкэнд, например: SQLITE ‹- in-- script.pl --out -› OUTPUT ... спасибо ! - person verteron; 19.04.2016

Чтобы принять решение, я подумал, что покажу код, который использует модуль JSON для загрузки некоторых данных из файла на диске

Предположим, мой файл data.json содержит это (взято из ваших собственных образцов данных)

server.json

[
  {
    "NAME"   : "Server1",
    "SERIAL" : "1234567890",
    "DESC"   : "A file server"
  },
  {
    "NAME"   : "Server2",
    "SERIAL" : "0987654321",
    "DESC"   : "An account server"
  }
]

Затем я могу написать этот код Perl, чтобы открывать и считывать данные в массив хешей Perl, например

server.pl

use strict;
use warnings 'all';
use v5.10.1;
use autodie;

use JSON;

use constant JSON_FILE => 'servers.json';

my $servers = do {
    open my $fh, '<:raw', JSON_FILE;
    local $/;
    decode_json <$fh>;
};

use Data::Dump;

dd $servers;

вывод

[
  { DESC => "A file server", NAME => "Server1", SERIAL => 1234567890 },
  { DESC => "An account server", NAME => "Server2", SERIAL => "0987654321" },
]

Обратите внимание, что я использовал прагму autodie, чтобы не проверять состояние вызова open вручную. autodie был впервые сделан основным модулем в Perl версии 5.10.1, так что мне это тоже потребовалось.

Вызов decode_json принимает только строку в формате JSON, поэтому я использовал блок do для открытия и чтения файла и декодирования содержимого, отбрасывая все временные значения, такие как дескриптор файла и строка JSON.

Data::Dump служит только для отображения формы считываемой структуры данных.

Данные JavaScript (JSON) очень похожи на эквивалентный Perl, за исключением следующих

  • Perl => заменяется на :
  • Допускаются только двойные кавычки, одинарные кавычки запрещены.
  • Хеш-ключи, а также строковые значения должны быть заключены в кавычки.
  • В последнем элементе списка не может быть запятой.

Но нет необходимости заключать в кавычки числовое буквальное значение. Он работает так же, как Perl, поэтому, если вы хотите сохранить числовую строку с начальным нулем, вам нужно будет использовать кавычки, в противном случае простое число в порядке.

Я полагаю, что обратный процесс - написание файла JSON - очевиден. Ссылка на структуру данных Perl должна быть передана в encode_json для создания строки JSON, которая затем записывается в файл обычным способом.

Если вы отчаянно нуждаетесь в скорости кодирования и декодирования, существует модуль JSON::XS, который частично написан на C и поэтому работает намного быстрее, однако любой модуль JSON будет намного быстрее, чем все, что вы могли бы настроить с помощью Template Toolkit. Вариант XS представляет собой замену чистой версии Perl в отношении encode_json и decode_json.

person Borodin    schedule 18.04.2016