Dancer::Template::Abstract - abstract class for Dancer's template engines


Dancer documentation Contained in the Dancer distribution.

Index


Code Index:

NAME

Top

Dancer::Template::Abstract - abstract class for Dancer's template engines

DESCRIPTION

Top

This class is provided as a base class for each template engine. Any template engine must inherit from it and provide a set of methods described below.

INTERFACE

Top

init()

The template engine can overload this method if some initialization stuff has to be done before the template engine is used.

The base class provides a plain init() method that only returns true.

default_tmpl_ext()

Template class that inherits this class should override this method to return a default template extension, example: for Template::Toolkit it returns "tt" and for HTML::Mason it returns "mason". So when you call template 'index'; in your dispatch code, Dancer will look for a file 'index.tt' or 'index.mason' based on the template you use.

Note 1: when returning the extension string, please do not add a dot in front of the extension as Dancer will do that.

Note 2: for backwards compatibility abstract class returns "tt" instead of throwing an exception 'method not implemented'.

User would be able to change the default extension using the <extension> configuration variable on the template configuration. For example, for the default (Simple) engine:

     template: "simple"
     engines:
       simple:
         extension: 'tmpl'

view($view)

The default behavior of this method is to return the path of the given view.

layout($layout, $tokens, $content)

The default behavior of this method is to merge a content with a layout.

render($self, $template, $tokens)

This method must be implemented by the template engine. Given a template and a set of tokens, it returns a processed string.

If $template is a reference, it's assumed to be a reference to a string that contains the template itself. If it's not a reference, it's assumed to be the path to template file, as a string. The render method will then have to open it and read its content (Dancer::FileUtils::read_file_content does that job).

This method's return value must be a string which is the result of the interpolation of $tokens in $template.

If an error occurs, the method should trigger an exception with die().

Examples :

    # with a template as a file
    $content = $engine->render('/my/template.txt', { var => 42 };

    # with a template as a scalar
    my $template = "here is <% var %>";
    $content = $engine->render(\$template, { var => 42 });

AUTHOR

Top

This module has been written by Alexis Sukrieh, see Dancer for details.


Dancer documentation Contained in the Dancer distribution.

package Dancer::Template::Abstract;

use strict;
use warnings;
use Carp;

use Dancer::Factory::Hook;
use Dancer::Deprecation;
use Dancer::FileUtils 'path';

use base 'Dancer::Engine';

Dancer::Factory::Hook->instance->install_hooks(
    qw/before_template_render after_template_render before_layout_render after_layout_render/
);

# overloads this method to implement the rendering
# args:   $self, $template, $tokens
# return: a string of $template's content processed with $tokens
sub render { confess "render not implemented" }

sub default_tmpl_ext { "tt" }

sub _template_name {
    my ( $self, $view ) = @_;
    my $def_tmpl_ext = $self->config->{extension} || $self->default_tmpl_ext();
    $view .= ".$def_tmpl_ext" if $view !~ /\.\Q$def_tmpl_ext\E$/;
    return $view;
}

sub view {
    my ($self, $view) = @_;

    $view = $self->_template_name($view);

    return path(Dancer::App->current->setting('views'), $view);
}

sub layout {
    my ($self, $layout, $tokens, $content) = @_;

    my $layout_name = $self->_template_name($layout);
    my $layout_path = path(Dancer::App->current->setting('views'), 'layouts', $layout_name);

    my $full_content =
      Dancer::Template->engine->render($layout_path,
        {%$tokens, content => $content});
    $full_content;
}

sub apply_renderer {
    my ($self, $view, $tokens) = @_;

    ($tokens, undef) = _prepare_tokens_options($tokens);

    $view = $self->view($view);

    Dancer::Factory::Hook->execute_hooks('before_template_render', $tokens);

    my $content = $self->render($view, $tokens);

    Dancer::Factory::Hook->execute_hooks('after_template_render', \$content);

    # make sure to avoid ( undef ) in list context return
    defined $content
      and return $content;
    return;
}

sub apply_layout {
    my ($self, $content, $tokens, $options) = @_;

    ($tokens, $options) = _prepare_tokens_options($tokens, $options);

    # If 'layout' was given in the options hashref, use it if it's a true value,
    # or don't use a layout if it was false (0, or undef); if layout wasn't
    # given in the options hashref, go with whatever the current layout setting
    # is.
    my $layout =
      exists $options->{layout}
      ? ($options->{layout} ? $options->{layout} : undef)
      : Dancer::App->current->setting('layout');

    defined $content or return;

    defined $layout or return $content;

    Dancer::Factory::Hook->execute_hooks('before_layout_render', $tokens, \$content);

    my $full_content =
      $self->layout($layout, $tokens, $content);

    Dancer::Factory::Hook->execute_hooks('after_layout_render', \$full_content);

    # make sure to avoid ( undef ) in list context return
    defined $full_content
      and return $full_content;
    return;
}

sub _prepare_tokens_options {
    my ($tokens, $options) = @_;

    $options ||= {};

    # these are the default tokens provided for template processing
    $tokens ||= {};
    $tokens->{perl_version}   = $];
    $tokens->{dancer_version} = $Dancer::VERSION;
    $tokens->{settings}       = Dancer::Config->settings;
    $tokens->{request}        = Dancer::SharedData->request;
    $tokens->{params}         = Dancer::SharedData->request->params;
    $tokens->{vars}           = Dancer::SharedData->vars;

    Dancer::App->current->setting('session')
      and $tokens->{session} = Dancer::Session->get;

    return ($tokens, $options);
}

sub _render_with_layout {
    my ($class, $content, $tokens, $options) = @_;

    Dancer::Deprecation::deprecated(
        feature => 'render_with_layout',
        version => '1.3000',
        fatal   => 1,
        reason  => "use the 'engine' keyword to get the template engine, and use 'apply_layout' on the result",
    );
}

sub template {
    my ($class, $view, $tokens, $options) = @_;
    my ($content, $full_content);

    # it's important that $tokens is not undef, so that things added to it via
    # a before_template in apply_renderer survive to the apply_layout. GH#354
    $tokens  ||= {};
    $options ||= {};

    $content = $view ? Dancer::Template->engine->apply_renderer($view, $tokens)
                     : delete $options->{content};

    defined $content and $full_content =
      Dancer::Template->engine->apply_layout($content, $tokens, $options);

    defined $full_content
      and return $full_content;

    Dancer::Error->new(
        code    => 404,
        message => "Page not found",
    )->render();
}

1;
__END__