The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Kelp::Module::Template;

use Kelp::Base 'Kelp::Module';
use Kelp::Template;

attr ext => 'tt';
attr engine => sub { die "'engine' must be initialized" };

sub build {
    my ( $self, %args ) = @_;

    # Build and initialize the engine attribute
    $self->engine( $self->build_engine(%args) );

    # Register one method - template
    $self->register(
        template => sub {
            my ( $app, $template, $vars, @rest ) = @_;
            return $self->render( $self->_rename($template), $vars, @rest );
        }
    );
}

sub build_engine {
    my ( $self, %args ) = @_;
    return Kelp::Template->new( %args );
}

sub render {
    my ( $self, $template, $vars ) = @_;
    return $self->engine->process( $template, $vars );
}

sub _rename {
    my ( $self, $name ) = @_;
    return unless $name;
    return !ref($name) && $name !~ /\.(.+)$/
      ? $name . '.' . $self->ext
      : $name;
}

1;

__END__

=pod

=head1 NAME

Kelp::Module::Template - Template processing for Kelp applications

=head1 SYNOPSIS

First ...

    # conf/config.pl
    {
        modules => ['Template'],
        modules_init => {
            Template => { ... }
        }
    };

Then ...

    # lib/MyApp.pm
    sub some_route {
        my $self = shift;
        $self->template('some_template', { bar => 'foo' });
    }

=head1 DESCRIPTION

This module provides an interface for using templates in a Kelp web application. It
uses L<Kelp::Template>, but it could be easily subclassed to use anything else.

=head1 REGISTERED METHODS

=head2 template

C<template($filename, \%vars)>

Renders a file using the currently loaded template engine. If the file doesn't
have an extension, the one specified in L</ext> will be assigned to it.

=head1 ATTRIBUTES

=head2 ext

The default extension of the template files. This module sets this attribute to
C<tt>, so

    $self->template( 'home' );

will look for C<home.tt>.

=head2 engine

This attribute will be initialized by the C<build_engine> method of this module,
and it is available to all code that needs access to the template engine
instance. See L</SUBCLASSING> for an example.

=head1 METHODS

=head2 build_engine

C<build_engine(%args)>

This method is responsible for creating, initializing and returning an instance
of the template engine used, for example L<Template>. Override it to use a
different template engine, for example L<Text::Haml>.

=head2 render

C<render($template, \%vars, @rest)>

This method should return a rendered text. Override it if you're subclassing and
using a different template engine.

=head1 PERKS

=head2 UTF8

To process templates in utf8, add the C<encoding> to the module configuration:

    # conf/config.pl
    {
        modules      => ['Template'],
        modules_init => {
            Template => {
                encoding => 'utf8'
            }
        }
    };

=head1 SUBCLASSING

To use a different template engine, you can subclass this module. You will need
to make sure your new class does the following (for the sake of the example we
will show you how to create a L<Text::Haml> rendering module):

=over

=item

Overrides the L</ext> attribute and provides the file extension of the new
template files.

    attr ext => 'haml';

=cut

=item

Overrides the L</build_engine> method and creates an instance of the new
template engine.

    sub build_engine {
        my ( $self, %args ) = @_;
        return Text::Haml->new;
    }

=cut

=item

Overrides the L</render> method and renders using C<$self-E<gt>engine>.

    sub render {
        my ( $self, $template, $vars, @rest ) = @_;

        # Get the template engine instance
        my $haml = $self->engine;

        # If the $template is a reference, then render string,
        # otherwise it's a file name.
        return ref($template) eq 'SCALAR'
          ? $haml->render( $$template, %$vars )
          : $haml->render_file( $template, %$vars );
    }

=cut

=back

=cut