Вы можете использовать шаблоны Mojo вне веб-фреймворка Mojolicious — я делал это для рендеринга статических страниц для своего блога. Однако по умолчанию Mojo::Template
не поставляется с обычными помощниками. Вместо этого остальная часть Mojolicous внедряет переменные и помощники в шаблон.
Для своего блога я решил реализовать собственную вспомогательную систему. Я опишу свое решение в остальной части этого ответа. Mojo, возможно, изменился за это время и может предпочесть какое-то другое решение.
Я смоделировал шаблон как пару ссылки stash и объекта Mojo::Template. Каждый шаблон компилируется в отдельный пакет. Позже мы можем ввести временные значения в ссылку на тайник и передать значения наружу. Помощник — это закрытие определенной ссылки на тайник, поэтому он может получить доступ к этим значениям без использования явного параметра.
Вот как компилируются шаблоны:
package AMON::Blog::TemplateCollection;
sub add_template($self, $name, $source) {
state $namespace_id = 0;
my $namespace = Package::Stash->new(
__PACKAGE__ . '::Namespace::' . ++$namespace_id);
my $template = Mojo::Template->new(
name => $name,
namespace => $namespace->name,
auto_escape => 1,
tag_start => '{{',
tag_end => '}}',
);
# enter the helpers into the namespace
my $stash_ref = \{};
while (my ($name, $code) = each %{ $self->helpers }) {
$namespace->add_symbol('&' . $name => $code->($stash_ref));
}
$template->parse($source);
$self->templates->{$name} = {
stash_ref => $stash_ref,
template => $template
};
return;
}
Вот помощник layout
, который записывает запрошенный макет в переменную stash:
layout => sub ($stash_ref) {
return sub ($name, %args) {
if (my $existing = $$stash_ref->{layout}) {
croak sprintf q(Can't change layout from "%s" to "%s"), $existing->{name}, $name;
}
$$stash_ref->{layout} = { name => $name, args => \%args };
};
},
Внешняя подпрограмма используется только для закрытия $stash_ref
и выполняется во время компиляции шаблона выше.
Чтобы отобразить шаблон, мы предоставляем временные значения тайника, а затем обрабатываем Mojo::Template. Если тайник содержит аргумент макета, мы рекурсивно отображаем шаблон макета с выводом текущего шаблона в качестве содержимого:
sub render($self, $name, %args) {
my $template = $self->templates->{$name}
// croak qq(Unknown template "$name");
my ($stash_ref, $template_object) = @$template{qw/stash_ref template/};
$$stash_ref = {
name => $name,
layout => undef,
args => \%args,
};
my $result = $template_object->process();
my $layout_args = $$stash_ref->{layout};
$$stash_ref = undef;
if (blessed $result and $result->isa('Mojo::Exception')) {
die $result;
}
if ($layout_args) {
my $name = $layout_args->{name};
my $args = $layout_args->{args};
return $self->render($name, %$args, content => $result);
}
return $result;
}
Этот подход не слишком элегантен, но он работает без использования всего остального Mojolicious (особенно контроллеров, которые бессмысленны для статического сайта). Некоторое время спустя я переключился на другой механизм шаблонов, который поддерживает наследование шаблонов из коробки, без таких сложных обходных путей.
person
amon
schedule
12.03.2018