The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
Changes 071
MANIFEST 213
META.yml 22
Makefile.PL 11
README 770
README.md 075
benchmark/cascade.pl 11
benchmark/procs/method.pl 11
example/cascade.pl 11
inc/Module/Install/XSUtil.pm 22
lib/Text/Xslate/Compiler.pm 22
lib/Text/Xslate/HashWithDefault.pm 32
lib/Text/Xslate/Manual/Builtin.pod 08
lib/Text/Xslate/Manual/Cookbook.pod 21
lib/Text/Xslate/Manual/FAQ.pod 115
lib/Text/Xslate/PP/Method.pm 014
lib/Text/Xslate/PP/Opcode.pm 11
lib/Text/Xslate/PP.pm 321
lib/Text/Xslate/Parser.pm 44
lib/Text/Xslate/Syntax/Kolon.pm 56
lib/Text/Xslate.pm 2134
src/Text-Xslate.xs 1445
src/xslate_methods.xs 012
t/010_internals/022_signal.t 01
t/010_internals/030_die_in_funcs.t 101
t/010_internals/039_taint_issue84.t 041
t/020_interface/017_validate.t 040
t/050_builtins/002_autobox.t 05
t/100_plugin/002_import.t 15
t/900_bugs/033_ex_safe_render.t 038
t/900_bugs/036_vpath_utf8.t 018
t/900_bugs/037_text_str_key.t 023
t/900_bugs/038_conbine_flaged_utf8_and_other.t 050
t/900_bugs/039_issue96.t 023
t/900_bugs/040_issue95.t 078
t/900_bugs/041_cachedir_other_process.t 056
t/900_bugs/042_perl59_issue.t 020
t/900_bugs/043_issue107.t 015
t/900_bugs/044_empty_result.t 013
t/900_bugs/33_ex_safe_render.t 380
xshelper.h 11
xt/200_depended.t 10
42 files changed (This is a version diff) 194760
@@ -1,5 +1,76 @@
 Revision history for Perl extension Text::Xslate
 
+3.2.5 2014-07-15 08:18:31+0900
+    [TEST FIXES]
+        - No CGI.pm, which is no longer a standard module
+
+3.2.4 2014-04-27 17:18:15+0900
+    [TEST FIXES]
+        - Fix #122, #120 testing issue on windows (syohex)
+    [FEATURES]
+        - Add $array.first() and $array.last() methods (#116, #118 / shyohex)
+
+3.2.3 2014-04-23 07:32:39+0900
+    - Made a mistake in the release engineering, re-packaged on the correct
+      status.
+
+3.2.2 2014-04-23 07:29:42+0900
+    [BUG FIXES]
+    - Fix #105 SEGV on blead (reported by tokuhirom, fixed by syohex in #117)
+
+3.2.1 2014-04-17 07:55:38+0900
+    [BUG FIXES]
+    - Fix #111 (by syohex in #113) inputting "0" made a wrong result
+
+3.2.0 2014-04-04 07:39:59+0900
+    [BUG FIXES]
+    - Fix #107, #109 HashWithDefault should use "exists" (yappo, tokuhirom)
+
+3.1.2 2014-02-20 21:09:47+0900
+    [TEST]
+    - Add a test for github issue #105, which will fail on Perl 5.19.x
+
+3.1.1 2014-01-24 07:50:52+0900
+    [DOCUMENTS]
+    - Fix some typos (#102)
+    - Add an explanation of `validate()` (#101)
+
+    [BUG FIXES]
+    - Fix a race condition on making cache dirs (#103)
+
+3.1.0 2013-11-16 16:46:35+0900
+    [BUG FIXES]
+    - Close #96; $/ affected the parse() method
+    
+    [FEATURES]
+    - Add $xslate->validate($file) method to check template syntax
+
+3.0.2 2013-11-15 21:56:53+0900
+    [BUG FIXES]
+    - Fix a mojibake issue where utf8::upgrade() was always called when
+      loading caches (hanabukuro++)
+
+3.0.1 2013-11-04 12:27:51+0900
+    [TEST FIXES]
+    - Fix a test that might fail on a slow machine like Raspberry Pi
+      (Getty++)
+
+3.0.0 2013-10-18 08:59:22+0900
+    - No code changes from 2.1.0, just re-packaging for package managers
+
+2.1.0 2013-10-17 22:18:21+0900
+    [BUG FIXES]
+    - Fix an issue that multi-bytes string literals used for a hash key
+      was not dealt as a text string (@Niratama++)
+      WARNING: this change could break your code if you use multi-byte
+               text string as a hash key.
+
+2.0010 2013-10-07 21:56:12+0900
+    [BUG FIXES]
+    - Fix an issue that vpath with text-strings raised errors
+      on newer perls due to specification changes in PerlIO::scalar (#90)
+    - Documentation tweaks (#84, #86)
+
 2.0009 2013-07-08 10:49:47-0700
     [BUG FIXES]
     - Fix a tied hash issue on string concat, thanks to zxchris (#81)
@@ -113,7 +113,7 @@ Makefile.PL
 MANIFEST			This list of files
 MANIFEST.SKIP
 META.yml
-README
+README.md
 script/xslate
 src/Text-Xslate.xs
 src/xslate_methods.xs
@@ -156,6 +156,7 @@ t/010_internals/034_is_code_ref.t
 t/010_internals/036_merge_hash.t
 t/010_internals/037_find_file.t
 t/010_internals/038_suffix.t
+t/010_internals/039_taint_issue84.t
 t/010_internals/100_threads.t
 t/010_internals/200_leaktrace.t
 t/010_internals/300_explicit_pp.t
@@ -175,6 +176,7 @@ t/020_interface/013_slurp_template.t
 t/020_interface/014_customize_option.t
 t/020_interface/015_render_recursion.t
 t/020_interface/016_pre_process_handler.t
+t/020_interface/017_validate.t
 t/030_kolon/001_interpolate.t
 t/030_kolon/002_field.t
 t/030_kolon/003_for.t
@@ -294,9 +296,18 @@ t/900_bugs/029_fork_and_cache.t
 t/900_bugs/030_issue71.t
 t/900_bugs/031_yappo.t
 t/900_bugs/032_issue79.t
+t/900_bugs/033_ex_safe_render.t
 t/900_bugs/034_hash_key_utf8.t
 t/900_bugs/035_issue81_tiedhash.t
-t/900_bugs/33_ex_safe_render.t
+t/900_bugs/036_vpath_utf8.t
+t/900_bugs/037_text_str_key.t
+t/900_bugs/038_conbine_flaged_utf8_and_other.t
+t/900_bugs/039_issue96.t
+t/900_bugs/040_issue95.t
+t/900_bugs/041_cachedir_other_process.t
+t/900_bugs/042_perl59_issue.t
+t/900_bugs/043_issue107.t
+t/900_bugs/044_empty_result.t
 t/900_bugs/issue79/tmpl/contentA.tt
 t/900_bugs/issue79/tmpl/contentB.tt
 t/900_bugs/issue79/tmpl/wrapperA.tt
@@ -11,7 +11,7 @@ build_requires:
 configure_requires:
   Devel::PPPort: 3.19
   ExtUtils::MakeMaker: 6.59
-  ExtUtils::ParseXS: 2.21
+  ExtUtils::ParseXS: 3.18
 distribution_type: module
 dynamic_config: 1
 generated_by: 'Module::Install version 1.06'
@@ -41,4 +41,4 @@ resources:
   homepage: http://xslate.org/
   license: http://dev.perl.org/licenses/
   repository: https://github.com/xslate/p5-Text-Xslate
-version: 2.0009
+version: 3.2.5
@@ -8,7 +8,7 @@ BEGIN {
 	# author requires, or bundled modules
     my @devmods = qw(
         inc::Module::Install             1.06
-        Module::Install::XSUtil          0.44
+        Module::Install::XSUtil          0.45
         Module::Install::TestTarget      0.19
         Module::Install::AuthorTests     0.002
     );
@@ -1,77 +0,0 @@
-This is Perl module Text::Xslate.
-
-NAME
-
-Text::Xslate - Scalable template engine for Perl5
-
-SYNOPSIS
-
-    use Text::Xslate;
-
-    my $tx = Text::Xslate->new();
-
-    my %vars = (
-        title => 'A list of books',
-        books => [
-            { title => 'Islands in the stream' },
-            { title => 'Programming Perl'      },
-            # ...
-        ],
-    );
-
-    my $template = q{
-    <h1><: $title :></h1>
-    <ul>
-    : for $books -> $book {
-        <li><: $book.title :></li>
-    : } # for
-    </ul>
-    };
-
-    print $tx->render_string($template, \%vars);
-
-INSTALLATION
-
-Install cpanm (App::cpanminus) and then run the following command to install
-Xslate:
-
-    $ cpanm Text::Xslate
-
-If you get the distribution, unpack it and build it as per the usual:
-
-    $ tar xzf Text-Xslate-{version}.tar.gz
-    $ cd Text-Xslate-{version}
-    $ perl Makefile.PL
-    $ make && make test
-
-Then install it:
-
-    $ make install
-
-If you want to install it from the repository, you must install authoring
-tools.
-
-    $ cpanm < author/requires.cpanm
-
-DOCUMENTATION
-
-Text::Xslate documentation is available as in POD. So you can do:
-
-    $ perldoc Text::Xslate
-
-to read the documentation online with your favorite pager.
-
-RESOURCE
-
-    web site:     http://xslate.org/
-    repositories: http://github.com/xslate
-    mailing list: http://groups.google.com/group/xslate
-    irc         : irc://irc.perl.org/#xslate
-
-LICENSE AND COPYRIGHT
-
-Copyright (c) 2010, Fuji, Goro (gfx). All rights reserved.
-
-This library is free software; you can redistribute it and/or modify
-it under the same terms as Perl itself.
-
@@ -0,0 +1,75 @@
+# NAME [![Build Status](https://secure.travis-ci.org/xslate/p5-Text-Xslate.png)](http://travis-ci.org/xslate/p5-Text-Xslate)
+
+Text::Xslate - Scalable template engine for Perl5
+
+# SYNOPSIS
+
+    use Text::Xslate;
+
+    my $tx = Text::Xslate->new();
+
+    my %vars = (
+        title => 'A list of books',
+        books => [
+            { title => 'Islands in the stream' },
+            { title => 'Programming Perl'      },
+            # ...
+        ],
+    );
+
+    my $template = q{
+    <h1><: $title :></h1>
+    <ul>
+    : for $books -> $book {
+        <li><: $book.title :></li>
+    : } # for
+    </ul>
+    };
+
+    print $tx->render_string($template, \%vars);
+
+# INSTALLATION
+
+Install cpanm (App::cpanminus) and then run the following command to install
+Xslate:
+
+    $ cpanm Text::Xslate
+
+If you get the distribution, unpack it and build it as per the usual:
+
+    $ tar xzf Text-Xslate-{version}.tar.gz
+    $ cd Text-Xslate-{version}
+    $ perl Makefile.PL
+    $ make && make test
+
+Then install it:
+
+    $ make install
+
+If you want to install it from the repository, you must install authoring
+tools.
+
+    $ cpanm < author/requires.cpanm
+
+# DOCUMENTATION
+
+Text::Xslate documentation is available as in POD. So you can do:
+
+    $ perldoc Text::Xslate
+
+to read the documentation online with your favorite pager.
+
+# RESOURCE
+
+    web site:     http://xslate.org/
+    repositories: http://github.com/xslate
+    mailing list: http://groups.google.com/group/xslate
+    irc         : irc://irc.perl.org/#xslate
+
+# LICENSE AND COPYRIGHT
+
+Copyright (c) 2010, Fuji, Goro (gfx). All rights reserved.
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
@@ -19,7 +19,7 @@ my $cache = defined($args{'--cache'}) ? $args{'--cache'} : 2;
 
 {
     package BlogEntry;
-    use Any::Moose;
+    use Mouse;
     has title => (is => 'rw');
     has body  => (is => 'rw');
 }
@@ -16,7 +16,7 @@ my $n = shift(@ARGV) || 10;
 
 {
     package Pair;
-    use Any::Moose;
+    use Mouse;
 
     has [qw(key value)] => (
         is => 'rw',
@@ -5,7 +5,7 @@ use FindBin qw($Bin);
 
 {
     package BlogEntry;
-    use Any::Moose;
+    use Mouse;
     has title => (is => 'rw');
     has body  => (is => 'rw');
 }
@@ -3,7 +3,7 @@ package Module::Install::XSUtil;
 
 use 5.005_03;
 
-$VERSION = '0.44';
+$VERSION = '0.45';
 
 use Module::Install::Base;
 @ISA     = qw(Module::Install::Base);
@@ -18,7 +18,7 @@ use File::Find;
 use constant _VERBOSE => $ENV{MI_VERBOSE} ? 1 : 0;
 
 my %ConfigureRequires = (
-    'ExtUtils::ParseXS' => 2.21,
+    'ExtUtils::ParseXS' => 3.18, # shipped with Perl 5.18.0
 );
 
 my %BuildRequires = (
@@ -124,14 +124,14 @@ my %builtin = (
                         \&Text::Xslate::Util::is_hash_ref],
 );
 
-has lvar_id => ( # local varialbe id
+has lvar_id => ( # local variable id
     is  => 'rw',
     isa => 'Int',
 
     init_arg => undef,
 );
 
-has lvar => ( # local varialbe id table
+has lvar => ( # local variable id table
     is  => 'rw',
     isa => 'HashRef[Int]',
 
@@ -11,9 +11,8 @@ sub TIEHASH {
 
 sub FETCH {
     my($self, $key) = @_;
-    my $value = $self->[0]{$key};
-    if(defined $value) {
-        return $value;
+    if(exists $self->[0]{$key}) {
+        return $self->[0]{$key};
     }
     else {
         return ref($self->[1]) eq 'CODE'
@@ -35,6 +35,14 @@ The namespace of ARRAY references is C<array>.
 
 Returns true;
 
+=head3 C<$a.first()>
+
+Returns the first element of I<$a>.
+
+=head3 C<$a.last()>
+
+Returns the last element of I<$a>.
+
 =head3 C<$a.size()>
 
 Returns the number of elements of I<$a>.
@@ -1,4 +1,3 @@
-
 =head1 NAME
 
 Text::Xslate::Manual::Cookbook - How to cook Xslate templates
@@ -279,7 +278,7 @@ See also: L<I18N::Handle>, L<App::I18N>.
 =head2 How to load templates before C<fork()>ing?
 
 It is a good idea to load templates in preforking-model applications.
-Here is an example to to load all the templates which is in a given path:
+Here is an example to load all the templates which is in a given path:
 
     use File::Find;
 
@@ -92,6 +92,12 @@ number of C<malloc()> calls required to render a template.
 
 =back
 
+=head3 How can I throw errors in functions and/or methods?
+
+Handle warnings by C<warn_handler> and raises exceptions if needed.
+
+That's because Xslate catches exceptions in templates and emits them as warnings.
+
 =head2 Configuration
 
 =head3 When I create the Xslate instance?
@@ -302,7 +308,15 @@ L<Pickles>
 
 =head3 Where are examples which use Xslate in Catalyst?
 
-Not yet. Please blog it and it'll be introduced here.
+There is a real-world project that uses Xslate with Catalyst.
+
+L<https://github.com/duckduckgo/community-platform>
+
+Initializing Xslate: L<https://github.com/duckduckgo/community-platform/blob/master/lib/DDGC.pm#L268>
+
+Working on: L<https://dukgo.com/>
+
+Enjoy!
 
 =head2 Development and support
 
@@ -22,6 +22,18 @@ our $_st;
 
 our $_context;
 
+sub _array_first {
+    my($array_ref) = @_;
+    return $_st->bad_arg('first') if @_ != 1;
+    return $array_ref->[0];
+}
+
+sub _array_last {
+    my($array_ref) = @_;
+    return $_st->bad_arg('last') if @_ != 1;
+    return $array_ref->[-1];
+}
+
 sub _array_size {
     my($array_ref) = @_;
     return $_st->bad_arg('size') if @_ != 1;
@@ -118,6 +130,8 @@ sub _hash_merge {
 
 BEGIN {
     our %builtin_method = (
+        'array::first'   => \&_array_first,
+        'array::last'    => \&_array_last,
         'array::size'    => \&_array_size,
         'array::join'    => \&_array_join,
         'array::reverse' => \&_array_reverse,
@@ -2,7 +2,7 @@ package Text::Xslate::PP::Opcode;
 use Mouse;
 extends qw(Text::Xslate::PP::State);
 
-our $VERSION = '2.0009';
+our $VERSION = '3.2.5';
 
 use Carp ();
 use Scalar::Util ();
@@ -3,7 +3,7 @@ package Text::Xslate::PP;
 use 5.008_001;
 use strict;
 
-our $VERSION = '2.0009';
+our $VERSION = '3.2.5';
 
 BEGIN{
     $ENV{XSLATE} = ($ENV{XSLATE} || '') . '[pp]';
@@ -33,7 +33,7 @@ require Text::Xslate;
 
 my $state_class = 'Text::Xslate::PP::Opcode';
 
-$VERSION =~ s/_//; # for developpers versions
+$VERSION =~ s/_//; # for developers versions
 
 if(_PP_ERROR_VERBOSE) {
     Carp->import('verbose');
@@ -113,6 +113,24 @@ sub render {
     return tx_execute( $st, $vars );
 }
 
+sub validate {
+    my ( $self, $name ) = @_;
+
+    Carp::croak("Usage: Text::Xslate::render(self, name)")
+        if !( @_ == 2 );
+    unless ( ref $self ) {
+        Carp::croak( "Invalid xslate instance" );
+    }
+
+    if ( !defined $name ) {
+        Carp::croak("Xslate: Template name is not given");
+    }
+
+    local $self->{cache} = 0; # do not touch the cache
+    $self->tx_load_template( $name, 0 );
+    return;
+}
+
 sub current_engine {
     return defined($_current_st) ? $_current_st->engine : undef;
 }
@@ -658,7 +676,7 @@ Text::Xslate::PP - Yet another Text::Xslate runtime in pure Perl
 
 =head1 VERSION
 
-This document describes Text::Xslate::PP version 2.0009.
+This document describes Text::Xslate::PP version 3.2.5.
 
 =head1 DESCRIPTION
 
@@ -281,7 +281,7 @@ sub auto_chomp {
         #       \n        <:- ...  :>
         #       ^^^^^^^^^^
         $p->[1] = '';
-        $nl += chomp ${$s_ref};
+        $nl += (${$s_ref} =~ s/\n\z//xms);
     }
     return $nl;
 }
@@ -303,7 +303,7 @@ sub split :method {
     my $lex_tag_start = qr/\A \Q$tag_start\E ($CHOMP_FLAGS?)/xms;
 
     # 'text' is a something without newlines
-    # follwoing a newline, $tag_start, or end of the input
+    # following a newline, $tag_start, or end of the input
     my $lex_text = qr/\A ( [^\n]*? (?: \n | (?= \Q$tag_start\E ) | \z ) ) /xms;
 
     my $lex_comment = $parser->comment_pattern;
@@ -311,7 +311,7 @@ sub split :method {
 
     my $in_tag = 0;
 
-    while($_) {
+    while($_ ne '') {
         if($in_tag) {
             my $start = 0;
             my $pos;
@@ -412,7 +412,7 @@ sub preprocess {
             if($s =~ /\A \s* [}] \s* \z/xms){
                 $code .= $s;
             }
-            elsif(chomp $s) {
+            elsif($s =~ s/\n\z//xms) {
                 $code .= qq{$s\n};
             }
             else {
@@ -120,7 +120,7 @@ Logical operators (C<< ! && || // not and or >>)
 
 String operators (C<< ~ >>)
 
-    : "[" ~ $var ~ "]" # concatination
+    : "[" ~ $var ~ "]" # concatenation
 
 The operator precedence is very like Perl's:
 
@@ -202,7 +202,7 @@ via the dot-name syntax.
         : else {
             odd
         : }
-        : $i~.cycle("even", "odd") # => "even" -> "odd" -> "even" -> "odd" ...
+        : $~i.cycle("even", "odd") # => "even" -> "odd" -> "even" -> "odd" ...
     : }
 
 Supported iterator elements are C<index :Int>, C<count :Int>,
@@ -347,6 +347,7 @@ Template inclusion is a traditional way to extend templates.
 
     : include "foo.tx";
     : include "foo.tx" { var1 => value1, var2 => value2, ... };
+    : include "foo.tx" {$vars}; # use $vars as the params
 
 As C<cascade> does, C<include> allows barewords:
 
@@ -555,15 +556,15 @@ There are special keywords:
 
 =item __FILE__
 
-Indicates the current file name.
+Indicates the current file name. Equivalent to C<< Text::Xslate->current_file >>.
 
 =item __LINE__
 
-Indicates the current line number.
+Indicates the current line number. Equivalent to C<< Text::Xslate->current_line >>.
 
 =item __ROOT__
 
-Means the root of the parameters.
+Means the root of the parameters. Equivalent to C<< Text::Xslate->current_vars >>.
 
 =back
 
@@ -4,7 +4,7 @@ use 5.008_001;
 use strict;
 use warnings;
 
-our $VERSION = '2.0009';
+our $VERSION = '3.2.5';
 
 use Carp              ();
 use File::Spec        ();
@@ -51,10 +51,6 @@ if(!exists $INC{'Text/Xslate/PP.pm'}) {
 }
 sub USE_XS() { $use_xs }
 
-# workaround warnings about numeric when it is a developpers' version
-# it must be here because the bootstrap routine requires the under bar.
-$VERSION =~ s/_//;
-
 # for error messages (see T::X::Util)
 sub input_layer { ref($_[0]) ? $_[0]->{input_layer} : ':utf8' }
 
@@ -87,7 +83,7 @@ BEGIN {
     *_DEFAULT_CACHE_DIR = sub() { $cache_dir };
 }
 
-# the real defaults are dfined in the parser
+# the real defaults are defined in the parser
 my %parser_option = (
     line_start => undef,
     tag_start  => undef,
@@ -99,7 +95,7 @@ my %compiler_option = (
     syntax     => undef,
     type       => undef,
     header     => undef, # template augment
-    footer     => undef, # template agument
+    footer     => undef, # template augment
     macro      => undef, # template augment
 );
 
@@ -241,7 +237,7 @@ sub _resolve_function_aliases {
     my($self, $funcs) = @_;
 
     foreach my $f(values %{$funcs}) {
-        my %seen; # to avoid infinate loops
+        my %seen; # to avoid infinite loops
         while(!( ref($f) or Scalar::Util::looks_like_number($f) )) {
             my $v = $funcs->{$f} or $self->_error(
                "Cannot resolve a function alias '$f',"
@@ -375,10 +371,14 @@ sub load_file {
 sub slurp_template {
     my($self, $input_layer, $fullpath) = @_;
 
-    open my($source), '<' . $input_layer, $fullpath
-        or $self->_error("LoadError: Cannot open $fullpath for reading: $!");
-    local $/;
-    return scalar <$source>;
+    if (ref $fullpath eq 'SCALAR') {
+        return $$fullpath;
+    } else {
+        open my($source), '<' . $input_layer, $fullpath
+            or $self->_error("LoadError: Cannot open $fullpath for reading: $!");
+        local $/;
+        return scalar <$source>;
+    }
 }
 
 sub _load_source {
@@ -409,8 +409,10 @@ sub _load_source {
         my $cachedir      = File::Spec->catpath($volume, $dir, '');
         if(not -e $cachedir) {
             require File::Path;
-            eval { File::Path::mkpath($cachedir) }
-                or Carp::croak("Xslate: Cannot prepare cache directory $cachepath (ignored): $@");
+            File::Path::mkpath($cachedir);
+            if (! -e $cachedir) {
+                Carp::croak("Xslate: Cannot prepare cache directory $cachepath (ignored): $@");
+            }
         }
 
         my $tmpfile = sprintf('%s.%d.d', $cachepath, $$, $self);
@@ -524,7 +526,7 @@ sub _load_compiled {
         }
         elsif($c->[0] eq 'literal') {
             # force upgrade to avoid UTF-8 key issues
-            utf8::upgrade($c->[1]);
+            utf8::upgrade($c->[1]) if($is_utf8);
         }
         push @asm, $c;
     }
@@ -661,7 +663,7 @@ Text::Xslate - Scalable template engine for Perl5
 
 =head1 VERSION
 
-This document describes Text::Xslate version 2.0009.
+This document describes Text::Xslate version 3.2.5.
 
 =head1 SYNOPSIS
 
@@ -744,7 +746,7 @@ There are also benchmarks in F<benchmark/> directory in the Xslate distribution.
 =head3 Smart escaping for HTML metacharacters
 
 Xslate employs the B<smart escaping strategy>, where a template engine
-escapes all the HTML metacharacters in template expressionsi unless users
+escapes all the HTML metacharacters in template expressions unless users
 mark values as B<raw>.
 That is, the output is unlikely to prone to XSS.
 
@@ -779,17 +781,21 @@ Possible options are:
 Specifies the include paths, which may be directory names or virtual paths,
 i.e. HASH references which contain C<< $file_name => $content >> pairs.
 
+Note that if you use taint mode (C<-T>), you have to give absolute paths
+to C<path> and C<cache_dir>. Otherwise you'll get errors because they
+depend on the current working directory which might not be secure.
+
 =item C<< cache => $level // 1 >>
 
 Sets the cache level.
 
-If I<$level> == 1 (default), Xslate caches compiled templates on the disk, and
+If C<< $level == 1 >> (default), Xslate caches compiled templates on the disk, and
 checks the freshness of the original templates every time.
 
-If I<$level> E<gt>= 2, caches will be created but the freshness
+If C<< $level >= 2 >>, caches will be created but the freshness
 will not be checked.
 
-I<$level> == 0 uses no caches, which is provided for testing.
+C<< $level == 0 >> uses no caches, which is provided for testing.
 
 =item C<< cache_dir => $dir // "$ENV{HOME}/.xslate_cache" >>
 
@@ -873,7 +879,7 @@ Specifies the verbose level.
 
 If C<< $level == 0 >>, all the possible errors will be ignored.
 
-If C<< $level> >= 1 >> (default), trivial errors (e.g. to print nil) will be ignored,
+If C<< $level >= 1 >> (default), trivial errors (e.g. to print nil) will be ignored,
 but severe errors (e.g. for a method to throw the error) will be warned.
 
 If C<< $level >= 2 >>, all the possible errors will be warned.
@@ -963,6 +969,8 @@ For example:
 
 The first argument is the template text string, which can be both B<text strings> and C<byte strings>.
 
+This filter is applied only to files, not a string template for C<render_string>.
+
 =back
 
 =head3 B<< $tx->render($file, \%vars) :Str >>
@@ -1019,6 +1027,11 @@ This method is significant when it is called by template functions and methods.
 
 Adds the argument into the output buffer. This method is available on executing.
 
+=head3 B<< $tx->validate($file) :Void >>
+
+Checks whether the syntax of I<$file> is valid or invalid as Xslate.
+If it detects the invalid factor, this method throws the exception.
+
 =head2 Exportable functions
 
 =head3 C<< mark_raw($str :Str) :RawStr >>
@@ -298,7 +298,9 @@ tx_load_lvar(pTHX_ tx_state_t* const st, I32 const lvar_ix) { /* the guts of TX_
 
     assert(SvTYPE(cframe) == SVt_PVAV);
 
-    if(AvFILLp(cframe) < real_ix || SvREADONLY(AvARRAY(cframe)[real_ix])) {
+    if(AvFILLp(cframe) < real_ix
+       || AvARRAY(cframe)[real_ix] == NULL
+       || SvREADONLY(AvARRAY(cframe)[real_ix])) {
         av_store(cframe, real_ix, newSV(0));
     }
     st->pad = AvARRAY(cframe) + TXframe_START_LVAR;
@@ -783,6 +785,19 @@ tx_sv_is_macro(pTHX_ SV* const sv) {
     return FALSE;
 }
 
+XS(XS_Text__Xslate__macrocall); /* -Wmissing-prototype */
+XS(XS_Text__Xslate__macrocall){
+    dVAR; dSP; /* macrocall routine do dMARK, so we don't it here */
+    dMY_CXT;
+    SV* const macro = (SV*)CvXSUBANY(cv).any_ptr;
+    if(!(MY_CXT.current_st && macro)) {
+        croak("Macro is not callable outside of templates");
+    }
+    XPUSHs( tx_proccall(aTHX_ MY_CXT.current_st, macro, "macro") );
+    PUTBACK;
+    return;
+}
+
 /* called by tx_methodcall() */
 /* proc may be a Xslate macro or a Perl subroutine (code ref) */
 SV*
@@ -802,23 +817,19 @@ tx_proccall(pTHX_ tx_state_t* const txst, SV* const proc, const char* const name
 
         return TX_st_sa;
     }
+    else if (tx_sv_is_code_ref(aTHX_ proc) && CvXSUB((CV*)SvRV(proc)) == XS_Text__Xslate__macrocall) {
+        /* macro wrapper XSUB created by Text::Xslate::Type::as_code_ref() */
+        SV* const m = CvXSUBANY((CV*)SvRV(proc)).any_ptr;
+        sv_dump(proc);
+        sv_dump(m);
+        Perl_croak(aTHX_ "xxx");
+        return tx_proccall(aTHX_ TX_st, m, name);
+    }
     else {
         return tx_funcall(aTHX_ TX_st, proc, name);
     }
 }
 
-XS(XS_Text__Xslate__macrocall); /* -Wmissing-prototype */
-XS(XS_Text__Xslate__macrocall){
-    dVAR; dSP; /* macrocall routine do dMARK, so we don't it here */
-    dMY_CXT;
-    SV* const macro = (SV*)CvXSUBANY(cv).any_ptr;
-    if(!(MY_CXT.current_st && macro)) {
-        croak("Macro is not callable outside of templates");
-    }
-    XPUSHs( tx_proccall(aTHX_ MY_CXT.current_st, macro, "macro") );
-    PUTBACK;
-    return;
-}
 
 static void
 tx_macro_enter(pTHX_ tx_state_t* const txst, AV* const macro, tx_pc_t const retaddr) {
@@ -1427,7 +1438,7 @@ CODE:
                 if(tx_oparg[opnum] & TXARGf_KEY) { /* shared sv */
                     STRLEN len;
                     const char* const pv = SvPV_const(*arg, len);
-                    st.code[i].u_arg.sv = newSVpvn_share(pv, len, 0U);
+                    st.code[i].u_arg.sv = newSVpvn_share(pv, SvUTF8(*arg) ? -len : len, 0U);
                 }
                 else if(tx_oparg[opnum] & TXARGf_INT) { /* sviv */
                     SvIV_please(*arg);
@@ -1600,6 +1611,26 @@ CODE:
 }
 
 void
+validate(SV* self, SV* source)
+CODE:
+{
+    TAINT_NOT; /* All the SVs we'll create here are safe */
+
+    /* $_[0]: engine */
+    if(!(SvROK(self) && SvTYPE(SvRV(self)) == SVt_PVHV)) {
+        croak("Xslate: Invalid xslate instance: %s",
+            tx_neat(aTHX_ self));
+    }
+
+    SvGETMAGIC(source);
+    if(!SvOK(source)) {
+        croak("Xslate: Template name is not given");
+    }
+
+    tx_load_template(aTHX_ self, source, FALSE);
+}
+
+void
 current_engine(klass)
 CODE:
 {
@@ -83,6 +83,16 @@ tx_keys(pTHX_ SV* const hvref) {
 /* SCALAR */
 
 /* ARRAY */
+TXBM(array, first) {
+    SV **svp = av_fetch((AV*)SvRV(*MARK), 0, FALSE);
+    sv_setsv(retval, svp ? *svp : &PL_sv_undef);
+}
+
+TXBM(array, last) {
+    AV* const av = (AV*)SvRV(*MARK);
+    SV **svp = av_fetch(av, av_len(av), FALSE);
+    sv_setsv(retval, svp ? *svp : &PL_sv_undef);
+}
 
 TXBM(array, size) {
     sv_setiv(retval, av_len((AV*)SvRV(*MARK)) + 1);
@@ -377,6 +387,8 @@ TXBM(hash, merge) {
 }
 
 static const tx_builtin_method_t tx_builtin_method[] = {
+    TXBM_SETUP(array,  first,   0, 0),
+    TXBM_SETUP(array,  last,    0, 0),
     TXBM_SETUP(array,  size,    0, 0),
     TXBM_SETUP(array,  join,    1, 1),
     TXBM_SETUP(array,  reverse, 0, 0),
@@ -5,6 +5,7 @@ use Test::More;
 use Text::Xslate;
 
 my $tx = Text::Xslate->new();
+$tx->render_string(''); # load related modules
 
 eval {
     local $SIG{ALRM} = sub { die "TIMEOUT" };
@@ -11,18 +11,9 @@ use Text::Xslate;
         is  => 'rw',
         isa => 'Int',
     );
-
-    package MyXslate;
-    our @ISA = qw(Text::Xslate);
-
-    sub render_string {
-        my($self, @args) = @_;
-        local $self->{foo} = 'bar';
-        return $self->SUPER::render_string(@args);
-    }
 }
 
-my $tx = MyXslate->new(
+my $tx = Text::Xslate->new(
     module       => [qw(Carp) => [qw(confess croak)] ],
     warn_handler => sub { die @_ },
 );
@@ -0,0 +1,41 @@
+#!perl -wT
+
+use strict;
+use Test::More;
+
+use Text::Xslate;
+use File::Path;
+use Scalar::Util qw(tainted);
+use Cwd qw(getcwd);
+
+my $cwd;
+my $cache_dir;
+tainted($cwd) and die "cwd is tainted";
+
+BEGIN{
+    $cwd = getcwd();
+    if ($cwd =~ /(.+)/) {
+        $cwd = $1;
+    }
+    $cache_dir = $cwd . '/xxx_test_taint_issue84';
+}
+END{ rmtree($cache_dir) }
+
+tainted($cache_dir) and die "cache_dir is tainted";
+
+
+for (1 .. 2) {
+    my $tx = Text::Xslate->new(
+        path => [$cwd . '/t/template'],
+        cache_dir => $cache_dir,
+        cache     => 1,
+    );
+
+    for(1 .. 2) {
+        is $tx->render('hello.tx', { lang => 'Xslate'}),
+            "Hello, Xslate world!\n";
+        utime time()+rand(100), time()+rand(100), "$cwd/t/template/hello.tx";
+    }
+}
+
+done_testing;
@@ -0,0 +1,40 @@
+#!perl
+use strict;
+use warnings;
+use Test::More;
+
+use Text::Xslate;
+
+my %vpath = (
+    ok0 => <<'T',
+Hello, world!
+T
+
+    ok1=> <<'T',
+Hello, <: $xslate :> world!
+T
+
+    ng0=> <<'T',
+Hello, <: $xslate world!
+T
+
+    ng1=> <<'T',
+Hello, <: $xslate ??? :> world!
+T
+);
+
+my $tx = Text::Xslate->new(path => [\%vpath]);
+
+foreach my $name (qw(ok0 ok1)) {
+    eval { $tx->validate($name) };
+    ok !$@, $name;
+    note $@ if $@;
+}
+
+foreach my $name (qw(ng0 ng1)) {
+    eval { $tx->validate($name) };
+    ok $@, $name;
+    note $@ if $@;
+}
+
+done_testing;
@@ -60,6 +60,11 @@ my @set = (
     ['<: $a.merge($a).join(",") :>', { a => [1, 2, 3] }, '1,2,3,1,2,3'],
     ['<: $a.merge([1, 2, 3]).join(",") :>', { a => [0] }, '0,1,2,3'],
 
+    ['<: $a.first() :>', { a => [1, 2, 3] }, '1', 'get first element'],
+    ['<: $a.first() :>', { a => [] }, '', 'first for empty array'],
+    ['<: $a.last() :>', { a => [1, 2, 3] }, '3', 'get last element'],
+    ['<: $a.last() :>', { a => [] }, '', 'last for empty array'],
+
     # hash
     ['<: $h.size() :>', { h => {} },        '0', 'for hash'],
     ['<: $h.size() :>', { h => {a => 1, b => 2, c => 3} }, '3'],
@@ -6,9 +6,13 @@ use Text::Xslate qw(html_escape html_builder mark_raw);
 use Text::Xslate::Util qw(p);
 use Data::Dumper;
 use Time::localtime qw(localtime);
-use CGI qw(span);
+#use CGI qw(span);
 use Digest::MD5 qw(md5 md5_hex);
 
+sub span {
+    return "<span>@_</span>";
+}
+
 $Data::Dumper::Sortkeys = 1;
 
 my $tx = Text::Xslate->new(
@@ -0,0 +1,38 @@
+use strict;
+use warnings;
+
+use Test::More;
+
+{
+    package MyCounter;
+    sub new { my $class = shift;bless {@_} => $class }
+    sub incr  { shift->{count}++ }
+    sub decr  { shift->{count}-- }
+    sub count { shift->{count} }
+}
+
+use Text::Xslate;
+my $tx = Text::Xslate->new(
+    cache  => 1,
+    syntax => 'TTerse',
+    path => {
+        'recurse.tt' => q{
+            [%- MACRO mymacro BLOCK -%]
+                [%- CALL recurse_count.decr -%]
+                [%- IF recurse_count.count -%]
+                    [%- mymacro() -%]
+                [%- END -%]
+            [%- END -%]
+            [%- mymacro() -%]
+        },
+    },
+);
+
+ok $tx->render('recurse.tt', { recurse_count => MyCounter->new(count => 101) });
+eval {
+    $tx->render('recurse.tt', { recurse_count => MyCounter->new(count => 102) });
+};
+
+ok $tx->render('recurse.tt', { recurse_count => MyCounter->new(count => 101) });
+
+done_testing;
@@ -0,0 +1,18 @@
+#!perl -w
+# "Strings with code points over 0xFF may not be mapped into in-memory file handles"
+use strict;
+use warnings;
+use utf8;
+use Test::More;
+
+use Text::Xslate;
+
+my %vpath = (
+    entry => 'あいう'
+);
+
+my $tx = Text::Xslate->new( cache => 0, path => \%vpath );
+is $tx->render('entry'), 'あいう';
+
+done_testing;
+
@@ -0,0 +1,23 @@
+use strict;
+use warnings;
+use utf8;
+use Test::More;
+
+use Text::Xslate;
+
+
+my $tx = Text::Xslate->new(
+    cache => 0,
+);
+
+is $tx->render_string(<<'T'), 10;
+: my $h = { "こんにちは" => 10};
+:= $h["こんにちは"]
+T
+
+is $tx->render_string(<<'T', { key => "こんにちは"}), 10;
+: my $h = { "こんにちは" => 10};
+:= $h[$key]
+T
+
+done_testing;
@@ -0,0 +1,50 @@
+#!perl
+
+use strict;
+use warnings;
+
+BEGIN{
+#    $ENV{XSLATE} = ' dump=ast '; # @@@ @@@
+}
+use Text::Xslate;
+
+use Test::More;
+use File::Temp qw(tempdir);
+
+my %vpath = (
+    ascii => "あ<: foo('i') :>う",
+    utf   => "あ<: foo('い') :>う",
+);
+
+my $tmpdir = tempdir(DIR => ".", CLEANUP => 1);
+
+my %opts = (
+    path      => \%vpath,
+    cache     => 1,
+    cache_dir => $tmpdir,
+    type => 'html', # enable auto escape
+    input_layer => ':bytes',
+    function => {
+        foo => sub {
+            my $s = shift;
+#            warn "[$s][", (utf8::is_utf8($s) ? 'utf' : 'binary'), "]\n";
+            Text::Xslate::mark_raw($s);
+        },
+    },
+);
+
+my $tx = Text::Xslate->new(\%opts);
+
+foreach my $type ('original', 'cached'){
+    my $tx = Text::Xslate->new(%opts);
+
+    foreach my $try (1, 2){
+        is($tx->render('ascii'), q{あiう},  "$type $try ascii");
+        is($tx->render('utf'),   q{あいう},  "$type $try utf");
+    }
+}
+
+$tmpdir = tempdir(DIR => ".", CLEANUP => 1);
+
+done_testing;
+
@@ -0,0 +1,23 @@
+#!perl
+use strict;
+use warnings;
+
+use Test::More;
+
+use Text::Xslate::Syntax::Kolon;
+
+{
+    my $content = <<'T';
+: cascade bar {
+:     hoge => 'fuga'
+: }
+T
+    my $parser = Text::Xslate::Syntax::Kolon->new();
+
+    local $/;
+    $parser->parse($content);
+}
+
+pass;
+
+done_testing;
@@ -0,0 +1,78 @@
+#!perl
+# https://github.com/xslate/p5-Text-Xslate/issues/95
+use strict;
+use warnings;
+use Fatal qw(open close);
+use File::Path qw(remove_tree);
+use Test::More;
+
+use Text::Xslate;
+
+# here's some test template files
+my $base_a = <<'EOF';
+here is base A
+: block body -> {}
+EOF
+
+my $sub_a = <<'EOF';
+: cascade base
+: override body {
+this is sub A
+: }
+EOF
+
+my $sub_b = <<'EOF';
+: cascade base
+: override body {
+this is sub B
+: }
+EOF
+
+# remove old directories if they exist and re-create
+remove_tree('path_a', 'path_b');
+END { remove_tree('path_a', 'path_b') }
+mkdir 'path_a';
+mkdir 'path_b';
+
+write_file('path_a/base.tx', $base_a);
+write_file('path_a/sub.tx', $sub_a);
+
+my $tx = Text::Xslate->new(
+    path => ['path_b', 'path_a'],
+
+    cache => 1,
+);
+
+my $out = $tx->render('sub.tx');
+
+# expect both base and sub A since there is nothing in path B
+is($out, "here is base A\nthis is sub A\n", "cascade with base in same directory");
+
+# now a new path_b sub file, and render again
+write_file('path_b/sub.tx', $sub_b);
+my $out2 = $tx->render('sub.tx');
+
+# we should get the A base (since there is no B base) with the B sub (since that is first in path)
+{ local $TODO = 'not yet';
+is($out2, "here is base A\nthis is sub B\n", "cascade with base in different directory");
+}
+
+
+$tx = Text::Xslate->new(
+    path => ['path_b', 'path_a'],
+
+    cache => 1,
+);
+
+note 'after re-creating an Xslate instance';
+my $out3 = $tx->render('sub.tx');
+is($out3, "here is base A\nthis is sub B\n", "cascade with base in different directory");
+
+done_testing;
+
+sub write_file {
+    my($path, $content) = @_;
+    open my $fh, ">", $path;
+    print $fh $content;
+    close $fh;
+}
@@ -0,0 +1,56 @@
+#!perl
+use strict;
+use warnings;
+use Test::More;
+use File::Path qw(rmtree);
+use t::lib::Util;
+use File::Spec;
+use Time::HiRes qw(sleep);
+use Text::Xslate;
+
+plan skip_all => 'fork emulation does not work' if $^O eq 'MSWin32';
+
+rmtree(cache_dir);
+
+my $tx = Text::Xslate->new(
+ path      => [path],
+ cache     => 1,
+ cache_dir => cache_dir,
+);
+
+my $cache_dir = do {
+    my $fi = $tx->find_file("hello.tx");
+    my($volume, $dir) = File::Spec->splitpath($fi->{cachepath});
+    File::Spec->catpath($volume, $dir, '');
+};
+
+my $mkpath = File::Path->can('mkpath');
+no warnings 'redefine';
+local *File::Path::mkpath = sub {
+    my ($path) = @_;
+    if ($path eq $cache_dir) {
+        note 'waiting child process';
+        sleep 0.2;
+        ok $cache_dir, 'cache_dir seems created on child process';
+        note 'mkpath on parent pwaiting child process';
+    }
+    $mkpath->(@_);
+};
+
+ok ! -e $cache_dir, 'cache directory does not exists';
+
+my $pid = fork;
+
+BAIL_OUT 'fork failed' unless defined $pid;
+
+if ($pid) {
+    my $fi = $tx->load_file("hello.tx");
+    ok -e $cache_dir, 'cache directory exists';
+    done_testing;
+    rmtree(cache_dir);
+} else {
+    note 'waiting if(not -e $cachedir) {';
+    sleep 0.1;
+    note 'mkpath on child process';
+    $mkpath->($cache_dir);
+}
@@ -0,0 +1,20 @@
+#!perl
+# https://github.com/xslate/p5-Text-Xslate/issues/105
+use strict;
+use warnings;
+use utf8;
+use Test::More 0.96;
+
+# This code cause segmentation fault on Perl 5.19.[79].
+
+use Text::Xslate;
+
+my $xslate = Text::Xslate->new();
+$xslate->render_string(<<'...');
+: '/' ~ uri('a')
+: for 3 -> $n { }
+...
+
+pass;
+
+done_testing;
@@ -0,0 +1,15 @@
+#!perl
+# https://github.com/xslate/p5-Text-Xslate/issues/107
+use strict;
+use warnings;
+use Test::More;
+
+use Text::Xslate;
+use Text::Xslate::Util qw(hash_with_default);
+
+
+my $tx = Text::Xslate->new();
+is($tx->render_string('[<: $oops :>]', hash_with_default(+{}, sub { 'null' })), '[null]');
+is($tx->render_string('[<: $oops :>]', hash_with_default(+{ oops => undef }, sub { 'null' })), '[]');
+
+done_testing;
@@ -0,0 +1,13 @@
+#!perl
+# https://github.com/xslate/p5-Text-Xslate/issues/111
+use strict;
+use warnings;
+use Test::More;
+use Text::Xslate;
+
+my $tx = Text::Xslate->new();
+
+is($tx->render_string('1'), '1');
+is($tx->render_string('0'), '0');
+
+done_testing;
@@ -1,38 +0,0 @@
-use strict;
-use warnings;
-
-use Test::More;
-
-{
-    package MyCounter;
-    sub new { my $class = shift;bless {@_} => $class }
-    sub incr  { shift->{count}++ }
-    sub decr  { shift->{count}-- }
-    sub count { shift->{count} }
-}
-
-use Text::Xslate;
-my $tx = Text::Xslate->new(
-    cache  => 1,
-    syntax => 'TTerse',
-    path => {
-        'recurse.tt' => q{
-            [%- MACRO mymacro BLOCK -%]
-                [%- CALL recurse_count.decr -%]
-                [%- IF recurse_count.count -%]
-                    [%- mymacro() -%]
-                [%- END -%]
-            [%- END -%]
-            [%- mymacro() -%]
-        },
-    },
-);
-
-ok $tx->render('recurse.tt', { recurse_count => MyCounter->new(count => 101) });
-eval {
-    $tx->render('recurse.tt', { recurse_count => MyCounter->new(count => 102) });
-};
-
-ok $tx->render('recurse.tt', { recurse_count => MyCounter->new(count => 101) });
-
-done_testing;
@@ -1,4 +1,4 @@
-/* THIS FILE IS AUTOMATICALLY GENERATED BY Module::Install::XSUtil 0.44. */
+/* THIS FILE IS AUTOMATICALLY GENERATED BY Module::Install::XSUtil 0.45. */
 /*
 =head1 NAME
 
@@ -17,7 +17,6 @@ my $cpanm = which('cpanm') or plan skip_all => 'no cpanm';
 my @modules = qw(
     Text::Xslate::Bridge::TT2Like
     Catalyst::View::Xslate
-    MojoX::Renderer::Xslate
 );
 
 foreach my $mod(@modules) {