【问题标题】:Perl Template Toolkit - Preprocess templates back into Perl scriptPerl Template Toolkit - 将模板预处理回 Perl 脚本
【发布时间】:2016-08-10 15:19:34
【问题描述】:

如果此问题已在其他地方提出和/或回答,请原谅我。

在我开始之前,我应该指出,此查询的性质可能不需要将代码添加到此线程,而答案可能只是指向模板工具包模块文档的链接,并带有注释“再读一遍” :)。更清楚地说,我会接受一个简洁的“是”或“否”答案。

假设我有一个可以正常工作的 perl 脚本,它可以毫无问题地处理 Template::Toolkit 模板。

但是,我的一个模板包含“名称”(人名、服务器名称,最后真的没关系:只是“名称”)。这些名称用于稍后创建包含已处理数据的 FILENAMES。

今天,我通过以下伪代码进行了这项工作:

$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 允许您以补充方式将数据注入现有的模板模块对象,但这与我想要做的截然相反

我的实际问题(同样,YESNO 可以作为答案是否可行):

Perl 脚本是否有可能读取一个模板,比如一个包含 Template-Toolkit 存储的 HASH 对象的模板,然后创建一个可用的新 HASH通过实际的 Perl 脚本(例如:在模板之外)?

如果这是可能的,它将不需要以复杂的方式从一个模板中提取数据并使用该数据来处理另一个模板。相反,我理论上可以执行以下操作,而无需手动填充需要写入磁盘然后通过 FH 再次读取的列表。

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

谢谢,请理解我已尽最大努力创建一个可用的伪模型,因为真正的代码涉及机密元素,遗憾的是,它排除了泄露。我希望你会明白。我可以根据需要提供其他“经过整理的抽象/示例”。

更新

回应用户ikegami:

首先,感谢您的回复。

好的,让我们从你的一个 cmets 开始:“......这没有任何意义,因为 TT 不存储哈希值”......嗯......除非我们在谈论两个不同的事情,否则这是不正确的,因为我今天正在这样做.. 例如,这是一个模板托管的对象,其中包含我所说的初始哈希的元素:

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

再一次,上面的东西是有效的,它确实存在于 TT 中............除非我弄错了,上面的东西看起来、感觉和尝起来肯定像一个哈希......我但是,请允许您将它包含在一个数组中......所以也许它不是一个 100% 的 A 级“纯 HASH 对象”,但我当然会像哈希一样利用它(而且也非常好)。

在你说什么之前,我已经认识到我很可能需要将 key 名称更新为 UNIQUE 以便于解析(哈希键本身应该是唯一的),但这是单独的努力。

当您说“这显然不是您要问的”时,您是正确的。按照您的要求,让我更清楚地说明最终问题:

可以模板工具包,从正在执行的 Perl 脚本中读取包含单个 HASH 对象(如上所述)的“模板 1”,并将所述 HASH 结构读入 仅存在于所述执行脚本中的新的 散列对象(与模板本身相反)?这个“新哈希”最终将用于命名“模板 2”生成的文件。

最后,满足您的最后一个要求:

上面的输入摘录提供了您请求的“输入”。我想要的输出是从被调用模板中的数据派生的真正 Perl 哈希。

所以作为 Perl 等价物(期望的输出):

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

.... 将用于将特定值(例如“NAME”值)“提供”给“稍后”处理的其他模板。

我希望这能消除混乱...再次感谢 ikegami ...

【问题讨论】:

  • 您可以提供一个子程序引用作为process() 的第三个参数,生成的输出将作为参数传递;然后,您可以同时编写输出并对其进行解析,而无需再次读取文件。不过,这似乎仍然很迂回。
  • 为什么要使用 TT 来生成姓名列表?这似乎在 Perl 中做得更好; TT 非常灵活,但请注意不要过多地使用模板引擎。
  • 这听起来很像你在names.tt 中为模板工具包编写的任何东西都应该用 Perl 编写。 Perl 可以做 TT 可以做的所有事情,除此之外还有很多。看起来模板没有参数,所以只是通过组合文字字符串来创建名称;对吗?
  • @verteron:那么也许还有其他TT模板文件使用INCLUDEnames.tt和其他文件中获取信息?在我看来,您应该使用JSON 文件来存储这样的信息。它代表 JavaScript Object Notation,虽然它起源于 JavaScript 语言中数据文字的格式,但它已成为在语言之间传输数据的有用格式。有 Perl、Python 和 Ruby 库可供阅读,您应该查看 JSON Perl 模块
  • @verteron:关于您在下方“答案”下方的评论,我并不是建议您将其作为评论发布。您的问题下方有一个 edit 链接,该链接也链接到我的评论中。应通过编辑您的问题来添加任何重要的附加信息,而不仅仅是对评论的回答。在 Stack Exchange 上发布为 Answer 的任何内容都必须是原始问题的解决方案。

标签: perl template-toolkit


【解决方案1】:

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" },
]

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

【讨论】:

  • 你好。我可以澄清几件事。第一次记住这只是第一阶段:收集创建文件的名称列表。当第 2 阶段开始时,我们绝对会利用 TT 的力量,我向你保证 :)。这些模板非常复杂,有多种用途,而且效果很好。第二,我知道这看起来很奇怪,但是关于为什么我的 obj 是这样构造的,有一个很好的(长)答案。但是,我会指出,由于这个以及其他不相关的问题,我们似乎可能会完全使用另一个后端,例如: SQLITE OUTPUT ...谢谢!
【解决方案2】:

为了有一个可以接受的解决方案,我想我会展示一些使用 JSON 模块从磁盘文件加载一些数据的代码

假设我的文件data.json 包含这个(取自您自己的样本数据)

servers.json

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

然后我可以编写这个 Perl 代码来打开并将数据读入一个 Perl 哈希数组,就像这样

servers.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 pragma 以避免手动检查open 调用的状态。 autodie 最初是在 Perl 版本 5.10.1 中创建的核心模块,所以我也要求这样做

decode_json 调用仅接受 JSON 格式的 字符串,因此我使用了do 块来打开和读取文件并解码内容,丢弃所有临时值,例如文件句柄和 JSON 字符串

Data::Dump 只是为了显示读入的数据结构的形式

JavaScript 数据 (JSON) 非常类似于等效的 Perl,除了以下内容

  • Perl 的 =&gt; 替换为 :
  • 只允许使用双引号,不能使用单引号
  • 哈希键和字符串值必须用引号括起来
  • 列表的最后一项不能有尾随逗号

但是没有必要在数字文字值周围加上引号。它的工作原理与 Perl 相同,因此,如果您希望保留前导零的数字字符串,则需要使用引号,否则裸数字就可以了

我相信相反的过程(编写 JSON 文件)是显而易见的。对 Perl 数据结构的引用应传递给 encode_json 以创建 JSON 字符串,然后以正常方式将其写入文件

如果您迫切需要更快的编码和解码速度,那么有一个 JSON::XS 模块,它部分用 C 语言编写,因此速度更快,但是任何一个 JSON 模块都比您应该设置的任何模块都快得多使用模板工具包。 XS 变体是关于 encode_jsondecode_json 的纯 Perl 版本的直接替代品

【讨论】:

    猜你喜欢
    • 2013-01-27
    • 2021-03-22
    • 2012-03-12
    • 2014-02-15
    • 1970-01-01
    • 2014-10-20
    • 2011-01-30
    • 2015-09-11
    • 1970-01-01
    相关资源
    最近更新 更多