Can I use Mojolicious to build a static site?

雨燕双飞 提交于 2020-01-13 18:01:59

问题


Is it possible to use the Mojolicious template system to build a static website?

I'm trying to use a (skeleton) script like this:

use Mojo::Template;
use Mojolicious::Plugin::DefaultHelpers;
use Mojolicious::Plugin::TagHelpers;

my $mt = Mojo::Template->new;
print $mt->render_file('index.html.ep');

Where index.html.ep is like this:

% layout 'default';
This is a foo

However I get an error nessage:

String found where operator expected at index.html.ep line 1, near "layout 'default'"
    (Do you need to predeclare layout?)
syntax error at index.html.ep line 1, near "layout 'default'"
1: % layout 'default';
2: This is a foo

Obviously, if I omit % layout 'default'; all is good, but being able to reuse snippets and layout is the whole point.

I know I could use Template Toolkit or some other templating system, but I want to avoid the cognitive friction of using multiple systems if at possible.

I'm also aware that I could start up mojolicious as a server and wget all the pages, but it seems overkill.

Any help here?


回答1:


You can use Mojo templates outside of the Mojolicious web framework – I used to do that to render static pages for my blog. However, the Mojo::Template does not come with the normal helpers by default. Instead, the rest of Mojolicous injects variables and helpers into the template.

For my blog, I decided to implement my own helper system. I'll describe my solution in the rest of this answer. Mojo may have changed in the meantime, and may prefer some different solution.

I modelled a template as a pair of a stash reference and the Mojo::Template object. Each Template is compiled into its own package. Later, we can inject temporary values into the stash reference and communicate values to the outside. A helper is a closure of a specific stash ref, so it can access these values without using an explicit parameter.

Here's how templates are compiled:

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;
}

Here is a layout helper that writes the requested layout into a stash variable:

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 };
    };
},

The outer sub is only used to close over the $stash_ref, and is executed during template compilation above.

To render the template, we supply temporary stash values, then process the Mojo::Template. If the stash contains a layout argument, we recurse to render the layout template with the current template's output as content:

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;
}

This approach is not terribly elegant, but it works without having to pull in all the rest of Mojolicious (particularly controllers, which are pointless for a static site). Some time later I switched to a different template engine that supports template inheritance out of the box, without such extensive workarounds.




回答2:


Adding another (minimal) way to do this:

use Mojolicious;

my $m = Mojolicious->new->log(Mojo::Log->new);
my $r = $m->renderer;

push @{$r->paths}, './templates';

my $c = Mojolicious::Controller->new->app($m);

my ($output, $format) = $r->render($c, { template => 'index' });
print $output

Iterating this through a number of templates, and sending the output to appropriately named files should be straightforward.




回答3:


If you run your Mojo app via Plack then you can use https://metacpan.org/pod/wallflower to create a static site from your app.



来源:https://stackoverflow.com/questions/49230849/can-i-use-mojolicious-to-build-a-static-site

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!