package Text::MicroMason::HTMLTemplate;
require Text::MicroMason::Base;
require Text::MicroMason::TemplateDir;
require Text::MicroMason::StoreOne;
require Text::MicroMason::HasParams;
push @ISA, map "Text::MicroMason::$_", qw( TemplateDir StoreOne HasParams );
use strict;
######################################################################
my %param_mapping = ( ### <<== INCOMPLETE ###
global_vars => 'loop_global_vars',
cache => '-CompileCache',
path => '-TemplatePaths',
);
######################################################################
sub output { (shift)->execute_again( @_ ) }
######################################################################
my $prefix_re = '[tT][mM][pP][lL]_';
sub lex_token {
# warn " Lexer: " . pos($_) . " of " . length($_) . "\n";
# Tags in format "", "", or ""
/\G \<(\/?)($prefix_re\w+)\s*(.*?)\> /gcxs
? ( ( $1 ? "tmpl_end" : lc($2) ) => { $_[0]->parse_args($3) } ) :
# Things that don't match the above
/\G ( (?: [^<] | <(?!\/?$prefix_re) )+ ) /gcxs ? ( 'text' => $1 ) :
# Lexer error
()
}
sub parse_args {
my $self = shift;
my $args = "$_[0]";
return () unless length($args);
return ( name => $args ) unless ( $args =~ /=/ );
my @tokens;
until ( $args =~ /\G\z/gc ) {
push ( @tokens,
$args =~ /\G \s* (\w+) \= (?: \"([^\"]+)\" | ( \w+ ) ) (?= \s | \z ) /gcxs
? ( lc($1) => ( defined($2) ? $2 : $3 ) ) :
$args =~ /\G ( .{0,20} ) /gcxs
&& die "Couldn't find applicable parsing rule at '$1'\n"
);
}
@tokens;
}
######################################################################
sub assemble_tmpl_var {
my ($self, $args) = @_;
my $output = "\$m->param( '$args->{name}' )";
if ( defined $args->{default} ) {
$output = "local \$_ = $output; defined ? \$_ : '$args->{default}'"
}
if ( $args->{escape} ) {
$output = "\$m->filter( $output, '$args->{escape}' )"
}
expr => "$output;"
}
sub assemble_tmpl_include {
my ($self, $args) = @_;
file => $args->{name}
}
sub assemble_tmpl_loop {
my ($self, $args) = @_;
if ( ! $self->{loop_context_vars} ) {
perl => q/foreach my $args ( $m->param( '/ . $args->{name} . q/' ) ) {
local $m->{params} = [ $args, $m->{loop_global_vars} ? @{$m->{params}} : () ];/
} else {
perl => q/my @loop = $m->param( '/ . $args->{name} . q/' );
foreach my $count ( 0 .. $#loop ) {
my $args = $loop[ $count ];
my %loop_context = (
__counter__ => $count,
__odd__ => ( $count % 2 ),
__first__ => ( $count == 0 ),
__inner__ => ( $count > 0 and $count < $#loop ),
__last__ => ( $count == $#loop ),
);
local $m->{params} = [ $args, \%loop_context, $m->{loop_global_vars} ? @{$m->{params}} : () ];
/
}
}
sub assemble_tmpl_if {
my ($self, $args) = @_;
perl => q/if ( $m->param( '/ . $args->{name} . q/' ) ) { /
}
sub assemble_tmpl_unless {
my ($self, $args) = @_;
perl => q/if ( ! $m->param( '/ . $args->{name} . q/' ) ) { /
}
sub assemble_tmpl_else {
perl => "} else {"
}
sub assemble_tmpl_end {
perl => "}"
}
######################################################################
use vars qw( %Filters );
sub defaults {
(shift)->NEXT('defaults'), filters => \%Filters,
}
# Output filtering
$Filters{1} = $Filters{html} = \&HTML::Entities::encode
if eval { require HTML::Entities};
$Filters{url} = \&URI::Escape::uri_escape if eval { require URI::Escape };
# $result = $mason->filter( @filters, $content );
sub filter {
my $self = shift;
my $content = pop;
foreach my $filter ( @_ ) {
my $function = ( ref $filter eq 'CODE' ) ? $filter :
$self->{filters}{ $filter } ||
$self->croak_msg("No definition for a filter named '$filter'" );
$content = &$function($content)
}
$content
}
######################################################################
1;
__END__
######################################################################
=head1 NAME
Text::MicroMason::HTMLTemplate - Alternate Syntax like HTML::Template
=head1 SYNOPSIS
Instead of using this class directly, pass its name to be mixed in:
use Text::MicroMason;
my $mason = Text::MicroMason::Base->new( -HTMLTemplate );
Use the standard compile and execute methods to parse and evalute templates:
print $mason->compile( text=>$template )->( @%args );
print $mason->execute( text=>$template, @args );
Or use HTML::Template's calling conventions:
$template = Text::MicroMason->new( -HTMLTemplate, filename=>'simple.tmpl' );
$template->param( %arguments );
print $template->output();
HTML::Template provides a syntax to embed values into a text template:
I'm sorry , I'm afraid I can't do that right now.
Good morning, !
Good afternoon, !
=head1 DESCRIPTION
This mixin class overrides several methods to allow MicroMason to emulate
the template syntax and some of the other features of HTML::Template.
This class automatically includes the following other mixins: TemplateDir, HasParams, and StoreOne.
=head2 Compatibility with HTML::Template
This is not a drop-in replacement for HTML::Template, as the implementation is quite different, but it should be able to process most existing templates without major changes.
This should allow current HTML::Template users to take advantage of
MicroMason's one-time compilation feature, which in theory could be faster
than HTML::Template's run-time interpretation. (No benchmarking yet.)
The following features of HTML::Template are not supported yet:
=over 4
=item *
Search path for files. (Candidate for separate mixin class or addition to TemplateDir.)
=item *
Many HTML::Template options are either unsupported or have different names and need to be mapped to equivalent sets of attributes. (Transform these in the new() method or croak if they're unsupported.)
=back
The following features of HTML::Template will likely never be supported due to fundamental differences in implementation:
=over 4
=item *
query() method
=back
Contributed patches to more closely support the behavior of HTML::Template
would be welcomed by the author.
=head2 Template Syntax
The following elements are recognized by the HTMLTemplate lexer:
=over 4
=item *
I
Anything not specifically parsed by the below rule is interpreted as literal text.
=item *
ETMPL_IE
A template tag with no attributes.
=item *
ETMPL_I IE
A template tag with a name attribute.
=item *
ETMPL_I NAME=I I