The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

package CGI::Application::Plugin::AnyTemplate::Base;

=head1 NAME

CGI::Application::Plugin::AnyTemplate::Base - Base class for templates

=head1 DESCRIPTION

This documentation is mainly for developers who want to write additional
Template drivers. For how to use the system, see the docs for
L<CGI::Application::Plugin::AnyTemplate>

=cut

use strict;
use Carp;
use Scalar::Util qw(weaken);

sub _new {
    my $proto = shift;
    my $class = ref $proto || $proto;

    my %args = @_;

    my $self = {};

    $self->{'driver_config'}     = delete $args{'driver_config'} || {};
    $self->{'native_config'}     = delete $args{'native_config'} || {};
    $self->{'include_paths'}     = delete $args{'include_paths'} || [];
    $self->{'filename'}          = delete $args{'filename'};
    $self->{'string_ref'}        = delete $args{'string_ref'};
    $self->{'callers_package'}   = delete $args{'callers_package'};
    $self->{'return_references'} = delete $args{'return_references'};
    $self->{'conf_name'}         = delete $args{'conf_name'};
    $self->{'webapp'}            = delete $args{'webapp'};

    $self->{'component_handler_class'} = delete $args{'component_handler_class'}
                                || 'CGI::Application::Plugin::AnyTemplate::ComponentHandler';

    bless $self, $class;

    weaken $self->{'webapp'};

    $self->initialize;

    return $self;
}

=head1 METHODS

=over 4

=item param

The C<param> method gets and sets values within the template.

    my $template = $self->template->load;

    my @param_names = $template->param();

    my $value = $template->param('name');

    $template->param('name' => 'value');
    $template->param(
        'name1' => 'value1',
        'name2' => 'value2'
    );

It is designed to behave similarly to the C<param> method in other modules like
C<CGI> and C<HTML::Template>.

=cut

sub param {
    my $self = shift;

    if (@_) {
        my $param;
        if (ref $_[0] eq 'HASH') {
            $param = shift;
        }
        elsif (@_ == 1) {
            return $self->{'param'}{$_[0]};
        }
        else {
            $param = { @_ };
        }
        $self->{'param'} ||= {};
        $self->{'param'}{$_} = $param->{$_} for keys %$param;
    }
    else {
        $self->{'param'} ||= {};
        return keys %{ $self->{'param'} };
    }
}

=item get_param_hash

Returns the template variables as a hash of names and values.

    my %params     = $template->get_param_hash;

In a scalar context, returns a reference to the hash used
internally to contain the values:

    my $params_ref = $template->get_param_hash;

=cut

sub get_param_hash {
    my $self = shift;
    $self->{'param'} ||= {};
    return %{ $self->{'param'} } if wantarray;
    return $self->{'param'};
}

=item clear_params

Clears the values stored in the template:

    $template->param(
        'name1' => 'value1',
        'name1' => 'value2'
    );
    $template->clear_params;
    $template->param(
        'name_foo' => 'value_bar',
    );

    # params are now:
        'name_foo' => 'value_bar',


=cut

sub clear_params {
    my $self = shift;
    $self->{'param'} = {};
}

=item output

Returns the template with all the values filled in.

    return $template->output();

You can also supply names and values to the template at this stage:

    return $template->output('name' => 'value', 'name2' => 'value2');

Before the template output is generated, the C<< template_pre_process >>
hook is called.  Any callbacks that you register to this hook will be
called before each template is processed.  Register a
C<template_pre_process> callback as follows:

    $self->add_callback('template_pre_process', \&my_tmpl_pre_process);

Pre-process callbacks will be passed a reference to the C<$template>
object, and can can modify the parameters passed into the template by
using the C<param> method:

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

        # Change the internal template parameters by reference
        my $params = $template->get_param_hash;

        foreach my $key (keys %$params) {
            $params{$key} = to_piglatin($params{$key});
        }

        # Can also set values using the param method
        $template->param('foo', 'bar');

    }


After the template output is generated, the C<template_post_process> hook is called.
You can register a C<template_post_process> callback as follows:

    $self->add_callback('template_post_process', \&my_tmpl_post_process);

Any callbacks that you register to this hook will be called after each
template is processed, and will be passed both a reference to the
template object and a reference to the output generated by the template.
This allows you to modify the output of the template:

    sub my_tmpl_post_process {
        my ($self, $template, $output_ref) = @_;

        $$output_ref =~ s/foo/bar/;
    }




When you call the C<output> method, any components embedded in the
template are run.  See C<EMBEDDED COMPONENTS>, below.

=cut

# calling forms:
#    $template->output;
#    $template->output('file.html', \%params);
#    $template->output(\%params)
#
#    $template->fill;
#    $template->fill('file.html', \%params);
#    $template->fill(\%params)

sub output {
    my $self = shift;

    $self->param(@_);

    my $webapp = $self->{'webapp'};

    if ($webapp and $webapp->can('call_hook')) {
        $webapp->call_hook('template_pre_process', $self);
    }

    my $output = $self->render_template;

    if ($webapp and $webapp->can('call_hook')) {
        my $output_param = $output;
        $output_param = \$output_param unless ref $output_param;
        $webapp->call_hook('template_post_process', $self, $output_param);
    }
    if ($self->{'return_references'}) {
        return ref $output ? $output : \$output;
    }
    else {
        return ref $output ? $$output : $output;
    }
}

=item filename

If the template was loaded from a file, the C<filename> method returns the template filename.

=cut

sub filename {
    my $self = shift;
    return $self->{'filename'};
}

=item string_ref

If the template was loaded from a string, the C<string_ref> method returns a reference to the string.

=cut

sub string_ref {
    my $self = shift;
    return $self->{'string_ref'};
}

=item object

Returns a reference to the underlying template driver, e.g. the
C<HTML::Template> object or the C<Template::Toolkit> object.

=back

=cut

sub object {
    my $self = shift;
    return $self->{'driver'};
}

=head1 DOCS FOR TEMPLATE MODULE DEVELOPERS

The following documentation is of interest primarly for developers who
wish to add support for a new type of Template system.

=head2 METHODS FOR DEVELOPERS

=over 4

=item initialize

This method is called by the controller at C<load> to create the
driver-specific subclass of C<CGI::Application::Plugin::AnyTemplate>

This is a virtual method and must be defined in the subclass.

The following paramters are passed to the driver and available as keys of the
driver's C<$self> object:

     'driver_config' => ...    # hashref of driver-specific config
     'native_config' => ...    # hashref of native template system specific config
     'include_paths' => ...    # listref of template include paths
     'filename'      => ...    # template filename
     'webapp'        => ...    # reference to the current CGI::Application $self

=cut

sub initialize {
    croak "Driver did not initialize its driver";
}


=item driver_config_keys

When it creates the driver object,
C<CGI::Application::Plugin::AnyTemplate> has to separate the
C<driver_config> from the C<native_config>.

C<driver_config_params> should return a list of parameters that are
specific to the driver_config and not the native template system
config.


For instance, the user can specify

    $self->template->config(
        HTMLTemplate => {
              embed_tag_name    => 'embed',
              global_vars       => 1,
              die_on_bad_params => 0,
              cache             => 1
        },
    );

The parameters C<global_vars>, C<die_on_bad_params>, and C<cache> are all
specific to L<HTML::Template>.  These are considered I<native> parameters.

But C<embed_tag_name> configures the
C<CGI::Application::Plugin::AnyTemplate::Driver::HTMLTemplate> subclass.  This
is considered a I<driver> parameter.

Therefore C<'embed_tag_name'> should be included in the list of
params returned by C<driver_config_params>.

Example C<driver_config_params>:

    sub driver_config_keys {
        'template_extension',
        'embed_tag_name'
    }

=cut

sub driver_config_keys {
    return;
}

=item default_driver_config

Should return a hash of default values for C<driver_config_params>.

For instance:

    sub default_driver_config {
        {
            template_extension => '.foo',
            embed_tag_name     => 'embed',
        };
    }


=cut

sub default_driver_config {
    return;
}

=item render_template

This method must be overriden in a subclass.  It has the responsibility
of filling the template in C<< $self->filename >> with the values in C<< $self->param >>
via the appropriate template system, and returning the output as either
a string or a reference to a string.

It also must manage embedding nested components.

=back

=cut

sub render_template {
    croak "render_template virtual method";
}

# Utility method for drivers to load their prerequsite modules

sub _require_prerequisite_modules {
    my ($class) = @_;

    my @missing_modules;

    foreach my $module ($class->required_modules) {
        eval "require $module";
        if ($@) {
            push @missing_modules, $module;
        }
        # $module->import;
    }
    if (@missing_modules) {
        foreach my $module (@missing_modules) {
            warn "$class: missing prerequisite module: $module\n";
        }
        die "Can't continue loading: $class\n";
    }
}


=head1 AUTHOR

Michael Graham, C<< <mgraham@cpan.org> >>

=head1 BUGS

Please report any bugs or feature requests to
C<bug-cgi-application-plugin-anytemplate@rt.cpan.org>, or through the web interface at
L<http://rt.cpan.org>.  I will be notified, and then you'll automatically
be notified of progress on your bug as I make changes.

=head1 SOURCE

The source code repository for this module can be found at http://github.com/mgraham/CAP-AnyTemplate/

=head1 SEE ALSO

    CGI::Application::Plugin::AnyTemplate
    CGI::Application::Plugin::AnyTemplate::ComponentHandler
    CGI::Application::Plugin::AnyTemplate::Driver::HTMLTemplate
    CGI::Application::Plugin::AnyTemplate::Driver::HTMLTemplateExpr
    CGI::Application::Plugin::AnyTemplate::Driver::HTMLTemplatePluggable
    CGI::Application::Plugin::AnyTemplate::Driver::TemplateToolkit
    CGI::Application::Plugin::AnyTemplate::Driver::Petal

    CGI::Application

    Template::Toolkit
    HTML::Template

    HTML::Template::Pluggable
    HTML::Template::Plugin::Dot

    Petal

    CGI::Application::Plugin::TT

=head1 COPYRIGHT & LICENSE

Copyright 2005 Michael Graham, All Rights Reserved.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=cut

1; # End of CGI::Application::Plugin::AnyTemplate