#   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#
#   file: lib/Dist/Zilla/Role/TextTemplater.pm
#

#pod =encoding UTF-8
#pod
#pod =head1 COPYRIGHT AND LICENSE
#pod
#pod Copyright © 2015 Van de Bugger
#pod
#pod This file is part of perl-Dist-Zilla-Role-TextTemplater.
#pod
#pod perl-Dist-Zilla-Role-TextTemplater is free software: you can redistribute it and/or modify it
#pod under the terms of the GNU General Public License as published by the Free Software Foundation,
#pod either version 3 of the License, or (at your option) any later version.
#pod
#pod perl-Dist-Zilla-Role-TextTemplater is distributed in the hope that it will be useful, but
#pod WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
#pod PARTICULAR PURPOSE. See the GNU General Public License for more details.
#pod
#pod You should have received a copy of the GNU General Public License along with
#pod perl-Dist-Zilla-Role-TextTemplater. If not, see <http://www.gnu.org/licenses/>.
#pod
#pod =cut

#   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

#pod =for :this This is C<Dist::Zilla::Role::TextTemplater> module documentation. Read this if you want to
#pod have text templating capabilities in your Dist::Zilla plugin.
#pod
#pod =for :that If you are using a C<TextTemplater>-based plugin, read the
#pod L<manual|Dist::Zilla::Role::TextTemplater::Manual>. General topics like getting source, building, installing, bug
#pod reporting and some others are covered in the L<readme|Dist::Zilla::Role::TextTemplater::ReadMe>.
#pod
#pod =head1 SYNOPSIS
#pod
#pod     package Dist::Zilla::Plugin::YourPlugin;
#pod     use Moose;
#pod     use namespace::autoclean;
#pod     with 'Dist::Zilla::Role::Plugin';
#pod     with 'Dist::Zilla::Role::TextTemplater';
#pod
#pod     sub method {
#pod         my $self = shift( @_ );
#pod         …
#pod         $result = $self->fill_in_string( $template );
#pod         …
#pod     };
#pod
#pod     sub another_method {
#pod         my $self = shift( @_ );
#pod         …
#pod         $self->fill_in_file( $file );
#pod         …
#pod     };
#pod
#pod     __PACKAGE__->meta->make_immutable;
#pod     1;
#pod
#pod =head1 DESCRIPTION
#pod
#pod The role provides consuming plugin with C<fill_in_string> and C<fill_in_file> methods and bunch of
#pod accompanying attributes and F<dist.ini> options.
#pod
#pod =cut

package Dist::Zilla::Role::TextTemplater;

use Moose::Role;
use namespace::autoclean;

# ABSTRACT: Have text templating capabilities in your Dist::Zilla plugin
our $VERSION = '0.004'; # VERSION

with 'Dist::Zilla::Role::ErrorLogger' => { -version => 0.005 };

use Text::Template qw{};
use Carp qw{ croak };
use List::Util qw{ min max };

# --------------------------------------------------------------------------------------------------

#pod =attr delimiters
#pod
#pod Pair of open and closing delimiters the code fragments surrounded by.
#pod
#pod Attribute introduces F<dist.ini> option with the same name. Option value will be split on
#pod whitespaces (and result should be two words) to initialize the attribute.
#pod
#pod C<Str|ArrayRef[Str]>, read-only. Default value is C<[ '{{', '}}' ]>.
#pod
#pod See C<DELIMITERS> option of L<Text::Template/"fill_in">.
#pod
#pod Note: Our default delimiters are "alternative delimiters" for C<Text::Template>. It means escaping
#pod delimiters with backslash does not work. See L<Text::Template/"Alternative Delimiters">.
#pod
#pod =cut

has delimiters => (
    is          => 'ro',
    isa         => 'Str|ArrayRef[Str]',
    lazy        => 1,
    default     => sub { [ '{{', '}}' ] },
    trigger     => sub {
        my ( $self, $new ) = @_;
        if ( not ref( $new ) ) {
            $new =~ s{\A\s+}{};     # Drop leading ws, or `split` may leave the first item empty.
            $new = [ split( qr{\s+}, $new ) ];
            @$new == 2
                or croak "\"delimiters\" value must be Str of *two* whitespace-separated words";
            $self->{ delimiters } = $new;
        } else {
            @$new == 2
                or croak "\"delimiters\" value must be ArrayRef with *two* elements";
        };
    },
);

# --------------------------------------------------------------------------------------------------

#pod =attr package
#pod
#pod Name of package to evaluate code fragments in.
#pod
#pod Attribute introduces F<dist.ini> option with the same name.
#pod
#pod C<Str>, read-only, optional.
#pod
#pod See C<PACKAGE> option of L<Text::Template/"fill_in">.
#pod
#pod =cut

has package => (
    is          => 'ro',
    isa         => 'Str',
    init_arg    => 'package',
);

# --------------------------------------------------------------------------------------------------

#pod =attr prepend
#pod
#pod Perl code to prepend to the beginning of every code fragment.
#pod
#pod Attribute introduces F<dist.ini> multi-value option with the same name.
#pod
#pod C<ArrayRef[Str]>, read-only, auto dereferenced. Default value is empty array. Another default value
#pod may be specified by defining C<_build_prepend> method.
#pod
#pod See C<PREPEND> option of L<Text::Template/"fill_in">.
#pod
#pod =cut

has prepend => (
    is          => 'ro',
    isa         => 'ArrayRef[Str]',
    builder     => '_build_prepend',
    auto_deref  => 1,
);

sub _build_prepend {
    return [];
};

# --------------------------------------------------------------------------------------------------

#pod =attr broken
#pod
#pod Callback to execute if a code fragment dies. Default callback formats detailed error message and
#pod log it by calling C<log_error>.
#pod
#pod C<CodeRef>, read-only, not an init argument. Default callback may be overridden by defining
#pod C<_build_broken> method.
#pod
#pod See C<BROKEN> option of L<Text::Template/"fill_in">.
#pod
#pod =cut

has broken => (
    is          => 'ro',
    isa         => 'CodeRef',
    builder     => '_build_broken',
    init_arg    => undef,
);

#   Claculate range of lines to show. $line is a lineno of focus line, $ctx is number of context
#   lines to show above and below the focus line, $min and $max defined full range. It is usually
#   1 and number of lines in the text. Note: linunumbers are one-based.
sub __range($$$$) {
    my ( $line, $ctx, $min, $max ) = @_;
    if ( $line > 0 ) {
        my $first = max( $line - $ctx, $min );  # First line to show.
        my $last  = min( $line + $ctx, $max );  # Last line to show.
        if ( $first - $min < $ctx ) {   # If number of skipped lines at top less than context,
            $first = $min;              # do not skip lines.
        };
        if ( $max - $last < $ctx ) {    # If number of skipped lines at botton less than context,
            $last = $max;               #  do not skip lines.
        };
        return ( $first, $last );
    } else {
        return ( 0, -1 );
    };
};

sub _build_broken {
    my ( $self ) = @_;
    return sub {
        my %args  = @_;
        my $error = $args{ error };     # Error message.
        my $tfile = $self->tt_filename; # Template filename.
        my $tline = $args{ lineno };    # Template line where the problem code fragment begins.
        my $eline = 0;                                      # Line where the problem occurs,
        if ( $error =~ m{ at \Q$tfile\E line (\d+)} ) {     # if the problem occurs in template.
            $eline = $1;
        };
        my @template = split( "\n", $self->tt_template );   # Template text.
        chomp( $error );
        $self->log_error( $error );
        $self->log_error( "Problem code fragment begins at $self->{ tt_filename } line $tline." );
        $self->log_error( "$tfile:" );
        #   TODO: Chop too long lines.
        my $ctx  = 2;                       # Context (number of lines to show above and below).
        my $w    = length( 0 + @template ); # Width of linenumber column.
        my $text = '    %0' . $w . 'd: %s'; # Template text line format.
        my $note = '    ' . ( ' ' x $w ) . '  ^^^ %s ^^^';              # Notice line format.
        my $skip = '    ' . ( ' ' x $w ) . '  ...skipped %d lines...';  # "Skipped" notice format.
        my ( $t0, $t1 ) = __range( $tline, $ctx,       1, 0 + @template );
        my ( $e0, $e1 ) = __range( $eline, $ctx, $t1 + 1, 0 + @template );
        my $last = 0;       # Number of last shown line.
        for my $line ( $t0 .. $t1, $e0 .. $e1 ) {
            if ( $line != $last + 1 ) {
                $self->log_error( [ $skip, $line - $last - 1 ] );
            };
            $self->log_error( [ $text, $line, $template[ $line - 1 ] ] );
            if ( $line == $tline ) {
                $self->log_error( [ $note, 'Code fragment begins in line above' ] );
            };
            if ( $line == $eline ) {
                $self->log_error( [ $note, 'Code fragment died in line above' ] );
            };
            $last = $line;
        };
        if ( $last < @template ) {
            $self->log_error( [ $skip, @template - $last ] );
        };
        # TODO: Do we need $self->abort() here?
    };
};

# --------------------------------------------------------------------------------------------------

#pod =attr tt_filename
#pod
#pod Name of file being processed, or "template" if no filename was specified (e. g. when processing a
#pod string and C<filename> option was not specified). Available only during template processing (e. g.
#pod may be used in a C<broken> subroutine).
#pod
#pod C<Str>, read-only, not an init arg.
#pod
#pod =cut

has tt_filename => (
    is          => 'ro',
    isa         => 'Str',
    init_arg    => undef,
);

# --------------------------------------------------------------------------------------------------

#pod =attr tt_template
#pod
#pod Template being processed. Available only during template processing (e. g. may be used in a
#pod C<broken> subroutine).
#pod
#pod C<Str>, read-only, not an init arg.
#pod
#pod =cut

has tt_template => (
    is          => 'ro',
    isa         => 'Str',
    init_arg    => undef,
);

# --------------------------------------------------------------------------------------------------

#pod =method mvp_multivalue_args
#pod
#pod The method tells C<Dist::Zilla> that C<prepend> is a multi-value option.
#pod
#pod =cut

around mvp_multivalue_args => sub {
    my ( $orig, $self ) = @_;
    return ( $self->$orig(), qw{ prepend } );
};

# --------------------------------------------------------------------------------------------------

#pod =method fill_in_string
#pod
#pod     $string = '…';
#pod     $result = $self->fill_in_string( $string, \%variables, \%extra_args );
#pod     $result = $self->fill_in_string( $string );
#pod
#pod The primary method of the role.
#pod
#pod The interface is compatible with method of C<TextTemplate> role, so this role can be used as a
#pod drop-in replacement for C<TextTemplate>. However, methods are implemented slightly differently, it
#pod may cause subtle difference in behaviour.
#pod
#pod Template variables C<$plugin> and C<$dist> are set by default, if not explicitly defined in
#pod C<\%variables>.
#pod
#pod The method creates C<Text::Template> object, enforces C<Text::Template> to respect C<filename>
#pod argument (see L<FILENAME parameter has no
#pod effect|https://rt.cpan.org/Ticket/Display.html?id=106093>), then calls C<fill_in> method on the
#pod object. C<$template>, C<\%variables>, and C<package>, C<prepend>, C<broken> attribute values, and
#pod finally C<%extra_args> are passed to the C<fill_in>.
#pod
#pod C<%extra_args> is passed in the end intentionally, so caller may override any of arguments
#pod specified by the method, for example:
#pod
#pod     $self->fill_in_string( $template, undef, { package => 'MY' } );
#pod         #   Execute code fragments in package MY regardless of $self->package value.
#pod
#pod It also allows to cancel defining C<$plugin> and C<$dist> variables, e. g.:
#pod
#pod     $self->fill_in_string( $template, undef, { hash => {} } );
#pod         #   Do not define any variables for the template.
#pod
#pod =cut

sub fill_in_string {
    my ( $self, $string, $hash, $args ) = @_;
    my %hash = (
        plugin => \( $self ),
        dist   => \( $self->zilla ),
        $hash ? %$hash : (),
    );
    my %args = (
        hash       => \%hash,
        delimiters => $self->delimiters,
        $self->package ? ( package => $self->package,              ) : (),
        $self->prepend ? ( prepend => join( "\n", $self->prepend ) ) : (),
        $self->broken  ? ( broken  => $self->broken                ) : (),
        $args ? %$args : (),
    );
    my ( $result, $error );
    {
        #   Original version of the code simply called `Text::Template::fill_in_string` function.
        #   However, this trivial approach does not work good because of `Text::Template` bug: it
        #   ignores `filename` argument, see <https://rt.cpan.org/Ticket/Display.html?id=106093>.
        #   It seems it will not be fixed soon. The bug can be workarounded by setting
        #   `Text::Template` object property `FILENAME`. In order to do it, we need access to
        #   `Text::Template` object, that means we have to create it manually, and then call
        #   `fill_in` method on it.
        local $Text::Template::ERROR;
        my $tt = Text::Template->new( type => 'STRING', source => $string, %args );
        if ( $tt ) {
            my $filename = ( Text::Template::_param( 'filename', %args ) or 'template' );
            local $self->{ tt_filename } = $filename;   # Will be used in `broken`.
            local $self->{ tt_template } = $string;     # ditto.
            $tt->{ FILENAME } = $filename;  # Workaround for the bug.
            local $SIG{ __WARN__ } = sub {  # TODO: Create an attribute?
                my $msg = "$_[ 0 ]";        # Stringify message, it could be an object.
                chomp( $msg );
                $self->log( $msg );
            };
            $result = $tt->fill_in( %args );
        };
        $error = $Text::Template::ERROR;
    }
    if ( not defined( $result ) ) {
        $self->log_error( $error );
    };
    $self->abort_if_error();
    return $result;
};

# --------------------------------------------------------------------------------------------------

#pod =method fill_in_file
#pod
#pod     $file = Dist::Zilla::File::OnDisk->new( { … } );
#pod     $result = $self->fill_in_file( $file, \%variables, \%extra_args );
#pod     $result = $self->fill_in_file( $file );
#pod
#pod Similar to C<fill_in_string>, but the first argument is not a string but a file. File content is
#pod treated as template, and will be replaced with result of template processing. New file content will
#pod also be returned.
#pod
#pod =cut

sub fill_in_file {
    my ( $self, $file, $hash, $args ) = @_;
    my %args = (
        filename => $file->name,
        $args ? %$args : (),
    );
    return $file->content( $self->fill_in_string( $file->content, $hash, \%args ) );
};

# --------------------------------------------------------------------------------------------------

1;

# --------------------------------------------------------------------------------------------------

#pod =head1 NOTES
#pod
#pod =head2 C<Text::Template> option spelling
#pod
#pod C<Text::Template> allows a programmer to use different spelling of options: all-caps, first-caps,
#pod all-lowercase, with and without leading dash, e. g.: C<HASH>, C<Hash>, C<hash>, C<-HASH>, C<-Hash>,
#pod C<-hash>. This is documented feature.
#pod
#pod C<Text::Template> recommends to pick a style and stick with it. (BTW, C<Text::Template>
#pod documentation itself uses all-caps spelling.) This role picked all-lowercase style. This choice
#pod have subtle consequences. Let us consider an example:
#pod
#pod     $self->fill_in_string( $template, undef, { PACKAGE => 'MY' } );
#pod
#pod Extra option C<PACKAGE> may or may not not have effect, depending on value of C<package> attribute
#pod (i. e. presence or absence C<package> option in F<dist.ini> file), because (this is not documented)
#pod spellings are not equal: different spellings have different priority. If C<PACKAGE> and C<package>
#pod are specified simultaneously, C<package> wins, C<PACKAGE> loses.
#pod
#pod This feature gives you a choice. If you want to ignore option specified by the user in F<dist.ini>
#pod and provide your value, use all-lowercase option name. If you want to provide default which can be
#pod overridden by the user, use all-caps options name.
#pod
#pod =head2 C<filename> option
#pod
#pod When C<Text::Template> reads template from a file, it uses the file name in error messages, e. g.:
#pod
#pod     Undefined subroutine &foo called at filename.ext line n
#pod
#pod where I<filename.ext> is the name of file containing the template. When C<Text::Template> processes
#pod a string, it uses word "template" instead of file name, e. g.:
#pod
#pod     Undefined subroutine &foo called at template line n
#pod
#pod The option C<filename> allows the caller to override it:
#pod
#pod     $self->fill_in_file( $file, undef, { filename => 'Assa.txt' } );
#pod
#pod Error message would look like:
#pod
#pod     Undefined subroutine &foo called at Assa.txt line n
#pod
#pod It may seem this does not make much sense, but in our case (C<Dist::Zilla> and its plugins)
#pod C<Text::Template> always processes strings and never reads files, because reading files is a duty
#pod of C<Dist::Zilla::File::OnDisk> class. Thus, using C<filename> option is critical to provide good
#pod error messages. Actually, C<fill_in_file> implementation looks like
#pod
#pod     $file->content( $self->fill_in_string( $file->content, undef, { filename => $file->name } ) );
#pod
#pod There are two problems with the option, though:
#pod
#pod =over
#pod
#pod =item *
#pod
#pod C<Text::Template> does not document this option.
#pod
#pod I believe it is a mistake and option should be documented.
#pod
#pod =item *
#pod
#pod C<Text::Template> ignores this option.
#pod
#pod I am sure this is a bug and hope it will be fixed eventually. I am afraid it will not be fixed
#pod soon, though.
#pod
#pod Meanwhile, C<Dist-Zilla-Role-TextTemplater> implements a workaround to let the option work, so C<TextTemplater>
#pod consumers can utilize the C<filename> option.
#pod
#pod =back
#pod
#pod =head1 SEE ALSO
#pod
#pod =for :list
#pod = L<Dist::Zilla>
#pod = L<Dist::Zilla::Role>
#pod = L<Dist::Zilla::Plugin>
#pod = L<Dist::Zilla::Role::TextTemplate>
#pod = L<Text::Template>
#pod
#pod =cut

# doc/what.pod #

#pod =encoding UTF-8
#pod
#pod =head1 WHAT?
#pod
#pod C<Dist-Zilla-Role-TextTemplater> is a C<Dist::Zilla> role, a replacement for standard role C<TextTemplate>. Both
#pod roles have the same great C<Text::Template> engine under the hood, but this one provides better
#pod control over the engine and much better error reporting.
#pod
#pod =cut

# end of file #
# doc/why.pod #

#pod =encoding UTF-8
#pod
#pod =head1 WHY?
#pod
#pod C<TextTemplate> role from C<Dist::Zilla> distribution v5.037 has the same great C<Text::Template>
#pod engine under the hood, but lacks of control and has I<awful> error reporting.
#pod
#pod =head2 Error Reporting
#pod
#pod Let us consider an example. Have a look at F<dist.ini>:
#pod
#pod     name     = Assa
#pod     version  = 0.001
#pod     [GatherDir]
#pod     [PruneCruft]
#pod     [GenerateFile/COPYING]
#pod         filename = COPYING
#pod         content  = This is {{$dist->name}}.
#pod         content  =
#pod         content  = {{$dst->license->notice}}
#pod         is_template = 1
#pod     [TemplateFiles]
#pod         filename = lib/Assa.pm
#pod         filename = lib/Assa/Manual.pod
#pod     [MetaResources::Template]
#pod         license = {{$dist->license->url}}
#pod         …
#pod     …
#pod
#pod (Do you see a typo? Real F<dist.ini> can be much longer.) Now let us build the distribution:
#pod
#pod     $ dzil build
#pod     [DZ] beginning to build Assa
#pod     Can't call method "license" on an undefined value at template line 3.
#pod
#pod Oops. Ok, it is clear what was happened, but where?? The only weak clue is word "template".
#pod However, there are many templates in the project: F<lib/Assa.pm>, F<lib/Assa/Manual.pod>, generated
#pod on-the-fly F<COPYING>, and even resources in F<META.yml> — all this stuff is generated from
#pod templates. Where is the problem? Have a happy bug hunting, dude.
#pod
#pod If these plugins (namely, C<GenerateFile>, C<TemplateFiles> and C<MetaResources::Template>) were
#pod written with C<TextTemplateB<r>> role, result would be:
#pod
#pod     $ dzil build
#pod     [DZ] beginning to build Assa
#pod     [COPYING] Can't call method "license" on an undefined value at COPYING line 3.
#pod     [COPYING] Problem code fragment begins at COPYING line 3.
#pod     [COPYING] COPYING:
#pod     [COPYING]     1: This is {{$dist->name}}.
#pod     [COPYING]     2:
#pod     [COPYING]     3: {{$dst->license->notice}}
#pod     [COPYING]        ^^^ Code fragment begins in line above ^^^
#pod     [COPYING]        ^^^ Code fragment died in line above ^^^
#pod     Aborting...
#pod
#pod See the difference.
#pod
#pod =head2 Engine Control
#pod
#pod C<TextTemplater> allows the end-user to specify C<delimiters>, C<package> and C<prepend> engine
#pod options in F<dist.ini> file, while C<TextTemplate> allows to specify C<prepend> only
#pod programmatically, and does I<not> allow to specify C<delimiters> and C<package>.
#pod
#pod =cut

# end of file #


# end of file #

__END__

=pod

=encoding UTF-8

=head1 NAME

Dist::Zilla::Role::TextTemplater - Have text templating capabilities in your Dist::Zilla plugin

=head1 VERSION

Version 0.004, released on 2015-08-06 16:14 UTC.

=head1 WHAT?

C<Dist-Zilla-Role-TextTemplater> is a C<Dist::Zilla> role, a replacement for standard role C<TextTemplate>. Both
roles have the same great C<Text::Template> engine under the hood, but this one provides better
control over the engine and much better error reporting.

This is C<Dist::Zilla::Role::TextTemplater> module documentation. Read this if you want to
have text templating capabilities in your Dist::Zilla plugin.

If you are using a C<TextTemplater>-based plugin, read the
L<manual|Dist::Zilla::Role::TextTemplater::Manual>. General topics like getting source, building, installing, bug
reporting and some others are covered in the L<readme|Dist::Zilla::Role::TextTemplater::ReadMe>.

=head1 SYNOPSIS

    package Dist::Zilla::Plugin::YourPlugin;
    use Moose;
    use namespace::autoclean;
    with 'Dist::Zilla::Role::Plugin';
    with 'Dist::Zilla::Role::TextTemplater';

    sub method {
        my $self = shift( @_ );
        …
        $result = $self->fill_in_string( $template );
        …
    };

    sub another_method {
        my $self = shift( @_ );
        …
        $self->fill_in_file( $file );
        …
    };

    __PACKAGE__->meta->make_immutable;
    1;

=head1 DESCRIPTION

The role provides consuming plugin with C<fill_in_string> and C<fill_in_file> methods and bunch of
accompanying attributes and F<dist.ini> options.

=head1 OBJECT ATTRIBUTES

=head2 delimiters

Pair of open and closing delimiters the code fragments surrounded by.

Attribute introduces F<dist.ini> option with the same name. Option value will be split on
whitespaces (and result should be two words) to initialize the attribute.

C<Str|ArrayRef[Str]>, read-only. Default value is C<[ '{{', '}}' ]>.

See C<DELIMITERS> option of L<Text::Template/"fill_in">.

Note: Our default delimiters are "alternative delimiters" for C<Text::Template>. It means escaping
delimiters with backslash does not work. See L<Text::Template/"Alternative Delimiters">.

=head2 package

Name of package to evaluate code fragments in.

Attribute introduces F<dist.ini> option with the same name.

C<Str>, read-only, optional.

See C<PACKAGE> option of L<Text::Template/"fill_in">.

=head2 prepend

Perl code to prepend to the beginning of every code fragment.

Attribute introduces F<dist.ini> multi-value option with the same name.

C<ArrayRef[Str]>, read-only, auto dereferenced. Default value is empty array. Another default value
may be specified by defining C<_build_prepend> method.

See C<PREPEND> option of L<Text::Template/"fill_in">.

=head2 broken

Callback to execute if a code fragment dies. Default callback formats detailed error message and
log it by calling C<log_error>.

C<CodeRef>, read-only, not an init argument. Default callback may be overridden by defining
C<_build_broken> method.

See C<BROKEN> option of L<Text::Template/"fill_in">.

=head2 tt_filename

Name of file being processed, or "template" if no filename was specified (e. g. when processing a
string and C<filename> option was not specified). Available only during template processing (e. g.
may be used in a C<broken> subroutine).

C<Str>, read-only, not an init arg.

=head2 tt_template

Template being processed. Available only during template processing (e. g. may be used in a
C<broken> subroutine).

C<Str>, read-only, not an init arg.

=head1 OBJECT METHODS

=head2 mvp_multivalue_args

The method tells C<Dist::Zilla> that C<prepend> is a multi-value option.

=head2 fill_in_string

    $string = '…';
    $result = $self->fill_in_string( $string, \%variables, \%extra_args );
    $result = $self->fill_in_string( $string );

The primary method of the role.

The interface is compatible with method of C<TextTemplate> role, so this role can be used as a
drop-in replacement for C<TextTemplate>. However, methods are implemented slightly differently, it
may cause subtle difference in behaviour.

Template variables C<$plugin> and C<$dist> are set by default, if not explicitly defined in
C<\%variables>.

The method creates C<Text::Template> object, enforces C<Text::Template> to respect C<filename>
argument (see L<FILENAME parameter has no
effect|https://rt.cpan.org/Ticket/Display.html?id=106093>), then calls C<fill_in> method on the
object. C<$template>, C<\%variables>, and C<package>, C<prepend>, C<broken> attribute values, and
finally C<%extra_args> are passed to the C<fill_in>.

C<%extra_args> is passed in the end intentionally, so caller may override any of arguments
specified by the method, for example:

    $self->fill_in_string( $template, undef, { package => 'MY' } );
        #   Execute code fragments in package MY regardless of $self->package value.

It also allows to cancel defining C<$plugin> and C<$dist> variables, e. g.:

    $self->fill_in_string( $template, undef, { hash => {} } );
        #   Do not define any variables for the template.

=head2 fill_in_file

    $file = Dist::Zilla::File::OnDisk->new( { … } );
    $result = $self->fill_in_file( $file, \%variables, \%extra_args );
    $result = $self->fill_in_file( $file );

Similar to C<fill_in_string>, but the first argument is not a string but a file. File content is
treated as template, and will be replaced with result of template processing. New file content will
also be returned.

=head1 NOTES

=head2 C<Text::Template> option spelling

C<Text::Template> allows a programmer to use different spelling of options: all-caps, first-caps,
all-lowercase, with and without leading dash, e. g.: C<HASH>, C<Hash>, C<hash>, C<-HASH>, C<-Hash>,
C<-hash>. This is documented feature.

C<Text::Template> recommends to pick a style and stick with it. (BTW, C<Text::Template>
documentation itself uses all-caps spelling.) This role picked all-lowercase style. This choice
have subtle consequences. Let us consider an example:

    $self->fill_in_string( $template, undef, { PACKAGE => 'MY' } );

Extra option C<PACKAGE> may or may not not have effect, depending on value of C<package> attribute
(i. e. presence or absence C<package> option in F<dist.ini> file), because (this is not documented)
spellings are not equal: different spellings have different priority. If C<PACKAGE> and C<package>
are specified simultaneously, C<package> wins, C<PACKAGE> loses.

This feature gives you a choice. If you want to ignore option specified by the user in F<dist.ini>
and provide your value, use all-lowercase option name. If you want to provide default which can be
overridden by the user, use all-caps options name.

=head2 C<filename> option

When C<Text::Template> reads template from a file, it uses the file name in error messages, e. g.:

    Undefined subroutine &foo called at filename.ext line n

where I<filename.ext> is the name of file containing the template. When C<Text::Template> processes
a string, it uses word "template" instead of file name, e. g.:

    Undefined subroutine &foo called at template line n

The option C<filename> allows the caller to override it:

    $self->fill_in_file( $file, undef, { filename => 'Assa.txt' } );

Error message would look like:

    Undefined subroutine &foo called at Assa.txt line n

It may seem this does not make much sense, but in our case (C<Dist::Zilla> and its plugins)
C<Text::Template> always processes strings and never reads files, because reading files is a duty
of C<Dist::Zilla::File::OnDisk> class. Thus, using C<filename> option is critical to provide good
error messages. Actually, C<fill_in_file> implementation looks like

    $file->content( $self->fill_in_string( $file->content, undef, { filename => $file->name } ) );

There are two problems with the option, though:

=over

=item *

C<Text::Template> does not document this option.

I believe it is a mistake and option should be documented.

=item *

C<Text::Template> ignores this option.

I am sure this is a bug and hope it will be fixed eventually. I am afraid it will not be fixed
soon, though.

Meanwhile, C<Dist-Zilla-Role-TextTemplater> implements a workaround to let the option work, so C<TextTemplater>
consumers can utilize the C<filename> option.

=back

=head1 WHY?

C<TextTemplate> role from C<Dist::Zilla> distribution v5.037 has the same great C<Text::Template>
engine under the hood, but lacks of control and has I<awful> error reporting.

=head2 Error Reporting

Let us consider an example. Have a look at F<dist.ini>:

    name     = Assa
    version  = 0.001
    [GatherDir]
    [PruneCruft]
    [GenerateFile/COPYING]
        filename = COPYING
        content  = This is {{$dist->name}}.
        content  =
        content  = {{$dst->license->notice}}
        is_template = 1
    [TemplateFiles]
        filename = lib/Assa.pm
        filename = lib/Assa/Manual.pod
    [MetaResources::Template]
        license = {{$dist->license->url}}
        …
    …

(Do you see a typo? Real F<dist.ini> can be much longer.) Now let us build the distribution:

    $ dzil build
    [DZ] beginning to build Assa
    Can't call method "license" on an undefined value at template line 3.

Oops. Ok, it is clear what was happened, but where?? The only weak clue is word "template".
However, there are many templates in the project: F<lib/Assa.pm>, F<lib/Assa/Manual.pod>, generated
on-the-fly F<COPYING>, and even resources in F<META.yml> — all this stuff is generated from
templates. Where is the problem? Have a happy bug hunting, dude.

If these plugins (namely, C<GenerateFile>, C<TemplateFiles> and C<MetaResources::Template>) were
written with C<TextTemplateB<r>> role, result would be:

    $ dzil build
    [DZ] beginning to build Assa
    [COPYING] Can't call method "license" on an undefined value at COPYING line 3.
    [COPYING] Problem code fragment begins at COPYING line 3.
    [COPYING] COPYING:
    [COPYING]     1: This is {{$dist->name}}.
    [COPYING]     2:
    [COPYING]     3: {{$dst->license->notice}}
    [COPYING]        ^^^ Code fragment begins in line above ^^^
    [COPYING]        ^^^ Code fragment died in line above ^^^
    Aborting...

See the difference.

=head2 Engine Control

C<TextTemplater> allows the end-user to specify C<delimiters>, C<package> and C<prepend> engine
options in F<dist.ini> file, while C<TextTemplate> allows to specify C<prepend> only
programmatically, and does I<not> allow to specify C<delimiters> and C<package>.

=head1 SEE ALSO

=over 4

=item L<Dist::Zilla>

=item L<Dist::Zilla::Role>

=item L<Dist::Zilla::Plugin>

=item L<Dist::Zilla::Role::TextTemplate>

=item L<Text::Template>

=back

=head1 AUTHOR

Van de Bugger <van.de.bugger@gmail.com>

=head1 COPYRIGHT AND LICENSE

Copyright © 2015 Van de Bugger

This file is part of perl-Dist-Zilla-Role-TextTemplater.

perl-Dist-Zilla-Role-TextTemplater is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.

perl-Dist-Zilla-Role-TextTemplater is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
perl-Dist-Zilla-Role-TextTemplater. If not, see <http://www.gnu.org/licenses/>.

=cut
