Search code examples
perlpluginshamltemplate-toolkit

Trying to create Template::Plugin::Filter for Haml


I'm trying to create a cpan module that integrates Text::Haml into Template Toolkit. I think that Haml is an interesting templating language but rather limited, it doesn't support loops or conditionals let alone more advanced things. But I can't seem to get eve a very simple version to work. The following are some test scripts that work to make sure my logic works.

Here's my attempt at the filter module using the Template::Plugin::Filter Documentation

use strict;
use warnings;
package Template::Plugin::Haml;

use Template::Plugin::Filter;

use parent 'Template::Plugin::Filter';

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

# thes aren't actually the problem
#   my $haml = Text::Haml->new;
#   my $html = $haml->render($text);
#   return $html;

    return $text;
}
1;

and some code to use it

#!/usr/bin/perl
# test0.pl
use strict;
use warnings;

use Template;

my $tt = Template->new;

my $vars = {};
my $output = \do{my $i};

$tt->process(\*DATA, $vars, $output);

print $$output;
__DATA__
[% USE Haml %]
[% FILTER Haml %]
#profile
[% END %]

but I get this warning Use of uninitialized value in print at test0.pl line 15, <DATA> line 1.

I know what this error means... but I'm not sure why the filter causing this. Any help would be appreciated

The following are some test scripts that other parts of my logic work, so that we're not looking at the wrong code to fix the issue. Otherwise they aren't really necessary for the problem

This shows how to use Template::Toolkit

#!/usr/bin/perl
# test1.pl - show how to use tt
use strict;
use warnings;

use Template;

my $tt = Template->new;

my $vars = {};
my $output = \do{my $i};

$tt->process(\*DATA, $vars, $output);

print $$output; # #profile
__DATA__
#profile

this shows us how to use Text::Haml

#!/usr/bin/perl
# test2.pl
use 5.010;
use strict;
use warnings;

use Text::Haml;

my $text = '#profile';
my $haml = Text::Haml->new;
my $html = $haml->render($text);
say $html; # <div id='profile'></div>

UPDATE 1

I've tried this (which is almost identical to the markdown plugin and one almost identical to the Textile plugin as well)

use strict;
use warnings;
package Template::Plugin::Haml;

use parent 'Template::Plugin::Filter';
use 'Text::Haml';

sub init {
    my $self = shift;
    $self->{_DYNAMIC} = 1;
    $self->install_filter( $self->{_ARGS}->[0] || 'haml');
    $self;
}

sub filter {
    my ( $self, $text, $args, $config ) = @_;

    my $haml = Text::Haml->new;
    return $haml->render($text);
}
1;

UPDATE 2 output from enabling DEBUG => 'all', at TT initialization

[Template::Provider] creating cache of unlimited slots for [ . ]
[Template::Service] process(GLOB(0x1719608), HASH(0x16f1650))
[Template::Context] template(GLOB(0x1719608))
[Template::Context] asking providers for [GLOB(0x1719608)] []
[Template::Provider] _load(GLOB(0x1719608), <no alias>)
[Template::Provider] _compile(HASH(0x1a947a0), <no compfile>)
[Template::Parser] compiled main template document block:
sub {
    my $context = shift || die "template sub called without context\n";
    my $stash   = $context->stash;
    my $output  = '';
    my $_tt_error;

    eval { BLOCK: {
#line 1 "input file handle"
$output .=  $context->debugging('msg', { 'line' => '1', 'text' => 'USE Haml', 'file' => 'input file handle' }); ## DEBUG ##
#line 1 "input file handle"
# USE
$stash->set('Haml',
            $context->plugin('Haml'));
#line 2 "input file handle"
$output .=  $context->debugging('msg', { 'line' => '2', 'text' => 'FILTER haml', 'file' => 'input file handle' }); ## DEBUG ##
#line 4 "input file handle"

# FILTER
$output .=  do {
    my $output = '';
    my $_tt_filter = $context->filter('haml')
              || $context->throw($context->error);

$output .=  "#profile";
#line 4 "input file handle"
$output .=  $context->debugging('msg', { 'line' => '4', 'text' => 'END', 'file' => 'input file handle' }); ## DEBUG ##

    &$_tt_filter($output);
};

    } };
    if ($@) {
        $_tt_error = $context->catch($@, \$output);
        die $_tt_error unless $_tt_error->type eq 'return';
    }

    return $output;
}
[Template::Service] PROCESS: Template::Document=HASH(0x1c69ba0)
[Template::Context] process([ Template::Document=HASH(0x1c69ba0) ], <no params>, <unlocalized>)
[Template::Context] template(Template::Document=HASH(0x1c69ba0))
[Template::Context] plugin(Haml, [ ])
[Template::Plugins] fetch(Haml, <no args>, Template::Context=HASH(0x1972040))
[Template::Plugins] loading Template/Plugin/Haml.pm (PLUGIN_BASE)
[Template::Plugins] calling Template::Plugin::Haml->load()
[Template::Plugins] Haml => Template::Plugin::Haml
[Template::Filters] store(haml, ARRAY(0x1c1f4d8))
[Template::Context] filter(haml, [ ]<no alias>)
[Template::Filters] fetch(haml, <no args>, Template::Context=HASH(0x1972040))
Use of uninitialized value in string eq at /usr/share/perl5/vendor_perl/Text/Haml.pm line 452, <DATA> line 1.
Use of uninitialized value in string eq at /usr/share/perl5/vendor_perl/Text/Haml.pm line 452, <DATA> line 1.
Use of uninitialized value in string eq at /usr/share/perl5/vendor_perl/Text/Haml.pm line 452, <DATA> line 1.
Use of uninitialized value in string eq at /usr/share/perl5/vendor_perl/Text/Haml.pm line 452, <DATA> line 1.
Use of uninitialized value in string eq at /usr/share/perl5/vendor_perl/Text/Haml.pm line 452, <DATA> line 1.
Use of uninitialized value in string eq at /usr/share/perl5/vendor_perl/Text/Haml.pm line 452, <DATA> line 1.
Use of uninitialized value in string eq at /usr/share/perl5/vendor_perl/Text/Haml.pm line 452, <DATA> line 1.
Use of uninitialized value in string eq at /usr/share/perl5/vendor_perl/Text/Haml.pm line 452, <DATA> line 1.
Use of uninitialized value in string eq at /usr/share/perl5/vendor_perl/Text/Haml.pm line 452, <DATA> line 1.
Use of uninitialized value in string eq at /usr/share/perl5/vendor_perl/Text/Haml.pm line 452, <DATA> line 1.
Use of uninitialized value in concatenation (.) or string at /usr/share/perl5/vendor_perl/Text/Haml.pm line 674, <DATA> line 1.
Use of uninitialized value in concatenation (.) or string at /usr/share/perl5/vendor_perl/Text/Haml.pm line 683, <DATA> line 1.

## input file handle line 1 : [% USE Haml %] ##

## input file handle line 2 : [% FILTER haml %] ##
<div id='profile'></div>
<>## input file handle line 4 : [% END %] ##</>

Solution

  • Here's a link to the final product on CPAN Template::Plugin::Haml

    Got it

    use strict;
    use warnings;
    package Template::Plugin::Haml;
    
    use parent 'Template::Plugin::Filter';
    use Text::Haml;
    
    sub init {
        my $self = shift;
        $self->{_DYNAMIC} = 1;
        $self->install_filter( $self->{_ARGS}->[0] || 'haml');
        $self;
    }
    
    sub filter {
        my ( $self, $text, $args, $config ) = @_;
    
        my $haml = Text::Haml->new;
        return $haml->render($text);
    }
    1;
    

    and test0.pl

    #!/usr/bin/perl
    # test0.pl
    use strict;
    use warnings;
    
    use Template;
    
    my $tt = Template->new; #or die $Template::Error, "\n";
    
    my $vars = {};
    my $output = \do{my $i};
    
    $tt->process(\*DATA, $vars, $output);
    
    print $$output; # \n\n<div id='profile'></div>\n\n
    __DATA__
    [% USE Haml %]
    [% FILTER haml %]
    #profile
    [% END %]
    

    seems I had quoted use 'Text::Haml' and that I needed some init code.