implement .wrap for templates
authorTony Cook <tony@develop-help.com>
Fri, 14 Jun 2013 05:53:42 +0000 (15:53 +1000)
committerTony Cook <tony@develop-help.com>
Fri, 14 Jun 2013 05:53:42 +0000 (15:53 +1000)
site/cgi-bin/modules/Squirrel/Template.pm
site/cgi-bin/modules/Squirrel/Template/Constants.pm
site/cgi-bin/modules/Squirrel/Template/Parser.pm
site/cgi-bin/modules/Squirrel/Template/Processor.pm
site/cgi-bin/modules/Squirrel/Template/Tokenizer.pm
t/020-templater/040-original.t

index 8c5c25b..019807d 100644 (file)
@@ -20,7 +20,7 @@ BEGIN {
 
 use constant DEBUG_GET_PARMS => 0;
 
-our $VERSION = "1.027";
+our $VERSION = "1.028";
 
 my %compile_cache;
 
@@ -869,6 +869,13 @@ C<< <:.while I<condition> :> I<content> <:.end :> >>
 
 Produce I<content> while I<condition> is true.
 
+=item *
+
+C<< <:.wrap I<name>, I<name>:I<value> ... :> >>
+
+Wrap content up until C<< <:.end wrap:> >> or end of file with the
+content from the file or macro I<name>.
+
 =back
 
 =head1 Special Variables
@@ -937,7 +944,7 @@ current - the current item in the iteration.
 
 This is set to the names and values supplied in a C<wrap> request, so:
 
-  <:= param.name :>
+  <:= params.name :>
 
 is equivalent to:
 
index dabefe7..4e2407f 100644 (file)
@@ -2,7 +2,7 @@ package Squirrel::Template::Constants;
 use strict;
 use Exporter qw(import);
 
-our $VERSION = "1.007";
+our $VERSION = "1.008";
 
 sub _define_sequence {
   my ($keys, $start) = @_;
@@ -44,7 +44,7 @@ my @node_comp = qw(NODE_COMP_FIRST);
 _define_sequence(\@node_comp, 4);
 my @node_with = qw(NODE_WITH_CONTENT NODE_WITH_END);
 _define_sequence(\@node_with, 6);
-my @node_wrap = qw(NODE_WRAP_FILENAME NODE_WRAP_ARGS NODE_WRAP_CONTENT);
+my @node_wrap = qw(NODE_WRAP_FILENAME NODE_WRAP_ARGS NODE_WRAP_CONTENT NODE_WRAP_END);
 _define_sequence(\@node_wrap, 4);
 my @node_switch = qw(NODE_SWITCH_CASES NODE_SWITCH_END);
 _define_sequence(\@node_switch, 5);
index a83c0a5..67075db 100644 (file)
@@ -2,7 +2,7 @@ package Squirrel::Template::Parser;
 use strict;
 use Squirrel::Template::Constants qw(:token :node);
 
-our $VERSION = "1.016";
+our $VERSION = "1.018";
 
 use constant TOK => 0;
 use constant TMPLT => 1;
@@ -440,6 +440,77 @@ sub _parse_wrap {
   }
 }
 
+sub _parse_ext_wrap {
+  my ($self, $wrap) = @_;
+
+  my $content = $self->_parse_content;
+  my $end = $self->[TOK]->get;
+
+  # it's not really the filename (yet)
+  my $tokens = Squirrel::Template::Expr::Tokenizer->new($wrap->[NODE_WRAP_FILENAME]);
+
+  my @errors;
+  my $parser = Squirrel::Template::Expr::Parser->new;
+  my $name_expr;
+  unless (eval { $name_expr = $parser->parse_tokens($tokens); 1 }) {
+    return $self->_error($wrap, "Could not parse expression: ".$@->[1]);
+  }
+
+  my @result;
+  my $next = $tokens->get;
+  my @args;
+  if ($next->[0] eq 'op,') {
+    unless (eval {
+      while ($next->[0] eq 'op,') {
+       my $key;
+       my $value;
+       $key = $parser->parse_tokens($tokens);
+       my $colon = $tokens->get;
+       $colon->[0] eq 'op:'
+         or die [ error => "Expected : but found $colon->[0]" ];
+       $value = $parser->parse_tokens($tokens);
+       push @args, [ $key, $value ];
+       $next = $tokens->get;
+      }
+
+      if ($next->[0] ne 'eof') {
+       die [ error => "Expected , or eof but found $next->[0]" ];
+      }
+      1;
+    }) {
+      return $self->_error($wrap, ref $@ ? $@->[0] : $@);
+    }
+  }
+  elsif ($next->[0] ne 'eof') {
+    push @errors, $self->_error($wrap, "Expected , or end of expression but found $next->[0]");
+  }
+
+  if ($end->[TOKEN_TYPE] eq 'end') {
+    if ($end->[TOKEN_END_TYPE] && $end->[TOKEN_END_TYPE] ne 'wrap') {
+      push @errors, $self->_error($end, "Expected '.end' or '.end wrap' for .wrap started $wrap->[TOKEN_FILENAME]:$wrap->[TOKEN_LINE] but found '.end $end->[TOKEN_END_TYPE]'");
+    }
+  }
+  elsif ($end->[TOKEN_TYPE] eq 'eof') {
+    $self->[TOK]->unget($end);
+  }
+  else {
+    $self->[TOK]->unget($end);
+    push @errors, $self->_error($end, "Expected '.end', '.end wrap' or eof for .wrap started $wrap->[TOKEN_FILENAME]:$wrap->[TOKEN_LINE] but found $end->[TOKEN_TYPE]");
+    $end = $self->_dummy($end, end => "");
+  }
+  $wrap->[NODE_WRAP_CONTENT] = $content;
+  $wrap->[NODE_WRAP_FILENAME] = $name_expr;
+  $wrap->[NODE_WRAP_ARGS] = \@args;
+  $wrap->[NODE_WRAP_END] = $end;
+
+  if (@errors) {
+    return $self->_comp($wrap, @errors);
+  }
+  else {
+    return $wrap;
+  }
+}
+
 sub _parse_define {
   my ($self, $define) = @_;
 
index f55a2fb..4ead9f4 100644 (file)
@@ -3,7 +3,7 @@ use strict;
 use Squirrel::Template::Constants qw(:node);
 use Scalar::Util ();
 
-our $VERSION = "1.023";
+our $VERSION = "1.024";
 
 use constant ACTS => 0;
 use constant TMPLT => 1;
@@ -501,6 +501,60 @@ sub _process_wrap {
   return ( @errors, @result );
 }
 
+sub _process_ext_wrap {
+  my ($self, $node) = @_;
+
+  my ($filename, $args, $content) =
+    @{$node}[NODE_WRAP_FILENAME, NODE_WRAP_ARGS, NODE_WRAP_CONTENT];
+
+  my %args;
+  my @result;
+  my @errors;
+  my $name;
+  if (eval {
+    $name = $self->[EVAL]->process($filename);
+    for my $arg (@$args) {
+      my $key = $self->[EVAL]->process($arg->[0]);
+      my $value = $self->[EVAL]->process($arg->[1]);
+      $args{$key} = $value;
+    }
+    1;
+  }) {
+    my $ctx = ".wrap '$name' from $node->[NODE_FILENAME]:$node->[NODE_LINE]";
+    if ($self->[TMPLT]->start_wrap(\%args)) {
+
+      my $parsed = $self->[TMPLT]->get_macro($name);
+      if (!$parsed) {
+       ($parsed, my $message) = $self->[TMPLT]->parse_file($name);
+       unless ($parsed) {
+         $self->[TMPLT]->trace_noimpl("call:$node->[NODE_FILENAME]:$node->[NODE_LINE]:$message\n");
+         push @result, $self->_error($node, "Loading wrap: $message");
+       }
+      }
+
+      if ($parsed) {
+       my $proc = __PACKAGE__->new($self->[ACTS], $self->[TMPLT],
+                                   sub { $self->process($content) });
+       push @result, $proc->process($parsed);
+      }
+      $self->[TMPLT]->end_wrap;
+    }
+    else {
+      push @result, $self->_error($node, "Starting wrap: too many levels?");
+    }
+  }
+  else {
+    @result =
+      (
+       $node->[NODE_ORIG],
+       $self->process($node->[NODE_WRAP_CONTENT]),
+       $node->[NODE_WRAP_END][NODE_ORIG],
+      );
+  }
+
+  return ( @errors, @result );
+}
+
 sub _process_wraphere {
   my ($self, $node) = @_;
 
index c549b3b..4e151ec 100644 (file)
@@ -2,7 +2,7 @@ package Squirrel::Template::Tokenizer;
 use strict;
 use Squirrel::Template::Constants qw(:token);
 
-our $VERSION = "1.013";
+our $VERSION = "1.014";
 
 use constant QUEUE => 0;
 use constant TEXT => 1;
@@ -109,6 +109,9 @@ sub get {
       elsif ($body =~ /\A\.call\s+(\S.*)?\z/s) {
        push @$queue, [ call => $tag, $line, $name, $1 ];
       }
+      elsif ($body =~ /\A\.wrap\s+(\S.*)?\z/s) {
+       push @$queue, [ ext_wrap => $tag, $line, $name, $1 ];
+      }
       elsif ($body =~ /\A\.iterateover\s+(\S.*)\z/s) {
        push @$queue, [ iterateover => $tag, $line, $name, $1 ];
       }
index f0cc910..546a3e5 100644 (file)
@@ -1,7 +1,7 @@
 #!perl -w
 # Basic tests for Squirrel::Template
 use strict;
-use Test::More tests => 180;
+use Test::More tests => 184;
 use HTML::Entities;
 
 sub template_test($$$$;$$);
@@ -107,7 +107,7 @@ abc
 abc
 OUTPUT
 
-  template_test(<<TEMPLATE, <<OUTPUT, "wrap", \%acts, "both");
+  template_test(<<TEMPLATE, <<OUTPUT, "wrap more", \%acts, "both");
 Before
 <:wrap wraptest.tmpl title=>[cat "foo " [str]], menu => 1, showtitle => "abc" -:>
 Alpha
@@ -170,6 +170,63 @@ TEMPLATE
 before
 * wrap here without being wrapped *
 after
+OUTPUT
+
+  template_test(<<TEMPLATE, <<OUTPUT, ".wrap simple", \%acts, "in", \%vars);
+<:.wrap "wraptest.tmpl", "title": "foo " _ str, "menu": 1, "showtitle":"abc" :>Alpha
+<:param menu:>
+<:param showtitle:>
+<:= params.showtitle :>
+TEMPLATE
+<title>foo ABC</title>
+Alpha
+1
+abc
+abc
+OUTPUT
+
+  template_test(<<TEMPLATE, <<OUTPUT, ".wrap unknown", \%acts, "both");
+<:.wrap "unknown.tmpl":>
+Body
+TEMPLATE
+* Loading wrap: File unknown.tmpl not found *
+OUTPUT
+
+  template_test(<<TEMPLATE, <<OUTPUT, ".wrap recursive", \%acts, "both", \%vars);
+<:.wrap "wrapself.tmpl", "title":"foo " _ str, "menu":1, "showtitle":"abc" :>Alpha
+<:= params.menu :>
+<:= params.showtitle :>
+TEMPLATE
+* Error starting wrap: Too many levels of wrap for 'wrapself.tmpl' *<title>foo ABC</title>
+<title>foo ABC</title>
+<title>foo ABC</title>
+<title>foo ABC</title>
+<title>foo ABC</title>
+<title>foo ABC</title>
+<title>foo ABC</title>
+<title>foo ABC</title>
+<title>foo ABC</title>
+<title>foo ABC</title>
+Alpha
+1
+abc
+OUTPUT
+
+  template_test(<<TEMPLATE, <<OUTPUT, ".wrap more", \%acts, "both", \%vars);
+Before
+<:.wrap "wraptest.tmpl", "title":"foo " _ str, "menu":1, "showtitle":"abc" -:>
+Alpha
+<:= params.menu:>
+<:= params.showtitle:>
+<:-.end wrap-:>
+After
+TEMPLATE
+Before
+<title>foo ABC</title>
+Alpha
+1
+abc
+After
 OUTPUT
 
   # undefined iterator - replacement should happen on the inside