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