@@ -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) {