├── .github └── workflows │ └── test.yml ├── .gitignore ├── Build.PL ├── Changes ├── HACKING ├── LICENSE ├── META.json ├── README.md ├── author ├── 040_signals_on_loading.t ├── augment.pl ├── bench_compile.pl ├── bench_deserialize.pl ├── cache.pl ├── chtags.pl ├── gen-graph.pl ├── large.pl ├── list_builtins.pl ├── modifiers.pl ├── pptest.pl ├── prof-large-file.pl ├── profile.pl ├── requires.cpanm ├── t_renumber.pl ├── tt.pl └── while-loop.pl ├── benchmark ├── cascade.pl ├── data_section.pl ├── demo-mt.pl ├── demo-tt.pl ├── expr.pl ├── expr_eq.pl ├── for.pl ├── include.pl ├── interpolate.pl ├── json.pl ├── procs │ ├── fib.pl │ ├── function.pl │ ├── keys.pl │ ├── map.pl │ ├── method.pl │ ├── sort.pl │ ├── uri_escape.pl │ └── values.pl ├── template │ ├── base.mt │ ├── base.tx │ ├── child.mt │ ├── child.tx │ ├── include.cs │ ├── include.ht │ ├── include.mt │ ├── include.tt │ ├── include.tx │ ├── list.cs │ ├── list.ht │ ├── list.mst │ ├── list.mt │ ├── list.tj │ ├── list.tt │ └── list.tx ├── x-poor-env.pl └── x-rich-env.pl ├── builder └── MyBuilder.pm ├── cpanfile ├── example ├── amon2.psgi ├── amon2.psgi.gold ├── autolink.pl ├── autolink.pl.gold ├── base.tx ├── bridge.pl ├── bridge.tx ├── cascade.pl ├── cascade.pl.gold ├── cascade.tx ├── chained-scope.pl ├── chained-scope.pl.gold ├── data_section.pl ├── data_section.pl.gold ├── dynamic_functions.pl ├── dynamic_functions.tx ├── err_handler.pl ├── fillinform.pl ├── fillinform.pl.gold ├── form.psgi ├── form.psgi.gold ├── hello.pl ├── hello.pl.gold ├── hello.tmpl ├── hello.tx ├── htmltemplate.pl ├── htparser.pl ├── i18n-data-localize.pl ├── i18n-data-localize.pl.gold ├── i18n.pl ├── i18n.pl.gold ├── list.pl ├── list.pl.gold ├── list.tx ├── locale │ └── ja.po ├── mojo.psgi ├── mojo.psgi.gold ├── put_js.pl ├── put_js.pl.gold ├── simple.psgi └── simple.psgi.gold ├── lib └── Text │ ├── Xslate.pm │ └── Xslate │ ├── Bridge.pm │ ├── Bridge │ └── Star.pm │ ├── Compiler.pm │ ├── HashWithDefault.pm │ ├── Manual.pod │ ├── Manual │ ├── Builtin.pod │ ├── Cookbook.pod │ ├── Debugging.pod │ └── FAQ.pod │ ├── PP.pm │ ├── PP │ ├── Method.pm │ ├── Opcode.pm │ ├── State.pm │ └── Type │ │ ├── Macro.pm │ │ ├── Pair.pm │ │ └── Raw.pm │ ├── Parser.pm │ ├── Runner.pm │ ├── Symbol.pm │ ├── Syntax │ ├── Kolon.pm │ ├── Metakolon.pm │ └── TTerse.pm │ ├── Type │ └── Raw.pm │ └── Util.pm ├── minil.toml ├── script └── xslate ├── src ├── Text-Xslate.xs ├── uri_unsafe.h ├── xslate.h ├── xslate_methods.xs └── xslate_opcode.inc ├── t ├── 000_load.t ├── 010_internals │ ├── 001_parse.t │ ├── 002_compile.t │ ├── 003_assemble.t │ ├── 004_magic.t │ ├── 005_load_file.t │ ├── 006_errhandler.t │ ├── 007_compile_errs.t │ ├── 008_files.t │ ├── 009_goto_address.t │ ├── 010_line_number.t │ ├── 011_missuse.t │ ├── 012_multilines.t │ ├── 013_deps_str.t │ ├── 014_deps_file.t │ ├── 015_deps_file2.t │ ├── 016_cached.t │ ├── 017_render.t │ ├── 018_opinfo.t │ ├── 019_runtime_error.t │ ├── 020_optimize.t │ ├── 021_edge_cases.t │ ├── 022_signal.t │ ├── 023_cachepath.t │ ├── 024_die_in_macros.t │ ├── 025_reset_hooks.t │ ├── 026_issue16_deep_recursion.t │ ├── 027_infinite_warn.t │ ├── 028_taint.t │ ├── 029_large_file.t │ ├── 030_die_in_funcs.t │ ├── 031_save_src.t │ ├── 032_encoding_error.t │ ├── 033_vpath_cache.t │ ├── 034_is_code_ref.t │ ├── 036_merge_hash.t │ ├── 037_find_file.t │ ├── 038_suffix.t │ ├── 039_taint_issue84.t │ ├── 100_threads.t │ ├── 200_leaktrace.t │ └── 300_explicit_pp.t ├── 020_interface │ ├── 001_parser_option.t │ ├── 002_myparser.t │ ├── 003_encoding.t │ ├── 004_no_vars.t │ ├── 005_util.t │ ├── 006_psgi.t │ ├── 007_data_section.t │ ├── 008_type.t │ ├── 009_hash_with_default.t │ ├── 010_err_encoding.t │ ├── 011_hwd_w_include_vars.t │ ├── 012_default_functions.t │ ├── 013_slurp_template.t │ ├── 014_customize_option.t │ ├── 015_render_recursion.t │ ├── 016_pre_process_handler.t │ └── 017_validate.t ├── 030_kolon │ ├── 001_interpolate.t │ ├── 002_field.t │ ├── 003_for.t │ ├── 004_if.t │ ├── 005_rel_ops.t │ ├── 006_arith_ops.t │ ├── 007_log_ops.t │ ├── 008_literal.t │ ├── 009_include.t │ ├── 010_escaped_str.t │ ├── 011_macro.t │ ├── 012_cascade.t │ ├── 013_more_field.t │ ├── 014_while.t │ ├── 015_methods.t │ ├── 016_funcs.t │ ├── 017_more_macro.t │ ├── 018_dynamic_filters.t │ ├── 019_explicit_interpolate.t │ ├── 020_chomp.t │ ├── 021_overlay.t │ ├── 022_given.t │ ├── 023_bitwise.t │ ├── 024_localize_vars.t │ ├── 025_objectliteral.t │ ├── 026_constant.t │ ├── 027_lambda.t │ ├── 028_smartmatch.t │ ├── 029_blockfilter.t │ ├── 030_xfuncs.t │ ├── 031_post_if.t │ ├── 032_include_bareword.t │ ├── 033_ov_forloop.t │ ├── 034_print.t │ ├── 035_include_w_vars.t │ ├── 036_foreach_else.t │ ├── 037_loop_ctl.t │ ├── 038_custom_esc.t │ ├── 039_root_vars.t │ └── 040_external_macro.t ├── 040_tterse │ ├── 001_parse.t │ ├── 002_interpolate.t │ ├── 003_field.t │ ├── 004_for.t │ ├── 005_if.t │ ├── 006_include.t │ ├── 007_funcs.t │ ├── 008_methods.t │ ├── 009_comments.t │ ├── 010_expr.t │ ├── 011_objectliterals.t │ ├── 012_macro.t │ ├── 013_wrapper.t │ ├── 014_set.t │ ├── 015_while.t │ ├── 016_call.t │ ├── 017_filter.t │ ├── 018_process.t │ ├── 019_switch.t │ ├── 020_chomp.t │ ├── 021_fake_use.t │ ├── 022_post_if.t │ ├── 023_loop_ctl.t │ ├── 024_for_else.t │ ├── 025_macro.t │ └── 100_not_supported.t ├── 050_builtins │ ├── 001_builtin_funcs.t │ ├── 002_autobox.t │ ├── 003_autobox_w_cb.t │ └── 004_copied.t ├── 100_plugin │ ├── 001_import_from.t │ ├── 002_import.t │ ├── 003_bridge.t │ ├── 004_star.t │ ├── 005_html_builder_module.t │ └── 100_error.t ├── 200_app │ ├── 001_hello.t │ ├── 002_tree.t │ ├── 003_runner.t │ ├── 004_encoding.t │ └── simple │ │ ├── dont_touch.tx │ │ ├── goodbye.tx │ │ └── hello.tx ├── 300_examples │ ├── 001_basic.t │ ├── 002_cascade.t │ ├── 003_metakolon.t │ └── 004_tterse.t ├── 900_bugs │ ├── 001_super.t │ ├── 002_addfunc.t │ ├── 003_frame_access.t │ ├── 004_errorhandling.t │ ├── 005_rec_include.t │ ├── 006_complex_lex.t │ ├── 007_uuv.t │ ├── 008_include_var.t │ ├── 009_quote_in_comments.t │ ├── 010_widechar_for_md5.t │ ├── 011_reserved_words.t │ ├── 012_ltgt_in_comments.t │ ├── 013_comments_lineno.t │ ├── 014_too_large_int.t │ ├── 015_my_scope.t │ ├── 016_x_in_tterse.t │ ├── 017_unallocated.t │ ├── 018_lvar_in_macro.pl │ ├── 019_nested_mm.t │ ├── 020_switch_foo.t │ ├── 021_cached_enc.t │ ├── 022_empty_if_block.t │ ├── 023_deploy_problem.t │ ├── 024_use_cache.t │ ├── 025_clobber-macro-args.t │ ├── 026_issue61.t │ ├── 027_issue65.t │ ├── 028_issue68.t │ ├── 029_fork_and_cache.t │ ├── 030_issue71.t │ ├── 031_yappo.t │ ├── 032_issue79.t │ ├── 033_ex_safe_render.t │ ├── 034_hash_key_utf8.t │ ├── 035_issue81_tiedhash.t │ ├── 036_vpath_utf8.t │ ├── 037_text_str_key.t │ ├── 038_conbine_flaged_utf8_and_other.t │ ├── 039_issue96.t │ ├── 040_issue95.t │ ├── 041_cachedir_other_process.t │ ├── 042_perl59_issue.t │ ├── 043_issue107.t │ ├── 044_empty_result.t │ ├── 045_issue130.t │ ├── 046_issue156.t │ ├── 046_issue88.t │ ├── 047_undef_concat.t │ ├── 048_issue172.t │ └── issue79 │ │ ├── tmpl │ │ ├── contentA.tt │ │ ├── contentB.tt │ │ ├── wrapperA.tt │ │ └── wrapperB.tt │ │ └── xslate.pl ├── lib │ ├── MyBridge2.pm │ ├── TTSimple.pm │ ├── Text │ │ └── Xslate │ │ │ └── Syntax │ │ │ └── Foo.pm │ ├── Util.pm │ └── UtilNoleak.pm └── template │ ├── common.tx │ ├── config.tt │ ├── eg │ ├── base.tx │ └── child.tx │ ├── error │ ├── bad_include.tx │ ├── bad_method.tx │ ├── bad_syntax.tx │ └── bad_tags.tx │ ├── footer1.tt │ ├── footer2.tt │ ├── for.tx │ ├── func.tx │ ├── header1.tt │ ├── header2.tt │ ├── hello.tt │ ├── hello.tx │ ├── hello.tx.mod │ ├── hello_sjis.tx │ ├── hello_utf8.tx │ ├── include.tt │ ├── include.tx │ ├── include2.tt │ ├── include2.tx │ ├── macro.tt │ ├── myapp │ ├── bad_redefine.tx │ ├── base.tx │ ├── base.tx.mod │ ├── cbar.tx │ ├── cfoo.tx │ └── derived.tx │ ├── oi │ ├── bad_base.tx │ └── bad_component.tx │ ├── other │ └── hello.tx │ ├── taint.tx │ ├── wrapper.tt │ └── wrapper_div.tt ├── tool ├── disasm.pl ├── opcode.PL ├── opcode_for_pp.PL └── uri_unsafe.PL └── xt ├── 01_podspell.t ├── 02_pod.t ├── 03_pod-coverage.t ├── 04_synopsis.t ├── 05_vars.t ├── 100_eg_pl.t ├── 101_eg_psgi.t └── 200_depended.t /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | tags-ignore: 8 | - '*' 9 | pull_request: 10 | 11 | jobs: 12 | linux: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | perl-version: 17 | - '5.8-buster' 18 | - '5.10-buster' 19 | - 'latest' 20 | container: 21 | image: perl:${{ matrix.perl-version }} 22 | steps: 23 | - uses: actions/checkout@v2 24 | - run: perl -V 25 | - run: cpanm --installdeps --notest --quiet --with-configure --with-develop . 26 | - run: perl Build.PL 27 | - run: ./Build 28 | - run: ./Build test 29 | windows: 30 | runs-on: windows-latest 31 | steps: 32 | - uses: actions/checkout@v2 33 | - run: perl -V 34 | - run: cpanm --installdeps --notest --quiet --with-configure --with-develop . 35 | - run: perl Build.PL 36 | - run: perl Build 37 | - run: perl Build test 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Text-Xslate-* 2 | .xslate_cache/ 3 | !.gitignore 4 | !.shipit 5 | !.proverc 6 | *~ 7 | *.o 8 | *.obj 9 | *.bs 10 | *.def 11 | Makefile* 12 | !Makefile.PL 13 | *blib 14 | META.yml 15 | MYMETA.* 16 | inc/ 17 | *.out 18 | *.bak 19 | ppport.h 20 | xshelper.h 21 | nytprof* 22 | cover_db* 23 | *.gcda 24 | *.gcno 25 | *.gcov 26 | *.stackdump 27 | *.c 28 | *.txc 29 | *.ttc 30 | *.cache 31 | xslate_ops.h 32 | lib/Text/Xslate/PP/Const.pm 33 | /Build 34 | /_build 35 | /lib/Text/Xslate.xs 36 | /src/xs_version.h 37 | -------------------------------------------------------------------------------- /HACKING: -------------------------------------------------------------------------------- 1 | NOTES FOR HACKERS 2 | 3 | Easy evaluation: 4 | $ xslate -e 'Hello, <: $ARGV[0] :> world!' Xslate 5 | 6 | Debugging options ($ENV{XSLATE}; or --debug for xslate(1)): 7 | dump=proto Output preprocessed proto templates 8 | dump=token Output token sequence 9 | dump=denote Output code denotation 10 | dump=gen Output code generation 11 | dump=ast Output abstract syntax tree 12 | dump=asm Output xslate assembly 13 | dump=asm:ix Output xslate assembly with indexes 14 | dump=pp Output Perl code PP::Booster generates 15 | optimize=0 Suppress optimization 16 | pp=verbose Make error messages verbose (by Carp->import('verbose')) 17 | 18 | Parser's terminology: 19 | arity Symbol class; used while compiling 20 | bp Binding power; or operator precedence 21 | nud Null denotation; processing values and prefix operators 22 | led Left denotation; processing infix and postfix operators 23 | std Statement denotation; processing statements 24 | 25 | Callbacks: functios, methods, and macros 26 | tx_call_sv() is a thin wrapper to Perl_call_sv() 27 | tx_funcall() tries to call Perl fuctions 28 | tx_proccall() tries to call macros, or fallback to tx_funcall() 29 | tx_methodcall() tries to call Perl methods, or fallback to tx_proccall() 30 | -------------------------------------------------------------------------------- /author/040_signals_on_loading.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | use Time::HiRes qw(sleep alarm); 7 | 8 | note $$; 9 | 10 | for (1..10) { 11 | my $tx = Text::Xslate->new(); 12 | 13 | eval { 14 | local $SIG{ALRM} = sub { die "TIMEOUT" }; 15 | alarm(0.010); 16 | $tx->render_string(q{: while true {} }); 17 | }; 18 | $@ =~ /TIMEOUT/ or note $@; 19 | } 20 | 21 | pass "program finished"; 22 | 23 | done_testing; 24 | -------------------------------------------------------------------------------- /author/augment.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Text::Xslate; 4 | 5 | my %template = ( 6 | 'base' => <<'T', 7 | : block hello -> { 8 | Hello, <: inner :>! 9 | : } 10 | T 11 | 12 | 'child' => <<'T', 13 | : cascade 'base' 14 | : augment hello -> { "Augment" } 15 | T 16 | ); 17 | 18 | my $tx = Text::Xslate->new( 19 | path => \%template, 20 | cache => 0, 21 | ); 22 | print $tx->render('child'); 23 | -------------------------------------------------------------------------------- /author/bench_compile.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Benchmark qw(:all); 4 | 5 | use Text::Xslate; 6 | 7 | my $tx = Text::Xslate->new(); 8 | my $tt = Text::Xslate->new(syntax => 'TTerse'); 9 | 10 | my $x = <<'T'; 11 | List: 12 | : for $data ->($item) { 13 | * <:= $item.title :> 14 | * <:= $item.title :> 15 | * <:= $item.title :> 16 | : } 17 | T 18 | 19 | my $y = <<'T'; 20 | List: 21 | [% FOREACH item IN data -%] 22 | * [% item.title %] 23 | * [% item.title %] 24 | * [% item.title %] 25 | [% END -%] 26 | T 27 | 28 | print "Parser: Kolon v.s. TTerse\n"; 29 | cmpthese 0, { 30 | kolon => sub { $tx->compile($x) }, 31 | tterse => sub { $tt->compile($y) }, 32 | }; 33 | -------------------------------------------------------------------------------- /author/bench_deserialize.pl: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use Benchmark qw(:all); 4 | 5 | BEGIN{ $ENV{PERL_JSON_BACKEND} = 'JSON::PP' } 6 | 7 | use Data::Serializer::JSON; 8 | use Data::Serializer::Storable; 9 | use Data::Serializer::Data::Dumper; 10 | 11 | use Test::More tests => 3; 12 | 13 | my $asm = [ 14 | map { [$_ => 42, undef, undef] } ('aa' .. 'zz') 15 | ]; 16 | print scalar @{$asm}, "\n"; 17 | 18 | my $j = Data::Serializer::JSON->serialize($asm); 19 | my $s = Data::Serializer::Storable->serialize($asm); 20 | my $d = Data::Serializer::Data::Dumper->serialize($asm); 21 | 22 | { 23 | is_deeply(Data::Serializer::JSON->deserialize($j), $asm); 24 | is_deeply(Data::Serializer::Storable->deserialize($s), $asm); 25 | is_deeply(Data::Serializer::Data::Dumper->deserialize($d), $asm); 26 | } 27 | 28 | cmpthese timethese -1 => { 29 | json => sub { Data::Serializer::JSON->deserialize($j) }, 30 | storable => sub { Data::Serializer::Storable->deserialize($s) }, 31 | datadumper => sub { Data::Serializer::Data::Dumper->deserialize($d) }, 32 | }; 33 | 34 | -------------------------------------------------------------------------------- /author/cache.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Text::Xslate; 3 | use Benchmark qw/ :all /; 4 | use Path::Class qw/ file /; 5 | END{ unlink "test.tx" } 6 | file("test.tx")->openw->print(q{[% FOR i IN list %] [% i %] [% END %]}); 7 | 8 | my $tx = Text::Xslate->new( syntax => "TTerse" ); 9 | print $tx->VERSION, "\n"; 10 | timethese 0, { 11 | tx => sub { 12 | $tx->render("test.tx", { list => [ 1 .. 100 ] }); 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /author/chtags.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use File::Find; 4 | use Fatal qw(open close); 5 | 6 | sub wanted { 7 | return if not -f $_; 8 | 9 | print "$_\n"; 10 | 11 | my $name = $_; 12 | 13 | open my $in, '<', $name; 14 | open my $out, '>', $name . ".tmp"; 15 | while(<$in>) { 16 | s/^([ \t]*) \Q?/$1:/xms; 17 | s/\Q/:>/xmsg; 19 | print $out $_; 20 | } 21 | close $in; 22 | close $out; 23 | 24 | rename "$name.tmp" => $name; 25 | } 26 | 27 | find({ 28 | wanted => \&wanted, 29 | }, qw(t lib example benchmark)); 30 | 31 | -------------------------------------------------------------------------------- /author/large.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Text::Xslate; 4 | 5 | my $file = shift(@ARGV) or die "No templae file supplied"; 6 | 7 | my $tx = Text::Xslate->new( 8 | path => 'author', 9 | cache => 0, 10 | ); 11 | 12 | $tx->render($file); 13 | 14 | -------------------------------------------------------------------------------- /author/list_builtins.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Text::Xslate; 4 | 5 | my $tx = Text::Xslate->new(); 6 | 7 | print join("\n", sort keys %{ $tx->{function} }), "\n"; 8 | 9 | -------------------------------------------------------------------------------- /author/modifiers.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use 5.010; 3 | use strict; 4 | { 5 | package Base; 6 | use Any::Moose; 7 | 8 | sub foo { say "Base::foo" } 9 | 10 | package Role; 11 | use Any::Moose '::Role'; 12 | 13 | before foo => sub { say "Role::before" }; 14 | after foo => sub { say "Role::after" }; 15 | 16 | package Derived; 17 | use Any::Moose; 18 | extends 'Base'; 19 | with 'Role'; 20 | 21 | before foo => sub { say "Derived::before" }; 22 | after foo => sub { say "Derived::after" }; 23 | } 24 | 25 | Derived->foo; 26 | -------------------------------------------------------------------------------- /author/pptest.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | BEGIN{ $ENV{XSLATE} ||= 'pp=booster;dump=pp;' } 3 | 4 | use strict; 5 | use Text::Xslate; 6 | 7 | my $tx = Text::Xslate->new(); 8 | 9 | $tx->render_string( <<'CODE', {} ); 10 | : macro foo -> $arg { 11 | Hello <:= $arg :>! 12 | : } 13 | : foo($value) 14 | CODE 15 | -------------------------------------------------------------------------------- /author/prof-large-file.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use File::Temp; 4 | use File::Basename; 5 | 6 | my $N = 500; 7 | 8 | my $tmp = File::Temp->new( 9 | UNLINK => 0, 10 | DIR => 'author', 11 | SUFFIX => '.tt', 12 | ); 13 | $tmp->unlink_on_destroy(1); 14 | 15 | my $tmpl = <<'XML'; 16 | 17 | [% aaa %] 18 | [% bbb %] 19 | 20 | XML 21 | 22 | print $tmp "\n"; 23 | 24 | for my $i(1 .. $N) { 25 | print $tmp $tmpl; 26 | } 27 | 28 | print $tmp "\n"; 29 | close $tmp; 30 | 31 | system($^X, 'author/large.pl', basename($tmp)) == 0 32 | or die "Failed to exec"; # wake up 33 | 34 | system($^X, '-d:NYTProf', 'author/large.pl', basename($tmp)) == 0 35 | or die "Failed to exec"; 36 | 37 | 38 | -------------------------------------------------------------------------------- /author/profile.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Text::Xslate; 4 | 5 | use UNIVERSAL(); # makes NYTProf happy 6 | 7 | { 8 | package BlogEntry; 9 | use Mouse; 10 | has title => (is => 'rw'); 11 | has body => (is => 'rw'); 12 | } 13 | 14 | my($cache, $n) = @ARGV; 15 | $cache //= 1; 16 | $n //= 100; 17 | 18 | my @blog_entries = map{ BlogEntry->new($_) } ( 19 | { 20 | title => 'Entry one', 21 | body => 'This is my first entry.', 22 | }, 23 | { 24 | title => 'Entry two', 25 | body => 'This is my second entry.', 26 | }, 27 | ) x 10; 28 | for(1 .. $n) { 29 | my $tx = Text::Xslate->new( 30 | path => ["./benchmark/template"], 31 | cache => $cache, 32 | ); 33 | 34 | $tx->render('child.tx', { blog_entries => \@blog_entries }); 35 | } 36 | 37 | print $INC{'Text/Xslate/Compiler.pm'} ? "compiled.\n" : "cached.\n"; 38 | -------------------------------------------------------------------------------- /author/requires.cpanm: -------------------------------------------------------------------------------- 1 | # for configure 2 | Module::Install 3 | Module::Install::XSUtil 4 | Module::Install::AuthorTests 5 | Module::Install::TestTarget 6 | Module::Install::Repository 7 | 8 | # for testing 9 | Test::Vars 10 | Test::Synopsis 11 | Test::Spellunker 12 | Test::LeakTrace 13 | Plack 14 | IPC::Run 15 | 16 | # for benchmarks 17 | Text::MicroTemplate::Extended 18 | Text::ClearSilver 19 | HTML::Template 20 | HTML::Template::Pro 21 | Template 22 | 23 | # for examples 24 | Data::Section::Simple 25 | HTML::FillInForm 26 | HTML::FillInForm::Lite 27 | HTML::Shakan 28 | URI::Find 29 | Mojolicious 30 | MojoX::Renderer::Xslate 31 | JavaScript::Value::Escape 32 | Locale::Maketext::Lexicon 33 | Data::Localize 34 | File::Which 35 | Amon2::Lite 36 | Catalyst::View::Xslate 37 | 38 | -------------------------------------------------------------------------------- /author/t_renumber.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | # re-number test files 3 | use strict; 4 | 5 | my $dir = shift(@ARGV) or die "Usage: $0 test-dir\n"; 6 | $dir =~ s{/$}{}; 7 | -d $dir or die "No such directory: $dir\n"; 8 | 9 | my $i = 0; 10 | foreach my $dir (sort { ($a =~ /(\d+)_\w+\.t$/)[0] <=> ($b =~ /(\d+)_\w+\.t$/)[0] } <$dir/*.t>) { 11 | my $n = ($dir =~ /(\d+)_\w+\.t$/)[0]; 12 | last if $n >= 100; 13 | 14 | (my $newdir = $dir) =~ s/(\d+)(_\w+\.t)$/ sprintf '%03d%s', ++$i, $2 /xmse; 15 | 16 | next if $dir eq $newdir; 17 | 18 | printf "%-36s => %-36s\n", $dir, $newdir; 19 | rename $dir => $newdir or die "Cannot rename $dir to $newdir: $!"; 20 | } 21 | -------------------------------------------------------------------------------- /author/tt.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Template; 4 | use Smart::Comments; 5 | 6 | my $t = Template->new(); 7 | 8 | $t->process(\<<'T', {}, \my $x) or die $t->error, "\n"; 9 | A 10 | [%- component -%] 11 | B 12 | T 13 | ### $x 14 | -------------------------------------------------------------------------------- /author/while-loop.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | BEGIN{ $ENV{XSLATE} ||= 'dump=asm;' } 3 | 4 | use strict; 5 | use Text::Xslate; 6 | 7 | my $tx = Text::Xslate->new( 8 | function => { f => sub { undef } }, 9 | ); 10 | 11 | print $tx->render_string(<<'TX', { x => shift }); 12 | : macro add -> $x, $y { $x + $y } 13 | : while(true) { 14 | : if(add(1, 2) > 0) { } 15 | : } 16 | TX 17 | -------------------------------------------------------------------------------- /benchmark/demo-mt.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | 4 | use Text::Xslate; 5 | use Text::MicroTemplate::File; 6 | 7 | use Time::HiRes qw(time); 8 | use FindBin qw($Bin); 9 | 10 | use Config; printf "Perl/%vd %s\n", $^V, $Config{archname}; 11 | foreach my $mod(qw(Text::Xslate Text::MicroTemplate)){ 12 | print $mod, '/', $mod->VERSION, "\n"; 13 | } 14 | my $path = "$Bin/template"; 15 | 16 | my $mt = Text::MicroTemplate::File->new( 17 | include_path => [$path], 18 | use_cache => 2, 19 | ); 20 | my $tx = Text::Xslate->new( 21 | path => [$path], 22 | cache_dir => '.xslate_cache', 23 | cache => 2, 24 | ); 25 | 26 | my %vars = ( 27 | data => [ 28 | ({ title => 'Programming Perl'}) x 100, 29 | ] 30 | ); 31 | { 32 | my $out = $mt->render_file('list.mt', \%vars); 33 | $tx->render('list.tx', \%vars) eq $out or die $out; 34 | } 35 | 36 | $| = 1; 37 | 38 | print "Text::MicrTemplate's render_file() x 1000\n"; 39 | my $start = time(); 40 | foreach (1 .. 1000) { 41 | print $_, "\r"; 42 | my $out = $mt->render_file('list.mt', \%vars); 43 | } 44 | print "\n"; 45 | my $mt_used = time() - $start; 46 | printf "Used: %.03f sec.\n", $mt_used; 47 | 48 | print "Text::Xslate's render() x 1000\n"; 49 | $start = time(); 50 | foreach (1 .. 1000) { 51 | print $_, "\r"; 52 | my $out = $tx->render('list.tx', \%vars); 53 | } 54 | print "\n"; 55 | my $tx_used = time() - $start; 56 | printf "Used: %.03f sec.\n", $tx_used; 57 | 58 | printf "In this benchmark, Xslate is about %.01f times faster than Text::MicroTemplate.\n", 59 | $mt_used / $tx_used; 60 | -------------------------------------------------------------------------------- /benchmark/demo-tt.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | 4 | use Text::Xslate; 5 | use Template; 6 | 7 | use Time::HiRes qw(time); 8 | use FindBin qw($Bin); 9 | 10 | use Config; printf "Perl/%vd %s\n", $^V, $Config{archname}; 11 | foreach my $mod(qw(Text::Xslate Template)){ 12 | print $mod, '/', $mod->VERSION, "\n"; 13 | } 14 | 15 | my $file = 'list.tt'; 16 | 17 | my $tt = Template->new( 18 | INCLUDE_PATH => ["$Bin/template"], 19 | COMPILE_EXT => '.out', 20 | ); 21 | my $tx = Text::Xslate->new( 22 | syntax => 'TTerse', 23 | path => ["$Bin/template"], 24 | cache_dir => '.xslate_cache', 25 | cache => 2, 26 | ); 27 | 28 | my %vars = ( 29 | data => [ 30 | ({ title => 'Programming Perl'}) x 100, 31 | ] 32 | ); 33 | { 34 | my $out; 35 | $tt->process($file, \%vars, \$out); 36 | $tx->render($file, \%vars) eq $out 37 | or die $tx->render($file, \%vars), "\n", $out; 38 | } 39 | 40 | $| = 1; 41 | 42 | print "Template-Toolkit's process() x 1000\n"; 43 | my $start = time(); 44 | foreach (1 .. 1000) { 45 | print $_, "\r"; 46 | $tt->process($file, \%vars, \my $out); 47 | } 48 | print "\n"; 49 | my $tt_used = time() - $start; 50 | printf "Used: %.03f sec.\n", $tt_used; 51 | 52 | print "Text::Xslate's render() x 1000\n"; 53 | $start = time(); 54 | foreach (1 .. 1000) { 55 | print $_, "\r"; 56 | my $out = $tx->render($file, \%vars); 57 | } 58 | print "\n"; 59 | my $tx_used = time() - $start; 60 | printf "Used: %.03f sec.\n", $tx_used; 61 | 62 | printf "In this benchmark, Xslate is about %.01f times faster than Template-Tookit.\n", 63 | $tt_used / $tx_used; 64 | -------------------------------------------------------------------------------- /benchmark/expr.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | 4 | use Text::Xslate; 5 | use Text::MicroTemplate qw(build_mt); 6 | 7 | use Benchmark qw(:all); 8 | use Config; printf "Perl/%vd %s\n", $^V, $Config{archname}; 9 | use Test::More; 10 | 11 | foreach my $mod(qw(Text::Xslate Text::MicroTemplate)){ 12 | print $mod, '/', $mod->VERSION, "\n"; 13 | } 14 | 15 | my $n = shift(@ARGV) || 100; 16 | 17 | my %vpath = ( 18 | expr => "Hello, <:= \$value + 1 :> world!\n" x $n, 19 | ); 20 | 21 | my $tx = Text::Xslate->new( 22 | path => \%vpath, 23 | cache_dir => '.xslate_cache', 24 | cache => 2, 25 | ); 26 | 27 | my $mt = build_mt(qq{Hello, {value} + 1 ?> world!\n} x $n); 28 | 29 | my $subst_tmpl = qq{Hello, %value% world!\n} x $n; 30 | 31 | my $vars = { 32 | value => '41', 33 | }; 34 | 35 | { 36 | plan tests => 2; 37 | my $expected = $tx->render(expr => $vars); 38 | is $mt->($vars), $expected, 'MT'; 39 | 40 | my $body = [$subst_tmpl]; 41 | $body->[0] =~ s/%(\w+)%/ $vars->{$1} + 1 /eg; 42 | is $body->[0], $expected, 's///g'; 43 | } 44 | 45 | # suppose PSGI response body 46 | cmpthese -1 => { 47 | xslate => sub { 48 | my $body = [$tx->render(expr => $vars)]; 49 | return; 50 | }, 51 | mt => sub { 52 | my $body = [$mt->($vars)]; 53 | return; 54 | }, 55 | 's///g' => sub { 56 | my $body = [$subst_tmpl]; 57 | $body->[0] =~ s/%(\w+)%/ $vars->{$1} + 1 /eg; 58 | return; 59 | }, 60 | }; 61 | 62 | -------------------------------------------------------------------------------- /benchmark/expr_eq.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | 4 | use Text::Xslate; 5 | use Text::MicroTemplate qw(build_mt); 6 | 7 | use Benchmark qw(:all); 8 | use Config; printf "Perl/%vd %s\n", $^V, $Config{archname}; 9 | use Test::More; 10 | 11 | foreach my $mod(qw(Text::Xslate Text::MicroTemplate)){ 12 | print $mod, '/', $mod->VERSION, "\n"; 13 | } 14 | 15 | my $n = shift(@ARGV) || 100; 16 | 17 | my %vpath = ( 18 | expr_eq => "Hello, <:= \$value == 42 ? 'Xslate' : 'unlikely' :> world!\n" x $n, 19 | ); 20 | 21 | my $tx = Text::Xslate->new( 22 | path => \%vpath, 23 | cache_dir => '.xslate_cache', 24 | cache => 2, 25 | ); 26 | 27 | my $mt = build_mt(qq{Hello, {value} == 42 ? 'Xslate' : 'unlikely' ?> world!\n} x $n); 28 | 29 | my $subst_tmpl = qq{Hello, %value% world!\n} x $n; 30 | 31 | my $vars = { 32 | value => '41', 33 | }; 34 | 35 | { 36 | plan tests => 2; 37 | my $expected = $tx->render(expr_eq => $vars); 38 | is $mt->($vars), $expected, 'MT'; 39 | 40 | my $body = [$subst_tmpl]; 41 | $body->[0] =~ s/%(\w+)%/ $vars->{$1} == 42 ? 'Xslate' : 'unlikely' /eg; 42 | is $body->[0], $expected, 's///g'; 43 | } 44 | # suppose PSGI response body 45 | 46 | cmpthese -1 => { 47 | xslate => sub { 48 | my $body = [$tx->render(expr_eq => $vars)]; 49 | return; 50 | }, 51 | mt => sub { 52 | my $body = [$mt->($vars)]; 53 | return; 54 | }, 55 | 's///g' => sub { 56 | my $body = [$subst_tmpl]; 57 | $body->[0] =~ s/%(\w+)%/ $vars->{$1} + 1 /eg; 58 | return; 59 | }, 60 | }; 61 | 62 | -------------------------------------------------------------------------------- /benchmark/json.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | 4 | use Text::Xslate; 5 | use JSON::XS; 6 | 7 | use Benchmark qw(:all); 8 | use Config; printf "Perl/%vd %s\n", $^V, $Config{archname}; 9 | 10 | foreach my $mod(qw(Text::Xslate JSON::XS)){ 11 | print $mod, '/', $mod->VERSION, "\n"; 12 | } 13 | 14 | my $n = shift(@ARGV) || 10; 15 | 16 | my %vpath = ( 17 | json => <<'TX', 18 | 23 | TX 24 | ); 25 | 26 | my $tx = Text::Xslate->new( 27 | path => \%vpath, 28 | cache_dir => '.xslate_cache', 29 | cache => 2, 30 | ); 31 | 32 | my $json = JSON::XS->new(); 33 | 34 | my %vars = ( 35 | books => [( 36 | { title => 'Islands in the stream', 37 | author => 'Ernest Hemingway' }, 38 | { title => 'Beautiful code', 39 | author => 'Brian Kernighan, Jon Bentley, et. al.' }, 40 | { title => q{Atkinson and Hilgard's Introduction to Psychology With Infotrac}, # ' 41 | author => 'Edward E. Smith, et. al.' }, 42 | { title => 'Programming Perl', 43 | author => 'Larry Wall, et.al.' }, 44 | { title => 'Compilers: Principles, Techniques, and Tools', 45 | author => 'Alfred V. Aho, et. al.' }, 46 | ) x $n], 47 | ); 48 | 49 | if(0) { 50 | print $tx->render(json => \%vars); 51 | print $json->encode(\%vars); 52 | } 53 | 54 | cmpthese -1 => { 55 | xslate => sub { 56 | my $body = $tx->render(json => \%vars); 57 | return; 58 | }, 59 | json => sub { 60 | my $body = $json->encode(\%vars); 61 | return; 62 | }, 63 | }; 64 | -------------------------------------------------------------------------------- /benchmark/procs/fib.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | 4 | use Text::Xslate; 5 | use Text::MicroTemplate qw(build_mt); 6 | 7 | use Benchmark qw(:all); 8 | use Config; printf "Perl/%vd %s\n", $^V, $Config{archname}; 9 | use Test::More; 10 | 11 | foreach my $mod(qw(Text::Xslate Text::MicroTemplate)){ 12 | print $mod, '/', $mod->VERSION, "\n"; 13 | } 14 | my $n = shift(@ARGV) || 10; 15 | 16 | my %vpath = ( 17 | fib => <<'TX', 18 | : macro fib -> $n { 19 | : $n <= 1 ? 1 : fib($n - 1) + fib($n - 2); 20 | : } 21 | : fib($x); 22 | TX 23 | ); 24 | 25 | my $tx = Text::Xslate->new( 26 | path => \%vpath, 27 | cache_dir => '.xslate_cache', 28 | cache => 2, 29 | ); 30 | 31 | my $mt = build_mt(<<'MT'); 32 | ? sub fib { 33 | ? my($n) = @_; 34 | ? $n <= 1 ? 1 : fib($n - 1) + fib($n - 2); 35 | ? } 36 | ?= fib($_[0]->{x}) 37 | MT 38 | 39 | my $vars = { 40 | x => $n, 41 | }; 42 | 43 | { 44 | plan tests => 1; 45 | my $out = $mt->($vars); 46 | chomp $out; 47 | is $out, $tx->render(fib => $vars), 'MT' 48 | or die; 49 | } 50 | # suppose PSGI response body 51 | print "fib($n) = ", $tx->render(fib => $vars), "\n"; 52 | cmpthese -1 => { 53 | xslate => sub { 54 | my $body = [$tx->render(fib => $vars)]; 55 | return; 56 | }, 57 | mt => sub { 58 | my $body = [$mt->($vars)]; 59 | return; 60 | }, 61 | }; 62 | 63 | -------------------------------------------------------------------------------- /benchmark/procs/function.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | 4 | use Text::Xslate; 5 | use Text::MicroTemplate qw(build_mt); 6 | 7 | use Benchmark qw(:all); 8 | use Config; printf "Perl/%vd %s\n", $^V, $Config{archname}; 9 | 10 | foreach my $mod(qw(Text::Xslate Text::MicroTemplate)){ 11 | print $mod, '/', $mod->VERSION, "\n"; 12 | } 13 | 14 | my $n = shift(@ARGV) || 100; 15 | 16 | my %vpath = ( 17 | function => <<'TX' x $n, 18 | Hello, <:= $lang | uc :> world! 19 | TX 20 | ); 21 | my $tx = Text::Xslate->new( 22 | path => \%vpath, 23 | cache_dir => '.xslate_cache', 24 | cache => 2, 25 | 26 | function => { uc => sub { uc $_[0] } }, 27 | ); 28 | 29 | my $mt = build_mt("Hello, {lang}) ?> world!\n" x $n); 30 | 31 | my $subst_tmpl = qq{Hello, %lang% world!\n} x $n; 32 | 33 | my $vars = { 34 | lang => 'Template', 35 | }; 36 | 37 | $tx->render(function => $vars) eq $mt->($vars) 38 | or die $tx->render(function => $vars); 39 | 40 | # suppose PSGI response body 41 | 42 | cmpthese -1 => { 43 | xslate => sub { 44 | my $body = [$tx->render(function => $vars)]; 45 | return; 46 | }, 47 | mt => sub { 48 | my $body = [$mt->($vars)]; 49 | return; 50 | }, 51 | 's///g' => sub { 52 | my $body = [$subst_tmpl]; 53 | $body->[0] =~ s/%(\w+)%/uc($vars->{$1})/eg; 54 | return; 55 | }, 56 | }; 57 | 58 | -------------------------------------------------------------------------------- /benchmark/procs/keys.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | 4 | use Text::Xslate; 5 | use Text::MicroTemplate qw(build_mt); 6 | 7 | use Benchmark qw(:all); 8 | use Config; printf "Perl/%vd %s\n", $^V, $Config{archname}; 9 | use Test::More; 10 | 11 | foreach my $mod(qw(Text::Xslate Text::MicroTemplate)){ 12 | print $mod, '/', $mod->VERSION, "\n"; 13 | } 14 | 15 | my $n = shift(@ARGV) || 10; 16 | 17 | my %vpath = ( 18 | keys => <<'TX' x $n, 19 | : for $h.keys() -> $k { 20 | [<: $k :>] 21 | : } 22 | TX 23 | ); 24 | 25 | my $tx = Text::Xslate->new( 26 | path => \%vpath, 27 | cache_dir => '.xslate_cache', 28 | cache => 2, 29 | ); 30 | 31 | my $mt = build_mt(<<'T' x $n); 32 | ? for my $k(sort keys %{ $_[0]->{h} }) { 33 | [] 34 | ? } 35 | T 36 | 37 | my $subst_tmpl = qq{Hello, %value% world!\n} x $n; 38 | 39 | my $vars = { 40 | h => { %ENV }, 41 | }; 42 | 43 | { 44 | plan tests => 1; 45 | is $mt->($vars), $tx->render(keys => $vars), 'MT' 46 | or exit(1); 47 | } 48 | 49 | # suppose PSGI response body 50 | cmpthese -1 => { 51 | xslate => sub { 52 | my $body = [$tx->render(keys => $vars)]; 53 | return; 54 | }, 55 | mt => sub { 56 | my $body = [$mt->($vars)]; 57 | return; 58 | }, 59 | }; 60 | 61 | -------------------------------------------------------------------------------- /benchmark/procs/map.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | 4 | use Text::Xslate; 5 | use Text::MicroTemplate qw(build_mt); 6 | 7 | use Benchmark qw(:all); 8 | use Config; printf "Perl/%vd %s\n", $^V, $Config{archname}; 9 | use Test::More; 10 | 11 | foreach my $mod(qw(Text::Xslate Text::MicroTemplate)){ 12 | print $mod, '/', $mod->VERSION, "\n"; 13 | } 14 | 15 | my $n = shift(@ARGV) || 10; 16 | 17 | my %vpath = ( 18 | map => <<'TX' x $n, 19 | : for $data.map(-> $x { $x + 1 }) -> $v { 20 | [<: $v :>] 21 | : } 22 | TX 23 | ); 24 | 25 | my $tx = Text::Xslate->new( 26 | path => \%vpath, 27 | cache_dir => '.xslate_cache', 28 | cache => 2, 29 | ); 30 | 31 | my $mt = build_mt(<<'T' x $n); 32 | ? { 33 | ? my $data = $_[0]->{data}; 34 | ? for my $v(map{ $_ + 1 } @{$data}) { 35 | [] 36 | ? } 37 | ? } 38 | T 39 | 40 | my $vars = { 41 | data => [ 1 .. 100 ], 42 | }; 43 | 44 | { 45 | plan tests => 1; 46 | is $mt->($vars), $tx->render(map => $vars), 'MT' 47 | or exit(1); 48 | } 49 | # suppose PSGI response body 50 | cmpthese -1 => { 51 | xslate => sub { 52 | my $body = [$tx->render(map => $vars)]; 53 | return; 54 | }, 55 | mt => sub { 56 | my $body = [$mt->($vars)]; 57 | return; 58 | }, 59 | }; 60 | 61 | -------------------------------------------------------------------------------- /benchmark/procs/sort.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | 4 | use Text::Xslate; 5 | use Text::MicroTemplate qw(build_mt); 6 | 7 | use Benchmark qw(:all); 8 | use Config; printf "Perl/%vd %s\n", $^V, $Config{archname}; 9 | use Test::More; 10 | 11 | foreach my $mod(qw(Text::Xslate Text::MicroTemplate)){ 12 | print $mod, '/', $mod->VERSION, "\n"; 13 | } 14 | 15 | my $n = shift(@ARGV) || 10; 16 | 17 | my %vpath = ( 18 | sort => <<'TX' x $n, 19 | : for $data.sort(-> $a, $b { $a.value <=> $b.value }) -> $v { 20 | [<: $v.value :>] 21 | : } 22 | TX 23 | ); 24 | 25 | my $tx = Text::Xslate->new( 26 | path => \%vpath, 27 | cache_dir => '.xslate_cache', 28 | cache => 2, 29 | ); 30 | 31 | my $mt = build_mt(<<'T' x $n); 32 | ? { 33 | ? my $data = $_[0]->{data}; 34 | ? for my $v(sort { $a->{value} <=> $b->{value} } @{$data}) { 35 | [{value} ?>] 36 | ? } 37 | ? } 38 | T 39 | 40 | my $vars = { 41 | data => [ map { +{ value => $_ } } reverse 1 .. 100 ], 42 | }; 43 | 44 | { 45 | plan tests => 1; 46 | is $mt->($vars), $tx->render(sort => $vars), 'MT' 47 | or exit(1); 48 | } 49 | # suppose PSGI response body 50 | print q{Benchmark of sort-by-values ($a.sort( -> $a, $b { $a.value <=> $b.value })):}, "\n"; 51 | cmpthese -1 => { 52 | xslate => sub { 53 | my $body = [$tx->render(sort => $vars)]; 54 | return; 55 | }, 56 | mt => sub { 57 | my $body = [$mt->($vars)]; 58 | return; 59 | }, 60 | }; 61 | 62 | -------------------------------------------------------------------------------- /benchmark/procs/uri_escape.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | 4 | use Text::Xslate; 5 | 6 | use URI::Escape; 7 | use URI::Escape::XS; 8 | 9 | use Benchmark qw(:all); 10 | use Config; printf "Perl/%vd %s\n", $^V, $Config{archname}; 11 | 12 | foreach my $mod(qw(Text::Xslate URI::Escape URI::Escape::XS)){ 13 | print $mod, '/', $mod->VERSION, "\n"; 14 | } 15 | 16 | my $n = shift(@ARGV) || 100; 17 | 18 | my %vpath = ( 19 | builtin_uri => <<'TX' x $n, 20 | Hello, <: $lang | uri :> world! 21 | TX 22 | uri_escape_pp => <<'TX' x $n, 23 | Hello, <: $lang | uri_pp :> world! 24 | TX 25 | 26 | uri_escape_xs => <<'TX' x $n, 27 | Hello, <: $lang | uri_xs :> world! 28 | TX 29 | 30 | ); 31 | my $tx = Text::Xslate->new( 32 | path => \%vpath, 33 | cache_dir => '.xslate_cache', 34 | cache => 2, 35 | 36 | function => { 37 | uri_pp => \&uri_escape, 38 | uri_xs => \&encodeURIComponent, 39 | }, 40 | ); 41 | 42 | my $vars = { 43 | lang => '/Text::Xslate/', 44 | }; 45 | 46 | { 47 | use Test::More; 48 | plan tests => 2; 49 | is $tx->render(uri_escape_pp => $vars), $tx->render(builtin_uri => $vars), 'URI::Escape'; 50 | is $tx->render(uri_escape_xs => $vars), $tx->render(builtin_uri => $vars), 'URI::Escape:XS'; 51 | } 52 | 53 | # suppose PSGI response body 54 | cmpthese -1 => { 55 | builtin => sub { 56 | my $body = [$tx->render(builtin_uri => $vars)]; 57 | }, 58 | 'URI::Escape' => sub { 59 | my $body = [$tx->render(uri_escape_pp => $vars)]; 60 | }, 61 | 'URI::Escape::XS' => sub { 62 | my $body = [$tx->render(uri_escape_xs => $vars)]; 63 | }, 64 | }; 65 | 66 | -------------------------------------------------------------------------------- /benchmark/procs/values.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | 4 | use Text::Xslate; 5 | use Text::MicroTemplate qw(build_mt); 6 | 7 | use Benchmark qw(:all); 8 | use Config; printf "Perl/%vd %s\n", $^V, $Config{archname}; 9 | use Test::More; 10 | 11 | foreach my $mod(qw(Text::Xslate Text::MicroTemplate)){ 12 | print $mod, '/', $mod->VERSION, "\n"; 13 | } 14 | 15 | my $n = shift(@ARGV) || 10; 16 | 17 | my %vpath = ( 18 | values => <<'TX' x $n, 19 | : for $h.values() -> $v { 20 | [<: $v :>] 21 | : } 22 | TX 23 | ); 24 | 25 | my $tx = Text::Xslate->new( 26 | path => \%vpath, 27 | cache_dir => '.xslate_cache', 28 | cache => 2, 29 | ); 30 | 31 | my $mt = build_mt(<<'T' x $n); 32 | ? { 33 | ? my $h = $_[0]->{h}; 34 | ? for my $v(map{ $h->{$_ } } sort keys %{ $h }) { 35 | [] 36 | ? } 37 | ? } 38 | T 39 | 40 | my $subst_tmpl = qq{Hello, %value% world!\n} x $n; 41 | 42 | my $vars = { 43 | h => { %ENV }, 44 | }; 45 | 46 | { 47 | plan tests => 1; 48 | is $mt->($vars), $tx->render(values => $vars), 'MT' 49 | or exit(1); 50 | } 51 | # suppose PSGI response body 52 | cmpthese -1 => { 53 | xslate => sub { 54 | my $body = [$tx->render(values => $vars)]; 55 | return; 56 | }, 57 | mt => sub { 58 | my $body = [$mt->($vars)]; 59 | return; 60 | }, 61 | }; 62 | 63 | -------------------------------------------------------------------------------- /benchmark/template/base.mt: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | <? block title => sub { ?>My amazing site<? }; ?> 7 | 8 | 9 | 10 | 18 | 19 |
20 | sub { } ?> 21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /benchmark/template/base.tx: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | <: block title -> { :>My amazing site<: } :> 7 | 8 | 9 | 10 | 18 | 19 |
20 | <: block content -> { ; } :> 21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /benchmark/template/child.mt: -------------------------------------------------------------------------------- 1 | ? extends "base" 2 | 3 | sub { ?>My amazing blog 4 | 5 | ? block content => sub { 6 | ? for my $entry(@$blog_entries) { 7 |

title ?>

8 |

body ?>

9 | ? } 10 | ? }; 11 | -------------------------------------------------------------------------------- /benchmark/template/child.tx: -------------------------------------------------------------------------------- 1 | : cascade base 2 | 3 | <: around title ->{ :>My amazing blog<: } :> 4 | 5 | : around content -> { 6 | : for $blog_entries -> ($entry) { 7 |

<:= $entry.title :>

8 |

<:= $entry.body :>

9 | : } # end for 10 | : } # end content 11 | -------------------------------------------------------------------------------- /benchmark/template/include.cs: -------------------------------------------------------------------------------- 1 | Include: 2 | -------------------------------------------------------------------------------- /benchmark/template/include.ht: -------------------------------------------------------------------------------- 1 | Include: 2 | 3 | -------------------------------------------------------------------------------- /benchmark/template/include.mt: -------------------------------------------------------------------------------- 1 | Include: 2 | ?= include "list", @_ 3 | -------------------------------------------------------------------------------- /benchmark/template/include.tt: -------------------------------------------------------------------------------- 1 | Include: 2 | [% INCLUDE "list.tt" -%] 3 | -------------------------------------------------------------------------------- /benchmark/template/include.tx: -------------------------------------------------------------------------------- 1 | Include: 2 | : include "list.tx" 3 | -------------------------------------------------------------------------------- /benchmark/template/list.cs: -------------------------------------------------------------------------------- 1 | List: 2 | 3 | * 4 | * 5 | * 6 | 7 | -------------------------------------------------------------------------------- /benchmark/template/list.ht: -------------------------------------------------------------------------------- 1 | List: 2 | 3 | * 4 | * 5 | * 6 | 7 | -------------------------------------------------------------------------------- /benchmark/template/list.mst: -------------------------------------------------------------------------------- 1 | List: 2 | $ loop(data) { $ 3 | * $=h:title$ 4 | * $=h:title$ 5 | * $=h:title$ 6 | $ } $ 7 | -------------------------------------------------------------------------------- /benchmark/template/list.mt: -------------------------------------------------------------------------------- 1 | List: 2 | ? for my $item(@{ $_[0]->{data} }) { 3 | * {title} ?> 4 | * {title} ?> 5 | * {title} ?> 6 | ? } 7 | -------------------------------------------------------------------------------- /benchmark/template/list.tj: -------------------------------------------------------------------------------- 1 | List: 2 | {data} }) { ?> 3 | * [= $item->{title} =] 4 | * [= $item->{title} =] 5 | * [= $item->{title} =] 6 | 7 | -------------------------------------------------------------------------------- /benchmark/template/list.tt: -------------------------------------------------------------------------------- 1 | List: 2 | [% FOREACH item IN data -%] 3 | * [% item.title | html %] 4 | * [% item.title | html %] 5 | * [% item.title | html %] 6 | [% END -%] 7 | -------------------------------------------------------------------------------- /benchmark/template/list.tx: -------------------------------------------------------------------------------- 1 | List: 2 | : for $data ->($item) { 3 | * <:= $item.title :> 4 | * <:= $item.title :> 5 | * <:= $item.title :> 6 | : } 7 | -------------------------------------------------------------------------------- /cpanfile: -------------------------------------------------------------------------------- 1 | requires 'perl', '5.008001'; 2 | requires 'Mouse', 'v2.5.0'; 3 | requires 'Data::MessagePack', '0.38'; 4 | requires 'parent', '0.221'; 5 | requires 'Scalar::Util', '1.14'; 6 | 7 | # fix local $SIG{__DIE__} + eval problems 8 | requires 'Encode', '2.26'; 9 | requires 'Storable', '2.15'; 10 | 11 | on configure => sub { 12 | requires 'Devel::PPPort', '3.33'; 13 | requires 'Module::Build::XSUtil'; 14 | requires 'version', '0.9913'; 15 | }; 16 | 17 | on test => sub { 18 | requires 'Test::More', '0.98'; 19 | requires 'Test::Requires'; 20 | requires 'File::Copy::Recursive'; 21 | requires 'File::Path', '2.07'; 22 | }; 23 | 24 | on develop => sub { 25 | requires 'Test::LeakTrace'; 26 | requires 'Devel::StackTrace'; 27 | requires 'Data::Section::Simple'; 28 | }; 29 | -------------------------------------------------------------------------------- /example/amon2.psgi: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use warnings; 4 | 5 | use Amon2::Lite; 6 | 7 | get '/' => sub { 8 | my ($c) = @_; 9 | $c->render('index.tt'); 10 | }; 11 | get '/hello' => sub { 12 | my ($c) = @_; 13 | $c->render('index.tt', { name => 'Amon2' }); 14 | }; 15 | 16 | sub res_404 { 17 | my($c) = @_; 18 | use Data::Dumper; 19 | die Dumper($c); 20 | } 21 | 22 | __PACKAGE__->to_app(); 23 | 24 | __DATA__ 25 | 26 | @@ index.tt 27 | 28 | 29 |

Hello, [% name // 'Xslate' %] world! 30 | 31 | -------------------------------------------------------------------------------- /example/amon2.psgi.gold: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Hello, Amon2 world! 4 | 5 | -------------------------------------------------------------------------------- /example/autolink.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Text::Xslate qw(html_builder html_escape); 4 | use URI::Find 20100505; 5 | 6 | my $text = <<'EOT'; 7 | 8 | EOT 9 | 10 | my $finder = URI::Find->new(sub { 11 | my($obj_uri, $orig_uri) = @_; 12 | my $safe_uri = html_escape($orig_uri); 13 | return qq|$safe_uri|; 14 | }); 15 | 16 | my $tx = Text::Xslate->new( 17 | function => { 18 | autolink => html_builder { 19 | my($text) = @_; 20 | $finder->find(\$text, \&html_escape); 21 | return $text; 22 | }, 23 | }, 24 | ); 25 | 26 | print $tx->render_string(<<'T', { text => $text }); 27 | : $text | autolink 28 | T 29 | -------------------------------------------------------------------------------- /example/autolink.pl.gold: -------------------------------------------------------------------------------- 1 | <http://example.com/?a=10&b=20> 2 | -------------------------------------------------------------------------------- /example/base.tx: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | <: block title -> { :>My amazing site<: } :> 7 | 8 | 9 | 10 | 18 | 19 |
20 | : block content -> { ; } 21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /example/bridge.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Text::Xslate; 4 | use FindBin qw($Bin); 5 | 6 | { 7 | package MyBridge; 8 | 9 | use parent qw(Text::Xslate::Bridge); 10 | 11 | use Scalar::Util qw(looks_like_number); 12 | use List::Util qw(sum); 13 | 14 | __PACKAGE__->bridge( 15 | scalar => { 16 | looks_like_number => \&_scalar_looks_like_number, 17 | length => \&_scalar_length, 18 | }, 19 | array => { 20 | sum => \&_array_sum, 21 | }, 22 | hash => { 23 | delete_keys => \&_hash_delete_keys, 24 | }, 25 | 26 | ); 27 | 28 | sub _scalar_looks_like_number { 29 | looks_like_number($_[0]); 30 | } 31 | 32 | sub _scalar_length { 33 | defined $_[0] ? length $_[0] : 0; 34 | } 35 | 36 | sub _array_sum { 37 | defined $_[0] ? sum @{$_[0]} : 0; 38 | } 39 | 40 | sub _hash_delete_keys { 41 | my %hash = %{+shift}; 42 | delete $hash{$_} for @_; 43 | return \%hash; 44 | } 45 | } 46 | 47 | my $tx = Text::Xslate->new( 48 | module => [qw(MyBridge)], 49 | path => $Bin, 50 | ); 51 | 52 | print $tx->render('bridge.tx'); 53 | -------------------------------------------------------------------------------- /example/bridge.tx: -------------------------------------------------------------------------------- 1 | : my $num = 100; 2 | : my $str = 'abc'; 3 | : my $array = [1,2,4,8,16]; 4 | : my $hash = {a => 1, b => 2, c => 3, d => 4}; 5 | 6 | : $num ~ ' is a number' if $num.looks_like_number(); 7 | 8 | : $str ~ ' is a string' if !$str.looks_like_number(); 9 | 10 | : $str ~ ' length is ' ~ $str.length(); 11 | 12 | : 'sum of ' ~ $array.join(',') ~ ' is ' ~ $array.sum(); 13 | 14 | : my $reduced = $hash.delete_keys('b', 'd'); 15 | 16 | After keys b and d have been deleted, the hash is: 17 | : for $reduced.kv() -> $pair { 18 | <: $pair.key :> = <: $pair.value :> 19 | : }; 20 | -------------------------------------------------------------------------------- /example/cascade.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Text::Xslate; 4 | use FindBin qw($Bin); 5 | 6 | { 7 | package BlogEntry; 8 | use Mouse; 9 | has title => (is => 'rw'); 10 | has body => (is => 'rw'); 11 | } 12 | 13 | my @blog_entries = map{ BlogEntry->new($_) } ( 14 | { 15 | title => 'Entry one', 16 | body => 'This is my first entry.', 17 | }, 18 | { 19 | title => 'Entry two', 20 | body => 'This is my second entry.', 21 | }, 22 | ); 23 | 24 | my $path = $Bin; 25 | my $tx = Text::Xslate->new( 26 | path => [$path], 27 | cache_dir => '.eg_cache', 28 | ); 29 | 30 | print $tx->render('cascade.tx', { blog_entries => \@blog_entries }); 31 | -------------------------------------------------------------------------------- /example/cascade.pl.gold: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | My amazing blog 7 | 8 | 9 | 10 | 16 | 17 |
18 |

Entry one

19 |

This is my first entry.

20 |

Entry two

21 |

This is my second entry.

22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /example/cascade.tx: -------------------------------------------------------------------------------- 1 | : cascade base 2 | 3 | <: around title ->{ :>My amazing blog<: } :> 4 | 5 | : around content -> { 6 | : for $blog_entries -> ($entry) { 7 |

<:= $entry.title :>

8 |

<:= $entry.body :>

9 | : } # end for 10 | : } # end content 11 | -------------------------------------------------------------------------------- /example/chained-scope.pl: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use warnings; 4 | use Text::Xslate; 5 | 6 | my %vpath = ( 7 | 'foo.tx' => 'Hello, <: $a ~ $b :> world!' . "\n", 8 | 'bar.tx' => ': include foo { __ROOT__.merge({ b => " with Kolon"}) }', 9 | ); 10 | 11 | my $tx = Text::Xslate->new( 12 | cache => 0, 13 | path => \%vpath, 14 | ); 15 | 16 | print $tx->render('bar.tx', { a => 'Xslate' }); 17 | 18 | -------------------------------------------------------------------------------- /example/chained-scope.pl.gold: -------------------------------------------------------------------------------- 1 | Hello, Xslate with Kolon world! 2 | -------------------------------------------------------------------------------- /example/data_section.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | 4 | use Text::Xslate; 5 | use Data::Section::Simple; 6 | 7 | my $tx = Text::Xslate->new( 8 | path => [ Data::Section::Simple->new()->get_data_section() ], 9 | cache_dir => '.eg_cache', 10 | ); 11 | 12 | print $tx->render('child.tx'); 13 | 14 | __DATA__ 15 | 16 | @@ base.tx 17 | 18 | 19 | <: block body -> { :>default body<: } :> 20 | 21 | 22 | @@ child.tx 23 | : cascade base; 24 | : override body -> { 25 | child body 26 | : } # endblock body 27 | -------------------------------------------------------------------------------- /example/data_section.pl.gold: -------------------------------------------------------------------------------- 1 | 2 | 3 | child body 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /example/dynamic_functions.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Text::Xslate; 4 | use FindBin qw($Bin); 5 | 6 | { 7 | package MyBridge; 8 | 9 | use parent qw(Text::Xslate::Bridge); 10 | 11 | __PACKAGE__->bridge( 12 | function => {my_func => \&_my_func}, 13 | scalar => {my_func => \&_scalar}, 14 | hash => {my_func => \&_hash}, 15 | array => {my_func => \&_array}, 16 | ); 17 | 18 | sub _scalar { 19 | my $obj = shift; 20 | _my_func(@_)->($obj); 21 | }; 22 | 23 | sub _hash { 24 | my $obj = shift; 25 | _my_func(@_)->(map {$_, $obj->{$_}} keys %$obj); 26 | }; 27 | 28 | sub _array { 29 | my $obj = shift; 30 | _my_func(@_)->(@$obj); 31 | }; 32 | 33 | sub _my_func { 34 | my @outer_args = @_; 35 | sub { 36 | my (@inner_args) = @_; 37 | join(', ', @outer_args, @inner_args) . "\n"; 38 | } 39 | } 40 | } 41 | 42 | my $tx = Text::Xslate->new( 43 | module => [qw(MyBridge)], 44 | path => $Bin, 45 | ); 46 | 47 | print $tx->render('dynamic_functions.tx'); 48 | -------------------------------------------------------------------------------- /example/dynamic_functions.tx: -------------------------------------------------------------------------------- 1 | Block syntax: 2 | : block my_block | my_func(foo => 'bar') -> {'baz, qux'} 3 | 4 | Subroutine syntax: 5 | : my_func(foo => 'bar')('baz, qux'); 6 | 7 | Filter syntax: 8 | : 'baz, qux' | my_func(foo => 'bar') 9 | 10 | Scalar object syntax: 11 | : 'baz, qux'.my_func(foo => 'bar') 12 | 13 | Hash object syntax: 14 | : {baz => 'qux'}.my_func(foo => 'bar') 15 | 16 | Array object syntax: 17 | : ['baz', 'qux'].my_func(foo => 'bar') 18 | -------------------------------------------------------------------------------- /example/err_handler.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Text::Xslate; 4 | my %vpath = ( 5 | hello => 'Hello, <: $lang :> world!' . "\n", 6 | ); 7 | my $tx = Text::Xslate->new( 8 | cache => 0, 9 | path => \%vpath, 10 | verbose => 2, 11 | warn_handler => sub { Text::Xslate->print('[[', @_, ']]') }, 12 | ); 13 | 14 | print $tx->render('hello', { lang => "Xslate" }); 15 | print $tx->render('hello', { }); 16 | 17 | -------------------------------------------------------------------------------- /example/fillinform.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Text::Xslate; 4 | use HTML::FillInForm::Lite 1.09; 5 | 6 | my $tx = Text::Xslate->new( 7 | html_builder_module => [ 'HTML::FillInForm::Lite' => [qw(fillinform)] ], 8 | cache_dir => '.eg_cache', 9 | ); 10 | 11 | my %vars = ( 12 | q => { foo => "" }, 13 | ); 14 | print $tx->render_string(<<'T', \%vars); 15 | FillInForm: 16 | : block form | fillinform($q) -> { 17 |
18 | 19 |
20 | : } 21 | T 22 | -------------------------------------------------------------------------------- /example/fillinform.pl.gold: -------------------------------------------------------------------------------- 1 | FillInForm: 2 |
3 | 4 |
5 | -------------------------------------------------------------------------------- /example/form.psgi.gold: -------------------------------------------------------------------------------- 1 | 2 | 3 | Using Form Builder 4 | 5 |
6 |

7 | Form:
8 | 9 | 10 |

11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /example/hello.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Text::Xslate; 4 | use FindBin qw($Bin); 5 | 6 | my $path = $Bin; 7 | my $tx = Text::Xslate->new( 8 | path => [$path], 9 | cache_dir => '.eg_cache', 10 | ); 11 | 12 | print $tx->render('hello.tx', { }); 13 | print $tx->render('hello.tx', { lang => "Xslate" }); 14 | 15 | -------------------------------------------------------------------------------- /example/hello.pl.gold: -------------------------------------------------------------------------------- 1 | Hello, Perl world! 2 | Hello, Xslate world! 3 | -------------------------------------------------------------------------------- /example/hello.tmpl: -------------------------------------------------------------------------------- 1 | Hello, world! 2 | 3 | -------------------------------------------------------------------------------- /example/hello.tx: -------------------------------------------------------------------------------- 1 | Hello, <: $lang // "Perl" :> world! 2 | -------------------------------------------------------------------------------- /example/htmltemplate.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | # TODO 3 | use strict; 4 | 5 | use Text::Xslate; 6 | 7 | my $tx = Text::Xslate->new( 8 | syntax => 'HTMLTemplate', 9 | cache => 0, 10 | ); 11 | 12 | print $tx->render('example/hello.tmpl', { lang => 'HTML::Template' }); 13 | 14 | -------------------------------------------------------------------------------- /example/htparser.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | # TODO 3 | use strict; 4 | 5 | use Text::Xslate; 6 | use File::Slurp qw(slurp); 7 | use HTML::Template::Parser; 8 | use HTML::Template::Parser::TreeWriter::TextXslate::Metakolon; 9 | 10 | my $htparser = HTML::Template::Parser->new(); 11 | my $ast = $htparser->parse(slurp 'example/hello.tmpl'); 12 | 13 | my $writer = HTML::Template::Parser::TreeWriter::TextXslate::Metakolon->new; 14 | my $source = $writer->write($ast); 15 | 16 | my $tx = Text::Xslate->new( 17 | syntax => 'Metakolon', 18 | cache => 0, 19 | ); 20 | 21 | print $tx->render_string($source, { lang => 'HTML::Template' }); 22 | 23 | -------------------------------------------------------------------------------- /example/i18n-data-localize.pl: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use warnings; 4 | use utf8; 5 | 6 | use Text::Xslate; 7 | use Text::Xslate::Util qw(html_escape html_builder); 8 | use Data::Localize; 9 | use Data::Localize::Gettext; 10 | use FindBin qw($Bin); 11 | use Encode (); 12 | 13 | my $i18n = Data::Localize->new( 14 | auto => 1, 15 | languages => ['ja'], 16 | ); 17 | $i18n->add_localizer( 18 | class => 'Gettext', 19 | path => "$Bin/locale/*.po", 20 | ); 21 | 22 | my $xslate = Text::Xslate->new( 23 | syntax => 'TTerse', 24 | function => { 25 | l => sub { 26 | return $i18n->localize(@_); 27 | }, 28 | l_raw => html_builder { 29 | my $format = shift; 30 | my @args = map { html_escape($_) } @_; 31 | return $i18n->localize($format, @args); 32 | }, 33 | }, 34 | ); 35 | 36 | my %param = (location => ''); # user inputs 37 | my $body = $xslate->render_string(<<'TEMPLATE', \%param); 38 | [% l_raw('Hello!
') %] 39 | [% l_raw('I am in %1
', $location) %] 40 | TEMPLATE 41 | 42 | print Encode::encode_utf8($body); 43 | 44 | -------------------------------------------------------------------------------- /example/i18n-data-localize.pl.gold: -------------------------------------------------------------------------------- 1 | こんにちは!
2 | 私は <Tokyo> にいます
3 | -------------------------------------------------------------------------------- /example/i18n.pl: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use warnings; 4 | use utf8; 5 | 6 | use Text::Xslate; 7 | use Text::Xslate::Util qw(html_escape html_builder); 8 | use Encode (); 9 | 10 | { 11 | package MyApp::I18N; 12 | use FindBin qw($Bin); 13 | use parent 'Locale::Maketext'; 14 | use Locale::Maketext::Lexicon { 15 | '*' => [Gettext => "$Bin/locale/*.po"], 16 | _auto => 1, 17 | _decode => 1, 18 | _style => 'gettext', 19 | }; 20 | } 21 | 22 | my $i18n = MyApp::I18N->get_handle('ja'); 23 | 24 | my $xslate = Text::Xslate->new( 25 | syntax => 'TTerse', 26 | function => { 27 | l => sub { 28 | return $i18n->maketext(@_); 29 | }, 30 | l_raw => html_builder { 31 | my $format = shift; 32 | my @args = map { html_escape($_) } @_; 33 | return $i18n->maketext($format, @args); 34 | }, 35 | }, 36 | ); 37 | 38 | my %param = (location => ''); # user inputs 39 | my $body = $xslate->render_string(<<'TEMPLATE', \%param); 40 | [% l_raw('Hello!
') %] 41 | [% l_raw('I am in %1
', $location) %] 42 | TEMPLATE 43 | 44 | print Encode::encode_utf8($body); 45 | 46 | -------------------------------------------------------------------------------- /example/i18n.pl.gold: -------------------------------------------------------------------------------- 1 | こんにちは!
2 | 私は <Tokyo> にいます
3 | -------------------------------------------------------------------------------- /example/list.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Text::Xslate; 4 | use FindBin qw($Bin); 5 | use File::Find; 6 | 7 | my $path = $Bin; 8 | my $tx = Text::Xslate->new( 9 | path => [$path], 10 | cache_dir => '.eg_cache', 11 | ); 12 | 13 | # preload templates 14 | find sub { 15 | if(/\.tx$/) { 16 | my $file = $File::Find::name; 17 | $file =~ s/\Q$path\E .//xsm; 18 | $tx->load_file($file); 19 | } 20 | }, $path; 21 | 22 | print $tx->render('list.tx', {data => [ 23 | { title => 'Islands in the stream' }, 24 | { title => 'Programming Perl' }, 25 | { title => 'River out of Eden' }, 26 | { title => 'Beautiful code' }, 27 | ]}); 28 | 29 | -------------------------------------------------------------------------------- /example/list.pl.gold: -------------------------------------------------------------------------------- 1 | List: 2 | ------------------------------- 3 | 1. Islands in the stream 4 | ------------------------------- 5 | 2. Programming Perl 6 | ------------------------------- 7 | 3. River out of Eden 8 | ------------------------------- 9 | 4. Beautiful code 10 | ------------------------------- 11 | -------------------------------------------------------------------------------- /example/list.tx: -------------------------------------------------------------------------------- 1 | List: 2 | : for $data ->($item) { 3 | : if $~item.is_first { 4 | ------------------------------- 5 | : } 6 | <: $~item.count :>. <: $item.title :> 7 | ------------------------------- 8 | : } 9 | -------------------------------------------------------------------------------- /example/locale/ja.po: -------------------------------------------------------------------------------- 1 | # for example/i18n.pl 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: Xslate I18N example\n" 5 | "POT-Creation-Date: YEAR-MO-DA HO:MI+ZONE\n" 6 | "PO-Revision-Date: 2011-11-02 12:00-0900\n" 7 | "Last-Translator: Fuji Goro\n" 8 | "Language-Team: Jaapanese\n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | 13 | msgid "Hello!
" 14 | msgstr "こんにちは!
" 15 | 16 | msgid "I am in %1
" 17 | msgstr "私は %1 にいます
" 18 | 19 | -------------------------------------------------------------------------------- /example/mojo.psgi: -------------------------------------------------------------------------------- 1 | #!perl 2 | package HelloMojo; 3 | use strict; 4 | use warnings; 5 | use Mojolicious 1.0; 6 | 7 | use Mojolicious::Lite; 8 | #use MojoX::Renderer::Xslate; # this is automatically loaded 9 | 10 | plugin 'xslate_renderer'; 11 | 12 | get '/' => 'index'; 13 | get '/:name' => 'index'; 14 | 15 | app->start; 16 | 17 | __DATA__ 18 | @@ index.html.tx 19 | 20 | 21 | Hello, <: $c.req.param('lang') // "Xslate" :> world! 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/mojo.psgi.gold: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hello, Xslate world! 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /example/put_js.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Text::Xslate; 4 | 5 | my $tx = Text::Xslate->new( 6 | module => ['JavaScript::Value::Escape' => [qw(js)]], 7 | ); 8 | 9 | my %params = ( 10 | user_input => '', 11 | ); 12 | 13 | print $tx->render_string(<<'T', \%params); 14 | 18 | T 19 | 20 | -------------------------------------------------------------------------------- /example/put_js.pl.gold: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /example/simple.psgi: -------------------------------------------------------------------------------- 1 | #!psgi -w 2 | use strict; 3 | use Text::Xslate; 4 | use Plack::Request; 5 | 6 | my %vpath = ( 7 | 'hello.tx' => <<'TX', 8 | 9 | 10 | 11 | hello 12 | 13 | 14 |

15 | 16 | 17 |

18 |

Hello, <: $lang :> world!

19 | 20 | 21 | TX 22 | ); 23 | 24 | my $tx = Text::Xslate->new( 25 | path => \%vpath, 26 | cache_dir => '.eg_cache', 27 | ); 28 | 29 | sub app { 30 | my($env) = @_; 31 | my $req = Plack::Request->new($env); 32 | my $res = $req->new_response( 33 | 200, 34 | [content_type => 'text/html; charset=utf-8'], 35 | ); 36 | my %vars = ( 37 | lang => $req->param('lang') || '', 38 | ); 39 | my $body = $tx->render('hello.tx', \%vars); 40 | utf8::encode($body); 41 | $res->body($body); 42 | return $res->finalize(); 43 | } 44 | 45 | return \&app; 46 | -------------------------------------------------------------------------------- /example/simple.psgi.gold: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | hello 5 | 6 | 7 |

8 | 9 | 10 |

11 |

Hello, <Xslate> world!

12 | 13 | 14 | -------------------------------------------------------------------------------- /lib/Text/Xslate/HashWithDefault.pm: -------------------------------------------------------------------------------- 1 | package Text::Xslate::HashWithDefault; 2 | use strict; 3 | 4 | use Tie::Hash; 5 | our @ISA = qw(Tie::ExtraHash); 6 | 7 | sub TIEHASH { 8 | my($class, $storage, $default) = @_; 9 | return bless [ $storage, $default ], $class; 10 | } 11 | 12 | sub FETCH { 13 | my($self, $key) = @_; 14 | if(exists $self->[0]{$key}) { 15 | return $self->[0]{$key}; 16 | } 17 | else { 18 | return ref($self->[1]) eq 'CODE' 19 | ? $self->[1]->($key) 20 | : $self->[1]; 21 | } 22 | } 23 | 24 | 1; 25 | __END__ 26 | 27 | =head1 NAME 28 | 29 | Text::Xslate::HashWithDefault - Helper class to fill in default values 30 | 31 | =head1 SYNOPSIS 32 | 33 | use Text::Xslate::Util qw(hash_with_default); 34 | 35 | my $hash_ref = hash_with_default({ }, sub { "FILLME('@_')" }); 36 | print $hash_ref->{foo}; # FILLME('foo') 37 | 38 | =head1 DESCRIPTION 39 | 40 | This is a helper class to provide C functionality, 41 | which is useful for debugging. 42 | 43 | See L for details. 44 | 45 | =cut 46 | 47 | -------------------------------------------------------------------------------- /lib/Text/Xslate/Manual.pod: -------------------------------------------------------------------------------- 1 | 2 | =head1 NAME 3 | 4 | Text::Xslate::Manual - Xslate users manual 5 | 6 | =head1 TABLE OF CONTENTS 7 | 8 | L - Perl Interface to the Xslate template engine 9 | 10 | L - The default template syntax 11 | 12 | L - A Template Toolkit compatible syntax 13 | 14 | L - Builtin methods and filters/functions 15 | 16 | L - The Xslate cookbook 17 | 18 | L - Frequently asked questions and answers 19 | 20 | L - How to debug Xslate templates 21 | 22 | =head1 NOTES 23 | 24 | Xslate manual pages are not yet completed. Patches are welcome. 25 | 26 | =head1 SEE ALSO 27 | 28 | L - Xslate repositories 29 | 30 | =cut 31 | 32 | -------------------------------------------------------------------------------- /lib/Text/Xslate/PP/Type/Macro.pm: -------------------------------------------------------------------------------- 1 | package Text::Xslate::PP::Type::Macro; 2 | use Mouse; 3 | use warnings FATAL => 'recursion'; 4 | 5 | use overload 6 | '&{}' => \&as_code_ref, 7 | fallback => 1, 8 | ; 9 | 10 | has name => ( 11 | is => 'ro', 12 | isa => 'Str', 13 | 14 | required => 1, 15 | ); 16 | 17 | has addr => ( 18 | is => 'ro', 19 | isa => 'Int', 20 | 21 | required => 1, 22 | ); 23 | 24 | has nargs => ( 25 | is => 'rw', 26 | isa => 'Int', 27 | 28 | default => 0, 29 | ); 30 | 31 | has outer => ( 32 | is => 'rw', 33 | isa => 'Int', 34 | 35 | default => 0, 36 | ); 37 | 38 | has state => ( 39 | is => 'rw', 40 | isa => 'Object', 41 | 42 | required => 1, 43 | weak_ref => 1, 44 | ); 45 | 46 | sub as_code_ref { 47 | my($self) = @_; 48 | 49 | return sub { 50 | my $st = $self->state; 51 | push @{$st->{SP}}, [@_]; 52 | $st->proccall($self); 53 | }; 54 | } 55 | 56 | no Mouse; 57 | __PACKAGE__->meta->make_immutable; 58 | __END__ 59 | 60 | =head1 NAME 61 | 62 | Text::Xslate::PP::Type::Macro - Text::Xslate macro object in pure Perl 63 | 64 | =head1 DESCRIPTION 65 | 66 | This module is used by Text::Xslate::PP internally. 67 | 68 | =head1 SEE ALSO 69 | 70 | L 71 | 72 | L 73 | 74 | =cut 75 | -------------------------------------------------------------------------------- /lib/Text/Xslate/PP/Type/Pair.pm: -------------------------------------------------------------------------------- 1 | package Text::Xslate::PP::Type::Pair; 2 | use Mouse; 3 | 4 | has [qw(key value)] => ( 5 | is => 'rw', 6 | required => 1, 7 | ); 8 | 9 | no Mouse; 10 | __PACKAGE__->meta->make_immutable(); 11 | __END__ 12 | 13 | =head1 NAME 14 | 15 | Text::Xslate::PP::Type::Pair - Text::Xslate builtin pair type in pure Perl 16 | 17 | =head1 DESCRIPTION 18 | 19 | This module is used by Text::Xslate::PP internally. 20 | 21 | =head1 SEE ALSO 22 | 23 | L 24 | 25 | L 26 | 27 | =cut 28 | -------------------------------------------------------------------------------- /lib/Text/Xslate/Syntax/Metakolon.pm: -------------------------------------------------------------------------------- 1 | package Text::Xslate::Syntax::Metakolon; 2 | use Mouse; 3 | 4 | extends qw(Text::Xslate::Parser); 5 | 6 | # [% ... %] and %% ... 7 | sub _build_line_start { '%%' } 8 | sub _build_tag_start { '[%' } 9 | sub _build_tag_end { '%]' } 10 | 11 | no Mouse; 12 | __PACKAGE__->meta->make_immutable(); 13 | 14 | __END__ 15 | 16 | =head1 NAME 17 | 18 | Text::Xslate::Syntax::Metakolon - The same as Kolon but using [% ... %] tags 19 | 20 | =head1 SYNOPSIS 21 | 22 | use Text::Xslate; 23 | my $tx = Text::Xslate->new( 24 | syntax => 'Metakolon', 25 | ); 26 | 27 | print $tx->render_string( 28 | 'Hello, [% $dialect %] world!', 29 | { dialect => 'Metakolon' } 30 | ); 31 | 32 | =head1 DESCRIPTION 33 | 34 | Metakolon is the same as Kolon except for using C<< [% ... %] >> tags and 35 | C<< %% ... >> line code, instead of C<< <: ... :> >> and C<< : ... >>. 36 | 37 | This may be useful when you want to produce Xslate templates by itself. 38 | 39 | See L for details. 40 | 41 | =head1 SEE ALSO 42 | 43 | L 44 | 45 | =cut 46 | -------------------------------------------------------------------------------- /lib/Text/Xslate/Type/Raw.pm: -------------------------------------------------------------------------------- 1 | package Text::Xslate::Type::Raw; 2 | # the package is implemented intentionally in Text::Xslate 3 | require Text::Xslate; 4 | 1 5 | __END__ 6 | 7 | =head1 NAME 8 | 9 | Text::Xslate::Type::Raw - The raw string representation 10 | 11 | =head1 DESCRIPTION 12 | 13 | This class represents raw strings so that Xslate does not escape them. 14 | 15 | Note that you cannot extend this class. 16 | 17 | =head1 METHODS 18 | 19 | =head2 new 20 | 21 | create a new instance 22 | 23 | =head2 as_string 24 | 25 | this method is overload to string. 26 | 27 | print Text::Xslate::Type::Raw->new('raw_string')->as_string 28 | print Text::Xslate::Type::Raw->new('raw_string') # the same, because of overload 29 | 30 | 31 | =cut 32 | -------------------------------------------------------------------------------- /minil.toml: -------------------------------------------------------------------------------- 1 | name = "Text-Xslate" 2 | module_maker = "ModuleBuild" 3 | allow_pureperl = 1 4 | badges = ["github-actions/test"] 5 | 6 | [build] 7 | build_class = "builder::MyBuilder" 8 | -------------------------------------------------------------------------------- /t/000_load.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More tests => 7; 5 | 6 | BEGIN { use_ok 'Text::Xslate' } 7 | BEGIN { use_ok 'Text::Xslate::Compiler' } 8 | BEGIN { use_ok 'Text::Xslate::Parser' } 9 | BEGIN { use_ok 'Text::Xslate::Syntax::Kolon' } 10 | BEGIN { use_ok 'Text::Xslate::Syntax::Metakolon' } 11 | BEGIN { use_ok 'Text::Xslate::Syntax::TTerse' } 12 | BEGIN { use_ok 'Text::Xslate::Type::Raw' } 13 | 14 | diag "Testing Text::Xslate/$Text::Xslate::VERSION"; 15 | if(Text::Xslate->isa('Text::Xslate::PP')) { 16 | diag "Backend: PP"; 17 | } 18 | else { 19 | diag "Backend: XS"; 20 | } 21 | diag '$ENV{XSLATE}=', $ENV{XSLATE} || ''; 22 | 23 | -------------------------------------------------------------------------------- /t/010_internals/001_parse.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate::Parser; 7 | use Text::Xslate::Util qw(p); 8 | 9 | my $debug = 0; 10 | 11 | my $parser = Text::Xslate::Parser->new(); 12 | isa_ok $parser, 'Text::Xslate::Parser'; 13 | 14 | my @data = ( 15 | [''], 16 | ['Hello, world!', qr/"Hello, world!"/], 17 | ['Hello, <:= $lang :> world!', qr/ \$lang \b/xms, qr/"Hello, "/, qr/" world!"/], 18 | ['aaa <:= $bbb :> ccc <:= $ddd :>', qr/aaa/, qr/\$bbb/, qr/ccc/, qr/\$ddd/], 19 | 20 | ['<: for $data ->($item) { print $item; } :>', qr/\b for \b/xms, qr/\$data\b/, qr/\$item/ ], 21 | 22 | ["

:

", qr{

:

}], 23 | ["

:

", qr{

:

}], 24 | 25 | [<<'T', qr/foo/, qr/item/, qr/data/], 26 | : macro foo ->($x, $y) { 27 | : for $x -> ($item) { 28 | <: $item :> 29 | : } 30 | : } 31 | : for $data -> ($item) { 32 | : foo($item) 33 | : } 34 | T 35 | 36 | [<<'T'], 37 | : if $foo { 38 | bar 39 | : } 40 | : else { 41 | baz 42 | : } 43 | T 44 | 45 | [';' x 10], 46 | 47 | [<<'T'], 48 | : my $foo = { 49 | : bar => 42, 50 | : }; 51 | T 52 | ); 53 | 54 | foreach my $d(@data) { 55 | my($str, @patterns) = @{$d}; 56 | 57 | note($str) if $debug; 58 | my $code = eval { p($parser->parse($str)) }; 59 | note($code) if $debug; 60 | 61 | ok $code, 'parse' 62 | or diag $@; 63 | 64 | foreach my $pat(@patterns) { 65 | like $code, $pat; 66 | } 67 | } 68 | 69 | done_testing; 70 | -------------------------------------------------------------------------------- /t/010_internals/002_compile.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate::Compiler; 7 | use Text::Xslate::Util qw(p); 8 | 9 | my $c = Text::Xslate::Compiler->new(); 10 | 11 | isa_ok $c, 'Text::Xslate::Compiler'; 12 | 13 | my @data = ( 14 | ['Hello, world!', qr/Hello, world!/], 15 | ['Hello, <:= $lang :> world!', qr/\b lang \b/xms, qr/Hello, /, qr/ world!/], 16 | ['aaa <:= $bbb :> ccc <:= $ddd :>', qr/aaa/, qr/\b bbb \b/xms, qr/ccc/, qr/\b ddd \b/xms], 17 | 18 | ['<: for $data ->($item) { print $item; } :>', qr/\b for /xms, qr/\b data \b/xms ], 19 | 20 | ['<: "foo" :>:<: "bar" :>', qr/ foo /xms, qr/ bar /xms, qr/ : /xms ], 21 | ); 22 | 23 | foreach my $d(@data) { 24 | my($str, @patterns) = @{$d}; 25 | 26 | my $code = p($c->compile($str)); 27 | #note($code); 28 | 29 | foreach my $pat(@patterns) { 30 | like $code, $pat, $str; 31 | } 32 | } 33 | 34 | done_testing; 35 | -------------------------------------------------------------------------------- /t/010_internals/003_assemble.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | # The purpose of this test is to ensure that _assemble() ends successfully. 3 | 4 | use strict; 5 | use Test::More tests => 4; 6 | 7 | use Text::Xslate; 8 | 9 | my %vpath = ( 10 | hello => 'Hello, world!', 11 | ); 12 | 13 | 14 | for my $n(1 .. 2) { 15 | my $tx = Text::Xslate->new(cache => 0, path => \%vpath); 16 | eval { 17 | $tx->load_file('hello'); 18 | }; 19 | is $@, '', "assemble inside of eval {} ($n)"; 20 | 21 | $tx = Text::Xslate->new(cache => 0, path => \%vpath); 22 | $tx->load_file('hello'); 23 | pass "assemble outside of eval {} ($n)"; 24 | } 25 | 26 | -------------------------------------------------------------------------------- /t/010_internals/006_errhandler.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | 8 | my $warn = ''; 9 | my $die = ''; 10 | my $tx = Text::Xslate->new( 11 | verbose => 2, 12 | warn_handler => sub{ $warn = "@_" }, 13 | die_handler => sub{ $die = "@_" }, 14 | ); 15 | 16 | is $tx->render_string( 17 | 'Hello, <:= $lang :> world!', 18 | { lang => undef }), "Hello, world!", "error handler" for 1 .. 2; 19 | 20 | like $warn, qr/\b nil \b/xms, 'warnings are produced'; 21 | like $warn, qr/Xslate/; 22 | like $warn, qr//; 23 | is $die, ''; 24 | 25 | $warn = ''; 26 | $die = ''; 27 | 28 | eval { 29 | is $tx->render_string( 30 | ': include "no/such/path" ', 31 | { lang => undef }), "Hello, world!", "error handler" for 1 .. 2; 32 | }; 33 | 34 | is $warn, ''; 35 | like $die, qr{no/such/path}, 'the error handler was called'; 36 | like $die, qr/Xslate/; 37 | like $@, qr{no/such/path}, 'errors are thrown, anyway'; 38 | like $@, qr/Xslate/; 39 | 40 | done_testing; 41 | -------------------------------------------------------------------------------- /t/010_internals/009_goto_address.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | # NOTE: the optimizer could break goto addresses 3 | 4 | use strict; 5 | use Test::More; 6 | 7 | use Text::Xslate; 8 | use Text::Xslate::Util qw(p); 9 | 10 | my $tx = Text::Xslate->new(); 11 | 12 | my @data = ( 13 | [<<'T', " [foo]\n" . " [foo]\n"], 14 | : if($value == "foo") { 15 | [<:= $value :>] 16 | [<:= $value :>] 17 | : } 18 | : else { 19 | [<:= $value :>] 20 | [<:= $value :>] 21 | : } 22 | T 23 | 24 | [<<'T', " [foo]\n" . " [foo]\n"], 25 | : if($value == "bar") { 26 | [<:= $value :>] 27 | [<:= $value :>] 28 | : } 29 | : else { 30 | [<:= $value :>] 31 | [<:= $value :>] 32 | : } 33 | T 34 | 35 | [<<'T', " [*foo]\n" . " [*foo]\n"], 36 | : if($value == "foo") { 37 | [<:= "*" ~ $value :>] 38 | [<:= "*" ~ $value :>] 39 | : } 40 | : else { 41 | [<:= "*" ~ $value :>] 42 | [<:= "*" ~ $value :>] 43 | : } 44 | T 45 | 46 | [<<'T', " [*foo]\n" . " [*foo]\n"], 47 | : if($value == "bar") { 48 | [<:= "*" ~ $value :>] 49 | [<:= "*" ~ $value :>] 50 | : } 51 | : else { 52 | [<:= "*" ~ $value :>] 53 | [<:= "*" ~ $value :>] 54 | : } 55 | T 56 | [<<'T', "\ntrue\n\n"], 57 | <: if 1 { :> 58 | true 59 | <: } else { :> 60 | false 61 | <: } :> 62 | T 63 | [<<'T', "\nfalse\n\n"], 64 | <: if 0 { :> 65 | true 66 | <: } else { :> 67 | false 68 | <: } :> 69 | T 70 | 71 | ); 72 | 73 | 74 | foreach my $pair(@data) { 75 | my($in, $out) = @$pair; 76 | 77 | my %vars = ( 78 | value => 'foo', 79 | ); 80 | is $tx->render_string($in, \%vars), $out 81 | or diag( p($tx->render(\%vars)) ); 82 | } 83 | 84 | done_testing; 85 | -------------------------------------------------------------------------------- /t/010_internals/013_deps_str.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | use FindBin qw($Bin); 8 | use File::Copy qw(copy move); 9 | use File::Path qw(rmtree); 10 | 11 | use lib "t/lib"; 12 | use Util; 13 | 14 | my $base = path . "/myapp/base.tx"; 15 | END{ 16 | move "$base.save" => $base if -e "$base.save"; 17 | 18 | rmtree cache_dir; 19 | } 20 | 21 | note 'for strings'; 22 | 23 | utime $^T-120, $^T-120, $base; 24 | 25 | my $tx = Text::Xslate->new(path => [path], cache_dir => cache_dir); 26 | 27 | #use Data::Dumper; print Dumper $tx; 28 | 29 | $tx->load_string(":cascade myapp::base"); 30 | is $tx->render('', {lang => 'Xslate'}), <<'T'; 31 | HEAD 32 | Hello, Xslate world! 33 | FOOT 34 | T 35 | 36 | move $base => "$base.save"; 37 | copy "$base.mod" => $base; 38 | 39 | utime $^T+60, $^T+60, $base; 40 | 41 | $tx->load_string(":cascade myapp::base"); 42 | is $tx->render('', {lang => 'Foo'}), <<'T'; 43 | HEAD 44 | Modified version of base.tx 45 | FOOT 46 | T 47 | 48 | move "$base.save" => $base; 49 | utime $^T+120, $^T+120, $base; 50 | 51 | $tx->load_string(":cascade myapp::base"); 52 | is $tx->render('', {lang => 'Perl'}), <<'T'; 53 | HEAD 54 | Hello, Perl world! 55 | FOOT 56 | T 57 | 58 | done_testing; 59 | -------------------------------------------------------------------------------- /t/010_internals/014_deps_file.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | use File::Copy qw(copy move); 8 | use File::Path qw(rmtree); 9 | 10 | use lib "t/lib"; 11 | use Util; 12 | 13 | my $base = path . "/myapp/base.tx"; 14 | my $derived = path . "/myapp/derived.tx"; 15 | rmtree cache_dir; 16 | END{ 17 | move "$base.save" => $base if -e "$base.save"; 18 | 19 | rmtree cache_dir; 20 | } 21 | 22 | note 'for files'; 23 | 24 | utime $^T - 120, $^T - 120, $base, $derived; 25 | 26 | my $tx = Text::Xslate->new(path => [path], cache_dir => cache_dir); 27 | 28 | #use Data::Dumper; print Dumper $tx; 29 | 30 | is $tx->render('myapp/derived.tx', {lang => 'Xslate'}), <<'T', 'original' for 1 .. 2; 31 | HEAD 32 | D-BEFORE 33 | Hello, Xslate world! 34 | D-AFTER 35 | FOOT 36 | T 37 | 38 | move $base => "$base.save"; 39 | copy "$base.mod" => $base; 40 | 41 | utime $^T+60, $^T+60, $base; 42 | note "modify $base"; 43 | 44 | is $tx->render('myapp/derived.tx', {lang => 'Foo'}), <<'T', 'modified' for 1 .. 2; 45 | HEAD 46 | D-BEFORE 47 | Modified version of base.tx 48 | D-AFTER 49 | FOOT 50 | T 51 | 52 | move "$base.save" => $base; 53 | utime $^T+120, $^T+120, $base; 54 | note "modify $base again"; 55 | 56 | is $tx->render('myapp/derived.tx', {lang => 'Perl'}), <<'T', 'again' for 1 .. 2; 57 | HEAD 58 | D-BEFORE 59 | Hello, Perl world! 60 | D-AFTER 61 | FOOT 62 | T 63 | 64 | done_testing; 65 | -------------------------------------------------------------------------------- /t/010_internals/015_deps_file2.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | use File::Copy qw(copy move); 8 | use File::Path qw(rmtree); 9 | use File::Spec; 10 | use lib "t/lib"; 11 | use Util; 12 | 13 | rmtree cache_dir; 14 | 15 | my $base; 16 | my $derived; 17 | my $base_c; 18 | my $derived_c; 19 | BEGIN { 20 | my $tx = Text::Xslate->new(path => [path], cache_dir => cache_dir); 21 | 22 | my $fi = $tx->find_file('myapp/base.tx'); 23 | $base = $fi->{fullpath}; 24 | $base_c = $fi->{cachepath}; 25 | 26 | $fi = $tx->find_file('myapp/derived.tx'); 27 | $derived = $fi->{fullpath}; 28 | $derived_c = $fi->{cachepath}; 29 | } 30 | 31 | END{ 32 | move "$base.save" => $base if -e "$base.save"; 33 | 34 | rmtree cache_dir; 35 | } 36 | 37 | 38 | note 'for files'; 39 | 40 | utime $^T - 120, $^T - 120, $base, $derived; 41 | 42 | { 43 | # compile and cache template files. 44 | my $tx = Text::Xslate->new(path => [path], cache_dir => cache_dir); 45 | $tx->render($_, {lang => 'Perl'}) for 'myapp/derived.tx'; 46 | } 47 | 48 | utime $^T - 60, $^T - 60, $base_c, $derived_c; 49 | note " cache files have been created at 60 seconds ago."; 50 | 51 | my $tx = Text::Xslate->new(path => [path], cache_dir => cache_dir); 52 | 53 | move $base => "$base.save"; 54 | copy "$base.mod" => $base; 55 | 56 | utime $^T, $^T, $base; 57 | note "modify $base just now"; 58 | 59 | is $tx->render('myapp/derived.tx', {lang => 'Foo'}), <<'T', "modified($_)" for 1 .. 2; 60 | HEAD 61 | D-BEFORE 62 | Modified version of base.tx 63 | D-AFTER 64 | FOOT 65 | T 66 | 67 | done_testing; 68 | -------------------------------------------------------------------------------- /t/010_internals/016_cached.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | use File::Path; 8 | use File::Temp 'tempfile'; 9 | use Config; 10 | 11 | use lib "t/lib"; 12 | use Util (); 13 | 14 | my $path = Util::path(); 15 | my $cache_dir = Util::cache_dir; 16 | rmtree($cache_dir); 17 | END{ rmtree($cache_dir) } 18 | 19 | my ($tempfh, $tempname) = tempfile(UNLINK => 1); 20 | print {$tempfh} <<'EOT'; 21 | use strict; 22 | use warnings; 23 | use Text::Xslate; 24 | use lib "t/lib"; 25 | use Util; 26 | my($path, $cache_dir) = @ARGV; 27 | my $tx = Text::Xslate->new( 28 | path => [$path, { 'foo.tx' => 'Hello' } ], 29 | cache_dir => $cache_dir, 30 | ); 31 | $tx->load_file('myapp/derived.tx'); 32 | $tx->load_file('foo.tx'); 33 | EOT 34 | close $tempfh; 35 | 36 | # XXX: @INC is too long to pass a command, so we need to give it via %ENV 37 | $ENV{PERL5LIB} = join $Config{path_sep}, @INC; 38 | is system($^X, $tempname, $path, $cache_dir), 0, 'compile' or die "failed to compile"; 39 | 40 | ok -d $cache_dir; 41 | 42 | for my $cache(1 .. 2) { 43 | my $tx = Text::Xslate->new( 44 | path => [$path, { 'foo.tx' => 'Hello' } ], 45 | cache_dir => $cache_dir, 46 | cache => $cache, 47 | ); 48 | 49 | for(1 .. 2) { 50 | like $tx->render('myapp/derived.tx', { lang => 'Xslate' }), 51 | qr/Hello, Xslate world!/, "cache => $cache (render/path)"; 52 | 53 | is $tx->render('foo.tx'), 'Hello', 'render/vpath'; 54 | 55 | ok !exists $INC{'Text/Xslate/Compiler.pm'}, 'Text::Xslate::Compiler is not loaded'; 56 | } 57 | #note(explain($tx->{_cache_path})); 58 | } 59 | 60 | done_testing; 61 | -------------------------------------------------------------------------------- /t/010_internals/018_opinfo.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | use lib "t/lib"; 8 | use Util; 9 | use File::Path qw(rmtree); 10 | 11 | rmtree(cache_dir); 12 | END{ rmtree(cache_dir) } 13 | 14 | my $warn; 15 | 16 | my $tx = Text::Xslate->new( 17 | verbose => 2, 18 | warn_handler => sub{ $warn .= join '', @_ }, 19 | path => path, 20 | cache_dir => cache_dir, 21 | cache => 1, 22 | ); 23 | 24 | $warn = ''; 25 | eval { 26 | $tx->render_string(<<'T'); 27 | : cascade oi::bad_base 28 | T 29 | }; 30 | is $@, '', 'exception'; 31 | like $warn, qr{ \b bad_base\.tx \b }xms; 32 | like $warn, qr{ \b bad_code_foo \b }xms; 33 | 34 | $warn = ''; 35 | eval { 36 | $tx->render_string(<<'T'); 37 | : cascade oi::bad_base with oi::bad_component 38 | T 39 | }; 40 | is $@, '', 'exception'; 41 | like $warn, qr{ \b bad_code_foo \b .+ \b bad_base\.tx \b }xms, 'warn (for base)'; 42 | like $warn, qr{ \b bad_code_bar \b .+ \b bad_component\.tx \b }xms, 'warn (for component)'; 43 | 44 | done_testing; 45 | -------------------------------------------------------------------------------- /t/010_internals/021_edge_cases.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | 7 | my $tx = Text::Xslate->new(); 8 | 9 | my @data = ( 10 | ['<: "foo\tbar\n" :>', "foo\tbar\n"], 11 | [q{<: 'foo\tbar\n' :>}, 'foo\tbar\n'], 12 | [q{<: ' & " \' ' :>}, ' & " ' '], 13 | [q{<: "<: 'foo' :>" :>}, "<: 'foo' :>"], 14 | 15 | [q{foo<:# this is a comment :>bar}, "foobar"], 16 | [q{<:$lang:> foo<:# this is a comment :>bar <:$lang:>}, "Xslate foobar Xslate"], 17 | [q{foo<: 18 | :>bar}, "foobar"], 19 | [q{foo<: # this is a comment 20 | $lang :>bar}, "fooXslatebar"], 21 | 22 | # edge-cases 23 | [q{: $lang}, 'Xslate'], 24 | [q{<: $lang :>:}, 'Xslate:' ], 25 | 26 | [<<'T', <<'X' ], 27 | <: $lang :> 28 | : "foo\n" 29 | T 30 | Xslate 31 | foo 32 | X 33 | 34 | [<<'T', <<'X' ], 35 | <: $lang :>: 42 36 | T 37 | Xslate: 42 38 | X 39 | 40 | [<<'T', 42 ], 41 | : for [42] -> $it { 42 | : $it 43 | : } 44 | T 45 | 46 | # from Template::Benchmark 47 | [<<'T' x 10, <<'X' x 10], 48 | <: for $hash.keys() ->($k) { :><:= $k :><:= ":" :> <:= $hash[ $k ] :><: } :> 49 | T 50 | age: 43name: Larry Nomates 51 | X 52 | 53 | ); 54 | 55 | my %vars = ( 56 | lang => 'Xslate', 57 | foo => "", 58 | hash => { name => 'Larry Nomates', age => 43, }, 59 | ); 60 | foreach my $pair(@data) { 61 | my($in, $out) = @$pair; 62 | is $tx->render_string($in, \%vars), $out 63 | or diag $in; 64 | } 65 | 66 | done_testing; 67 | -------------------------------------------------------------------------------- /t/010_internals/022_signal.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | use Text::Xslate; 5 | 6 | plan skip_all => 'disable on Windows with older perl(<5.14.2)' if $^O eq 'MSWin32' && $] < 5.014002; 7 | 8 | my $tx = Text::Xslate->new(); 9 | $tx->render_string(''); # load related modules 10 | 11 | eval { 12 | local $SIG{ALRM} = sub { die "TIMEOUT" }; 13 | alarm(1); 14 | $tx->render_string(q{: while true {} }); 15 | }; 16 | 17 | like $@, qr/TIMEOUT/; 18 | 19 | done_testing; 20 | -------------------------------------------------------------------------------- /t/010_internals/023_cachepath.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | use Text::Xslate::Util qw(); 8 | use File::Path; 9 | use lib "t/lib"; 10 | use Util; 11 | 12 | rmtree(cache_dir); 13 | END{ rmtree(cache_dir) } 14 | 15 | package FooOverloadingObjectDir; 16 | use overload 17 | q{""} => sub { return ${shift()} }, 18 | fallback => 1, 19 | ; 20 | sub new { bless \"$_[1]" => $_[0] } 21 | 22 | package main; 23 | 24 | 25 | { 26 | my $tx1 = Text::Xslate->new( 27 | path => [FooOverloadingObjectDir->new(path), { 'foo.tx' => 'Hello' } ], 28 | cache_dir => cache_dir, 29 | cache => 2, 30 | ); 31 | my $tx2 = Text::Xslate->new( 32 | path => [FooOverloadingObjectDir->new(path . '/other'), { 'foo.tx' => 'Hello' } ], 33 | cache_dir => cache_dir, 34 | cache => 2, 35 | ); 36 | 37 | # different path's cachepath is different too 38 | isnt $tx1->find_file('hello.tx')->{cachepath}, 39 | $tx2->find_file('hello.tx')->{cachepath}; 40 | } 41 | 42 | 43 | done_testing; 44 | -------------------------------------------------------------------------------- /t/010_internals/024_die_in_macros.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | 7 | my %vpath = ( 8 | "base.tx" => <<'T', 9 | : block main -> {} 10 | T 11 | 12 | "derived.tx" => <<'T', 13 | : cascade base 14 | : around main -> { 15 | <: $o.foo :> 16 | : } 17 | T 18 | 19 | "block.tx" => <<'T', 20 | : block main -> { 21 | <: $o.foo :> 22 | : } 23 | T 24 | 25 | ); 26 | 27 | { 28 | package Foo; 29 | sub new { bless {} => shift } 30 | sub foo { 31 | die "foo"; 32 | } 33 | package Bar; 34 | sub new { bless {} => shift } 35 | sub bar { 36 | eval { die "bar" }; 37 | } 38 | } 39 | 40 | my $tx = Text::Xslate->new(path => \%vpath, cache => 0, verbose => 0); 41 | 42 | ok $tx->render('block.tx', { o => Foo->new }), 'block'; 43 | ok $tx->render('block.tx', { o => Bar->new }), 'block'; 44 | 45 | 46 | ok $tx->render('derived.tx', { o => Foo->new }), 'cascade'; 47 | ok $tx->render('derived.tx', { o => Bar->new }), 'cascade'; 48 | 49 | is $tx->render_string('Hello, world!'), 'Hello, world!', 'render_string() works'; 50 | 51 | done_testing; 52 | -------------------------------------------------------------------------------- /t/010_internals/025_reset_hooks.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use warnings; 4 | 5 | use Test::More tests => 20; 6 | use Text::Xslate; 7 | 8 | sub reset_diehook { 9 | local $SIG{__DIE__} = sub { die @_ }; 10 | return 42; 11 | } 12 | 13 | sub reset_warnhook { 14 | local $SIG{__WARN__} = sub { warn @_ }; 15 | return 42; 16 | } 17 | 18 | { 19 | my $tx = Text::Xslate->new(function => { 20 | reset_diehook => \&reset_diehook, 21 | reset_warnhook => \&reset_warnhook, 22 | }); 23 | 24 | for ( 1 .. 10 ) { 25 | is $tx->render_string( '[<: reset_diehook() :>]' ), '[42]'; 26 | is $tx->render_string( '[<: reset_warnhook() :>]' ), '[42]'; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /t/010_internals/026_issue16_deep_recursion.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | use Text::Xslate; 5 | 6 | my $tx = Text::Xslate->new( 7 | cache => 0, 8 | syntax => 'TTerse', 9 | path => { 10 | wrap_begin => '[% WRAPPER "base" %]', 11 | wrap_end => '[% END %]', 12 | 13 | header => 'Header' . "\n", 14 | base => '[% INCLUDE "header" %]Hello, [% content %] world!' . "\n", 15 | content => 'Xslate', 16 | }, 17 | header => ['wrap_begin'], 18 | footer => ['wrap_end'], 19 | ); 20 | 21 | is $tx->render_string(q{Xslate}), <<'X'; 22 | Header 23 | Hello, Xslate world! 24 | X 25 | 26 | is $tx->render('content'), <<'X'; 27 | Header 28 | Hello, Xslate world! 29 | X 30 | 31 | done_testing; 32 | -------------------------------------------------------------------------------- /t/010_internals/027_infinite_warn.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | 7 | my %vpath = ( 8 | 'runner.tx' => <<'T', 9 | :include "test.tx"; 10 | T 11 | 'test.tx' => <<'T', 12 | : $test()[0]; 13 | T 14 | ); 15 | 16 | my $err_buf = ''; 17 | close STDERR; 18 | open STDERR, '>', \$err_buf; 19 | 20 | my $tx = Text::Xslate->new(cache => 0, path => \%vpath); 21 | 22 | is $tx->render('runner.tx', {test => sub {warn "WARNING";['OK'] }}), 'OK'; 23 | like $err_buf, qr/\b WARNING \b/xms; 24 | 25 | done_testing; 26 | -------------------------------------------------------------------------------- /t/010_internals/029_large_file.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use warnings; 4 | 5 | use Test::More; 6 | use Text::Xslate; 7 | use File::Temp (); 8 | note 'preparing...'; 9 | my $temp = File::Temp->new(DIR => '.'); 10 | $temp->unlink_on_destroy(1); 11 | 12 | $temp->print("Hello, world!\n") for 1 .. 1024*5; 13 | $temp->close(); 14 | note 'prepared'; 15 | 16 | foreach (1 .. 2) { 17 | note $_; 18 | my $tx = Text::Xslate->new( 19 | cache => 1, 20 | cache_dir => '.xslate_cache', 21 | ); 22 | eval { 23 | $tx->render($temp->filename); 24 | }; 25 | ok !$@ or note $@; 26 | } 27 | 28 | 29 | done_testing; 30 | -------------------------------------------------------------------------------- /t/010_internals/030_die_in_funcs.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | use Text::Xslate; 5 | 6 | { 7 | package Foo; 8 | use Mouse; 9 | 10 | has bar => ( 11 | is => 'rw', 12 | isa => 'Int', 13 | ); 14 | } 15 | 16 | my $tx = Text::Xslate->new( 17 | module => [qw(Carp) => [qw(confess croak)] ], 18 | warn_handler => sub { die @_ }, 19 | ); 20 | 21 | eval { 22 | $tx->render_string('<: croak("foo") :>'); 23 | }; 24 | like $@, qr/foo/, 'croak in functions'; 25 | 26 | eval { 27 | $tx->render_string('<: confess("bar") :>'); 28 | }; 29 | like $@, qr/bar/, 'confess in templates'; 30 | 31 | eval { 32 | $tx->render_string('<: $foo.bar("xyzzy") :>', { foo => Foo->new }); 33 | }; 34 | like $@, qr/Validation failed/, 'confess in templates'; 35 | done_testing; 36 | 37 | -------------------------------------------------------------------------------- /t/010_internals/031_save_src.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use if exists $INC{'Text/Xslate.pm'}, 4 | 'Test::More', skip_all => 'Text::Xslate has been loaded'; 5 | use Test::More; 6 | BEGIN{ $ENV{XSLATE} ||= ''; $ENV{XSLATE} .= ':save_src' } 7 | 8 | use Text::Xslate; 9 | use File::Spec; 10 | use lib "t/lib"; 11 | use Util; 12 | 13 | my $tx = Text::Xslate->new( 14 | path => [{ foo => 'Hello, <: "" :>world!' }, path], 15 | cache => 0, 16 | ); 17 | 18 | note 'from file'; 19 | is $tx->render('hello.tx', { lang => 'Xslate' } ), "Hello, Xslate world!\n"; 20 | is $tx->{source}{File::Spec->catfile(path, 'hello.tx')}, 21 | "Hello, <:= \$lang :> world!\n" 22 | or diag(explain($tx->{source})); 23 | 24 | note 'from hash'; 25 | is $tx->render('foo'), 'Hello, world!'; 26 | is $tx->{source}{foo}, 'Hello, <: "" :>world!' 27 | or diag(explain($tx->{source})); 28 | 29 | note 'from '; 30 | is $tx->render_string('<: 1 + 41 :>'), 42; 31 | is $tx->{source}{''}, '<: 1 + 41 :>'; 32 | 33 | done_testing; 34 | -------------------------------------------------------------------------------- /t/010_internals/032_encoding_error.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More skip_all => 'TODO: the behaviours of PP and XS does not match'; 4 | use Text::Xslate; 5 | use File::Path qw(rmtree); 6 | use utf8; 7 | 8 | use lib "t/lib"; 9 | use Util; 10 | BEGIN{ rmtree(cache_dir) } 11 | END { rmtree(cache_dir) } 12 | 13 | my $str = 'エクスレイト'; 14 | utf8::encode($str); 15 | 16 | my $tx = Text::Xslate->new( 17 | path => [path], 18 | cache_dir => cache_dir, 19 | cache => 0, 20 | ); 21 | 22 | diag $tx->render('hello_utf8.tx', { name => $str }); 23 | diag $tx->render('hello_utf8.tx', { name => $str }); 24 | pass; 25 | 26 | done_testing; 27 | 28 | -------------------------------------------------------------------------------- /t/010_internals/033_vpath_cache.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use warnings; 4 | use Test::More; 5 | use File::Path qw(rmtree); 6 | use File::Basename; 7 | 8 | use constant CACHE_DIR => basename(__FILE__); 9 | BEGIN { rmtree(CACHE_DIR) } 10 | END { rmtree(CACHE_DIR) } 11 | 12 | use Text::Xslate; 13 | 14 | my $compile_called = 0; 15 | { 16 | package MyEngine; 17 | our @ISA = qw(Text::Xslate); 18 | 19 | sub compile { 20 | my($self, @args) = @_; 21 | ::note 'compile!'; 22 | $compile_called++; 23 | return $self->SUPER::compile(@args); 24 | } 25 | } 26 | 27 | my $phase = 0; 28 | foreach my $i(0, 1) { 29 | my %vpath = ( 30 | hello => 'hello#' . $i, 31 | ); 32 | 33 | my $tx = MyEngine->new( 34 | cache_dir => CACHE_DIR, 35 | path => [\%vpath], 36 | ); 37 | foreach my $j(0, 1) { 38 | note "# $i-$j"; 39 | 40 | is $tx->render('hello'), 'hello#' . $i; 41 | 42 | if($phase == 0) { 43 | is $compile_called, 1, 'compiling at first time'; 44 | } 45 | elsif($phase == 1) { 46 | is $compile_called, 1, 'using cache, not compiled'; 47 | } 48 | elsif($phase == 2) { 49 | is $compile_called, 2, 're-compiled because %vpath changed'; 50 | } 51 | elsif($phase == 3) { 52 | is $compile_called, 2, 'using cache'; 53 | } 54 | else { 55 | fail 'something failed'; 56 | } 57 | $phase++; 58 | } 59 | } 60 | 61 | done_testing; 62 | 63 | -------------------------------------------------------------------------------- /t/010_internals/038_suffix.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use warnings; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | 8 | BEGIN { 9 | package Text::Xslate::Syntax::Custom; 10 | use Mouse; 11 | extends 'Text::Xslate::Parser'; 12 | 13 | sub init_symbols { 14 | my $self = shift; 15 | 16 | $self->SUPER::init_symbols(@_); 17 | 18 | $self->symbol('suffix')->set_nud($self->can('nud_suffix')); 19 | } 20 | 21 | sub nud_suffix { 22 | my $self = shift; 23 | my ($symbol) = @_; 24 | 25 | return $symbol->clone(arity => 'suffix'), 26 | } 27 | 28 | package Text::Xslate::Compiler::Custom; 29 | use Mouse; 30 | extends 'Text::Xslate::Compiler'; 31 | 32 | sub _generate_suffix { 33 | my $self = shift; 34 | my ($node) = @_; 35 | 36 | return ( 37 | $self->opcode('suffix'), 38 | ); 39 | } 40 | 41 | package Text::Xslate::Custom; 42 | use base 'Text::Xslate'; 43 | 44 | sub options { 45 | my $class = shift; 46 | 47 | my $options = $class->SUPER::options(@_); 48 | 49 | $options->{compiler} = 'Text::Xslate::Compiler::Custom'; 50 | $options->{syntax} = 'Custom'; 51 | 52 | return $options; 53 | } 54 | } 55 | 56 | my $tx = Text::Xslate::Custom->new(suffix => '.mustache'); 57 | 58 | is( 59 | $tx->render_string( 60 | ': suffix;', 61 | {} 62 | ), 63 | '.mustache', 64 | "got the right suffix" 65 | ); 66 | 67 | done_testing; 68 | -------------------------------------------------------------------------------- /t/010_internals/039_taint_issue84.t: -------------------------------------------------------------------------------- 1 | #!perl -wT 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | use File::Path; 8 | use Scalar::Util qw(tainted); 9 | use Cwd qw(getcwd); 10 | 11 | my $cwd; 12 | my $cache_dir; 13 | tainted($cwd) and die "cwd is tainted"; 14 | 15 | BEGIN{ 16 | $cwd = getcwd(); 17 | if ($cwd =~ /(.+)/) { 18 | $cwd = $1; 19 | } 20 | $cache_dir = $cwd . '/xxx_test_taint_issue84'; 21 | } 22 | END{ rmtree($cache_dir) } 23 | 24 | tainted($cache_dir) and die "cache_dir is tainted"; 25 | 26 | 27 | for (1 .. 2) { 28 | my $tx = Text::Xslate->new( 29 | path => [$cwd . '/t/template'], 30 | cache_dir => $cache_dir, 31 | cache => 1, 32 | ); 33 | 34 | for(1 .. 2) { 35 | is $tx->render('hello.tx', { lang => 'Xslate'}), 36 | "Hello, Xslate world!\n"; 37 | utime time()+rand(100), time()+rand(100), "$cwd/t/template/hello.tx"; 38 | } 39 | } 40 | 41 | done_testing; 42 | -------------------------------------------------------------------------------- /t/010_internals/300_explicit_pp.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | # Explicit use of PP version 3 | 4 | use strict; 5 | use Test::More tests => 5; 6 | 7 | BEGIN { 8 | use_ok 'Text::Xslate::PP'; 9 | use_ok 'Text::Xslate', qw(escaped_string); 10 | } 11 | 12 | 13 | use B; 14 | 15 | ok( !B::svref_2object(Text::Xslate->can('render'))->XSUB, 'render() is not an xsub' ); 16 | 17 | eval { 18 | my $tx = Text::Xslate->new(); 19 | 20 | is $tx->render_string('Hello, <: $lang :> world!', { lang => escaped_string('') }), 21 | "Hello, world!"; 22 | }; 23 | is $@, ''; 24 | 25 | done_testing; 26 | -------------------------------------------------------------------------------- /t/020_interface/002_myparser.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | use lib "t/lib"; 8 | 9 | # moniker 10 | my $tx1 = Text::Xslate->new(syntax => 'Foo'); 11 | # fq name 12 | my $tx2 = Text::Xslate->new(syntax => 'Text::Xslate::Syntax::Foo'); 13 | 14 | my @data = ( 15 | ['Hello, <%= $lang %> world!' => 'Hello, Xslate world!'], 16 | ['Hello, <%= $foo %> world!' => 'Hello, <bar> world!'], 17 | [q{foo <%= $lang 18 | %> bar} => "foo Xslate bar"], 19 | 20 | # no default 21 | ['Hello, <: $lang :> world!' => 'Hello, <: $lang :> world!'], 22 | [':= $lang', ':= $lang'], 23 | 24 | # no line code 25 | ['%= $lang', '%= $lang'], 26 | ); 27 | 28 | foreach my $pair(@data) { 29 | my($in, $out) = @$pair; 30 | my %vars = (lang => 'Xslate', foo => ""); 31 | 32 | is $tx1->render_string($in, \%vars), $out, $in; 33 | is $tx2->render_string($in, \%vars), $out, $in; 34 | } 35 | 36 | done_testing; 37 | -------------------------------------------------------------------------------- /t/020_interface/004_no_vars.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | 8 | use lib "t/lib"; 9 | use Util; 10 | 11 | my $tx = Text::Xslate->new(); 12 | 13 | is $tx->render_string("Hello, world!"), "Hello, world!"; 14 | is $tx->render_string("Hello, world!", undef), "Hello, world!"; 15 | 16 | done_testing; 17 | -------------------------------------------------------------------------------- /t/020_interface/007_data_section.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | use lib "t/lib"; 8 | use Util; 9 | 10 | use File::Path qw(rmtree); 11 | 12 | rmtree(cache_dir); 13 | END{ rmtree(cache_dir) } 14 | 15 | my %vpath = ( 16 | 'foo.tx' => 'Hello, <: $lang :> world!', 17 | 18 | 'base.tx' => <<'T', 19 | 20 | <: block body -> { :>default body<: } :> 21 | 22 | T 23 | 24 | 'child.tx' => <<'T', 25 | : cascade base; 26 | : override body -> { 27 | child body 28 | : } # endblock body 29 | T 30 | ); 31 | 32 | for my $cache(0 .. 2) { 33 | note "cache => $cache"; 34 | my $tx = Text::Xslate->new( 35 | path => [ \%vpath, path ], 36 | cache_dir => cache_dir, 37 | cache => $cache, 38 | ); 39 | 40 | is $tx->render('foo.tx', { lang => 'Xslate' }), 'Hello, Xslate world!', "(1)"; 41 | is $tx->render('foo.tx', { lang => 'Perl' }), 'Hello, Perl world!', "(2)"; 42 | 43 | is $tx->render('child.tx'), <<'X' for 1 .. 2; 44 | 45 | child body 46 | 47 | 48 | X 49 | 50 | is $tx->render('hello.tx', { lang => 'Xslate' }), "Hello, Xslate world!\n", "for real files"; 51 | 52 | 53 | # reload 54 | local $vpath{'foo.tx'} = 'Modified'; 55 | $tx = Text::Xslate->new( 56 | path => \%vpath, 57 | cache_dir => cache_dir, 58 | cache => $cache, 59 | ); 60 | is $tx->render('foo.tx', { lang => 'Xslate' }), 'Modified', 'reloaded'; 61 | } 62 | 63 | done_testing; 64 | -------------------------------------------------------------------------------- /t/020_interface/008_type.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | use lib "t/lib"; 8 | use Util; 9 | use File::Path qw(rmtree); 10 | 11 | rmtree(cache_dir); 12 | END{ rmtree(cache_dir) } 13 | 14 | my $tx = Text::Xslate->new( 15 | path => path, 16 | cache_dir => cache_dir, 17 | type => 'text', 18 | ); 19 | 20 | my @set = ( 21 | [<<'T', { value => "" }, <<'X', 'type => "text"'], 22 | Hello, <: $value :>! 23 | T 24 | Hello, ! 25 | X 26 | ); 27 | 28 | foreach my $d(@set) { 29 | my($in, $vars, $out, $msg) = @$d; 30 | is $tx->render_string($in, $vars), $out, $msg or diag $in; 31 | } 32 | 33 | is $tx->render('hello.tx', { lang => '' }), 34 | "Hello, world!\n"; 35 | 36 | $tx = Text::Xslate->new( 37 | path => path, 38 | cache_dir => cache_dir, 39 | type => 'html', 40 | ); 41 | is $tx->render('hello.tx', { lang => '' }), 42 | "Hello, <Xslate> world!\n", "type => 'html'"; 43 | 44 | $tx = Text::Xslate->new( 45 | path => path, 46 | cache_dir => cache_dir, 47 | type => 'xml', 48 | ); 49 | is $tx->render('hello.tx', { lang => '' }), 50 | "Hello, <Xslate> world!\n", "type => 'xml'"; 51 | 52 | done_testing; 53 | -------------------------------------------------------------------------------- /t/020_interface/009_hash_with_default.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | use Text::Xslate; 5 | use Text::Xslate::Util qw(hash_with_default); 6 | 7 | my $tx = Text::Xslate->new( 8 | cache => 0, 9 | ); 10 | 11 | my $vars = hash_with_default {}, 'FILLME'; 12 | 13 | is $tx->render_string(<<'T', $vars), "Hello, FILLME world!\n"; 14 | Hello, <: $oops :> world! 15 | T 16 | 17 | is $tx->render_string(<<'T', $vars), "FILLME, FILLME, FILLME\n"; 18 | <: $a :>, <: $b :>, <: $c :> 19 | T 20 | 21 | $vars = hash_with_default {}, sub { "FILLME/@_" }; 22 | 23 | is $tx->render_string(<<'T', $vars), "Hello, FILLME/oops world!\n"; 24 | Hello, <: $oops :> world! 25 | T 26 | 27 | is $tx->render_string(<<'T', $vars), "FILLME/a, FILLME/b, FILLME/c\n"; 28 | <: $a :>, <: $b :>, <: $c :> 29 | T 30 | 31 | done_testing; 32 | 33 | -------------------------------------------------------------------------------- /t/020_interface/010_err_encoding.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | use Text::Xslate; 5 | use lib "t/lib"; 6 | use Util; 7 | use utf8; 8 | 9 | binmode Test::More->builder->output, ":utf8"; 10 | binmode Test::More->builder->failure_output, ":utf8"; 11 | binmode Test::More->builder->todo_output, ":utf8"; 12 | 13 | my $warn = ''; 14 | 15 | my $tx = Text::Xslate->new( 16 | cache => 0, 17 | path => [path], 18 | verbose => 2, 19 | warn_handler => sub { 20 | $warn .= "@_"; 21 | }, 22 | ); 23 | 24 | like $tx->render('hello_utf8.tx'), qr/こんにちは/; 25 | like $warn, qr/こんにちは/; 26 | 27 | eval { 28 | $tx->render_string('<: こんにちは'); # syntax error 29 | }; 30 | note $@; 31 | like $@, qr/\<\: こんにちは/, 'wide characters in error messages'; 32 | 33 | done_testing; 34 | 35 | -------------------------------------------------------------------------------- /t/020_interface/011_hwd_w_include_vars.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use warnings; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | use Text::Xslate::Util qw(hash_with_default); 8 | 9 | my $tx = Text::Xslate->new( 10 | path => [{ 'dish.tx' => '{[% food %]}' } ], 11 | cache => 0, 12 | syntax => 'TTerse', 13 | verbose => 2, 14 | ); 15 | 16 | my $_var = { food => 'rakkyo' }; 17 | my $var; 18 | my $tmpl =<render_string($tmpl, $var), '{gyoza}'; 27 | 28 | $var = hash_with_default($_var, sub { qq{*FILLME ($_[0])*} }); 29 | is $tx->render_string($tmpl, $var), '{gyoza}'; 30 | 31 | done_testing; 32 | 33 | -------------------------------------------------------------------------------- /t/020_interface/012_default_functions.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | use Text::Xslate; 5 | 6 | { 7 | package MyXslate; 8 | use parent qw(Text::Xslate); 9 | 10 | sub default_functions { 11 | return { 12 | foo => sub { 'bar' }, 13 | blessed => sub { 42 }, 14 | }; 15 | } 16 | } 17 | 18 | my $tx = MyXslate->new( 19 | module => [qw(Scalar::Util) => [qw(blessed)]], 20 | ); 21 | 22 | is $tx->render_string('<: foo() :>'), 'bar'; 23 | is $tx->render_string('<: blessed($o) :>', { o => bless {}, 'XYZ' }), 'XYZ'; 24 | 25 | 26 | done_testing; 27 | 28 | -------------------------------------------------------------------------------- /t/020_interface/013_slurp_template.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use utf8; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | use Encode (); 8 | 9 | use File::Path qw(rmtree); 10 | use lib "t/lib"; 11 | use Util; 12 | rmtree(cache_dir); 13 | END{ rmtree(cache_dir) } 14 | 15 | { 16 | package MyTemplate; 17 | our @ISA = qw(Text::Xslate); 18 | 19 | sub slurp_template { 20 | my($self, $input_layer, $fullpath) = @_; 21 | 22 | my $content = $self->SUPER::slurp_template($input_layer, $fullpath); 23 | return Encode::encode('cp932', $content); 24 | } 25 | } 26 | 27 | my $p = Encode::encode('cp932', 'エクスレート'); 28 | 29 | for(1 .. 2) { 30 | my $tx = MyTemplate->new( 31 | path => [path], 32 | cache_dir => cache_dir, 33 | ); 34 | 35 | 36 | is $tx->render('hello_utf8.tx', { name => $p }), 37 | Encode::encode('cp932', "こんにちは! エクスレート!\n"); 38 | 39 | is $tx->render('hello_utf8.tx', { name => $p }), 40 | Encode::encode('cp932', "こんにちは! エクスレート!\n"); 41 | } 42 | done_testing; 43 | 44 | 45 | -------------------------------------------------------------------------------- /t/020_interface/015_render_recursion.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | # recursive call of render() 3 | # related to issue #65 but there are no problems. 4 | # 5 | use strict; 6 | use warnings; 7 | use Test::More; 8 | 9 | use Text::Xslate; 10 | 11 | my %vpath = ( 12 | foo => <<'T', 13 | : for [42] -> $i { 14 | : render('bar'); 15 | : } 16 | T 17 | 18 | bar => <<'T', 19 | Hello, world! 20 | T 21 | ); 22 | 23 | my $tx; 24 | $tx = Text::Xslate->new( 25 | cache => 0, 26 | path => [\%vpath], 27 | 28 | function => { 29 | render => sub { 30 | return $tx->render(@_); 31 | }, 32 | }, 33 | ); 34 | 35 | is $tx->render('foo'), "Hello, world!\n"; 36 | 37 | done_testing; 38 | 39 | -------------------------------------------------------------------------------- /t/020_interface/016_pre_process_handler.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use utf8; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | use Encode (); 8 | 9 | use File::Path qw(rmtree); 10 | use lib "t/lib"; 11 | use Util; 12 | rmtree(cache_dir); 13 | END{ rmtree(cache_dir) } 14 | 15 | my $p = Encode::encode('cp932', 'エクスレート'); 16 | 17 | for(1 .. 2) { 18 | my $tx = Text::Xslate->new( 19 | path => [path], 20 | cache_dir => cache_dir, 21 | pre_process_handler => sub { Encode::encode('cp932', $_[0]) }, 22 | ); 23 | 24 | 25 | is $tx->render('hello_utf8.tx', { name => $p }), 26 | Encode::encode('cp932', "こんにちは! エクスレート!\n"); 27 | 28 | is $tx->render('hello_utf8.tx', { name => $p }), 29 | Encode::encode('cp932', "こんにちは! エクスレート!\n"); 30 | } 31 | done_testing; 32 | -------------------------------------------------------------------------------- /t/020_interface/017_validate.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use warnings; 4 | use Test::More; 5 | use File::Temp qw(tempdir); 6 | 7 | use Text::Xslate; 8 | 9 | my %vpath = ( 10 | ok0 => <<'T', 11 | Hello, world! 12 | T 13 | 14 | ok1=> <<'T', 15 | Hello, <: $xslate :> world! 16 | T 17 | 18 | ng0=> <<'T', 19 | Hello, <: $xslate world! 20 | T 21 | 22 | ng1=> <<'T', 23 | Hello, <: $xslate ??? :> world! 24 | T 25 | ); 26 | 27 | my $tx = Text::Xslate->new( 28 | path => [\%vpath], 29 | cache_dir => tempdir(CLEANUP => 1), 30 | ); 31 | 32 | foreach my $name (qw(ok0 ok1)) { 33 | eval { $tx->validate($name) }; 34 | ok !$@, $name; 35 | note $@ if $@; 36 | } 37 | 38 | foreach my $name (qw(ng0 ng1)) { 39 | eval { $tx->validate($name) }; 40 | ok $@, $name; 41 | note $@ if $@; 42 | } 43 | 44 | done_testing; 45 | -------------------------------------------------------------------------------- /t/030_kolon/001_interpolate.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | use Text::Xslate::Util qw(p); 7 | 8 | my $tx = Text::Xslate->new(); 9 | 10 | my @data = ( 11 | ['Hello, world!' => 'Hello, world!'], 12 | ['Hello, <: $lang :> world!' => 'Hello, Xslate world!'], 13 | ['Hello, <: $foo :> world!' => 'Hello, <bar> world!'], 14 | ['<: $lang :> <: $foo :> <: $lang :> <: $foo :>' => 'Xslate <bar> Xslate <bar>'], 15 | [q{<:$lang:>}, 'Xslate'], 16 | [q{foo <:= $lang 17 | :> bar} => "foo Xslate bar"], 18 | [q{<: print $lang :>} => "Xslate"], 19 | [q{<:print $lang:>} => "Xslate"], 20 | [q{<:print$lang:>} => "Xslate"], 21 | [q{<:print $lang, "\n":>} => "Xslate\n"], 22 | 23 | [q{<:print "<", $lang, ">":>} => "<Xslate>"], 24 | [q{<:print_raw "<", $lang, ">":>} => ""], 25 | ); 26 | 27 | my %vars = (lang => 'Xslate', foo => ""); 28 | my $vars_str = p(\%vars); 29 | foreach my $pair(@data) { 30 | my($in, $out) = @$pair; 31 | 32 | #diag $in; 33 | is $tx->render_string($in, \%vars), $out or diag $in; 34 | 35 | is p(\%vars), $vars_str, 'vars are not changed'; 36 | } 37 | 38 | done_testing; 39 | -------------------------------------------------------------------------------- /t/030_kolon/009_include.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | use lib "t/lib"; 8 | use Util; 9 | 10 | my $tx = Text::Xslate->new(cache => 0, path => [path]); 11 | 12 | for(1 .. 2) { # to test including depth 13 | for(1 .. 100){ 14 | is $tx->render('include.tx', { lang => "Xslate" }), 15 | "include:\n" . "Hello, Xslate world!\n", "index.tx ($_)"; 16 | 17 | is $tx->render('include2.tx', { file => "hello.tx", lang => "Xslate" }), 18 | "include2:\n" . "Hello, Xslate world!\n", 'index2.tx (literal)'; 19 | 20 | is $tx->render('include2.tx', { file => "include.tx", lang => "Xslate" }), 21 | "include2:\n" . "include:\n" . "Hello, Xslate world!\n", 'index2.tx (var)'; 22 | } 23 | 24 | eval { 25 | $tx->render('include2.tx', { file => "include2.tx", lang => "Xslate" }); 26 | }; 27 | like $@, qr/\bXslate\b/xms, "recursion"; 28 | like $@, qr/too \s+ deep/xms, "recursion"; 29 | } 30 | 31 | done_testing; 32 | -------------------------------------------------------------------------------- /t/030_kolon/013_more_field.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | 7 | my $tx = Text::Xslate->new(); 8 | 9 | my @data = ( 10 | ['<:= $var[ $foo && "bar" ] :>', 'BAR'], 11 | ['<:= $var[ $foo || "bar" ] :>', 'FOO'], 12 | 13 | ['<:= $var[ "foo" && "bar" ] :>', 'BAR'], 14 | ['<:= $var[ "foo" || "bar" ] :>', 'FOO'], 15 | 16 | ['<:= $var[ $foo == "foo" ? "bar" : "baz" ] :>', 'BAR'], 17 | ['<:= $var[ $foo != "foo" ? "bar" : "baz" ] :>', 'BAZ'], 18 | 19 | ['<:= $ary[ 0+0 ] :>', 10], 20 | ['<:= $ary[ 0+1 ] :>', 20], 21 | ['<:= $ary[ 1+1 ] :>', 30], 22 | 23 | ['<:= $ary[ 0-0 ] :>', 10], 24 | ['<:= $ary[ 2-1 ] :>', 20], 25 | ['<:= $ary[ 3-1 ] :>', 30], 26 | 27 | ['<:= $ary[ +0 ] :>', 10], 28 | ['<:= $ary[ -0 ] :>', 10], 29 | 30 | ['<:= $ary[ +0.0 ] :>', 10], 31 | ['<:= $ary[ -0.0 ] :>', 10], 32 | 33 | ['<:= $var[ $ary[3] ] :>', "FOO"], 34 | ); 35 | 36 | foreach my $pair(@data) { 37 | my($in, $out) = @$pair; 38 | 39 | my %vars = ( 40 | var => { foo => 'FOO', bar => 'BAR', baz => "BAZ" }, 41 | 42 | ary => [10, 20, 30, "foo"], 43 | 44 | foo => 'foo', 45 | ); 46 | 47 | is $tx->render_string($in, \%vars), $out or diag $in; 48 | } 49 | 50 | done_testing; 51 | -------------------------------------------------------------------------------- /t/030_kolon/018_dynamic_filters.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | 7 | use FindBin qw($Bin); 8 | 9 | sub mk_indent { 10 | my($prefix) = @_; 11 | 12 | return sub { 13 | my($str) = @_; 14 | $str =~ s/^/$prefix/xmsg; 15 | return $str; 16 | } 17 | } 18 | 19 | my $tx = Text::Xslate->new( 20 | function => { 21 | indent => \&mk_indent, 22 | }, 23 | ); 24 | 25 | my @set = ( 26 | [ q{<: $value | indent("> ") :>}, { value => 'Xslate' } 27 | => '> Xslate' ], 28 | 29 | [ q{<: $value | indent("> ") :>}, { value => "Xslate\nPerl\n" } 30 | => "> Xslate\n> Perl\n" ], 31 | 32 | [ q{: $value | indent("| ") }, { value => "Xslate\nPerl\n" } 33 | => "| Xslate\n| Perl\n" ], 34 | 35 | [ q{: indent("* ")($value) }, { value => "Xslate\nPerl\n" } 36 | => "* Xslate\n* Perl\n" ], 37 | ); 38 | 39 | foreach my $d(@set) { 40 | my($in, $vars, $out, $msg) = @$d; 41 | is $tx->render_string($in, $vars), $out, $msg or diag $in;; 42 | } 43 | 44 | done_testing; 45 | -------------------------------------------------------------------------------- /t/030_kolon/019_explicit_interpolate.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | 7 | my $tx = Text::Xslate->new(); 8 | 9 | my @data = ( 10 | ['Hello, world!' => 'Hello, world!'], 11 | ['Hello, <:= $lang :> world!' => 'Hello, Xslate world!'], 12 | ['Hello, <:= $foo :> world!' => 'Hello, <bar> world!'], 13 | ['<:= $lang :> <:= $foo :> <:= $lang :> <:= $foo :>' => 'Xslate <bar> Xslate <bar>'], 14 | [q{foo <:= $lang 15 | :> bar} => "foo Xslate bar"], 16 | [q{<: print $lang :>} => "Xslate"], 17 | [q{<:print $lang:>} => "Xslate"], 18 | [q{<:print $lang, "\n":>} => "Xslate\n"], 19 | [q{<:print "<", $lang, ">":>} => "<Xslate>"], 20 | [q{<:print_raw "<", $lang, ">":>} => ""], 21 | 22 | ['<:= "foo\tbar\n" :>', "foo\tbar\n"], 23 | [q{<:= 'foo\tbar\n' :>}, 'foo\tbar\n'], 24 | [q{<:= ' & " \' ' :>}, ' & " ' '], 25 | 26 | [q{foo<:# this is a comment :>bar}, "foobar"], 27 | [q{<:=$lang:> foo<:# this is a comment :>bar <:=$lang:>}, "Xslate foobar Xslate"], 28 | [q{foo<: 29 | :>bar}, "foobar"], 30 | [q{foo<: # this is a comment 31 | $lang :>bar}, "fooXslatebar"], 32 | ); 33 | 34 | foreach my $pair(@data) { 35 | my($in, $out) = @$pair; 36 | my %vars = (lang => 'Xslate', foo => ""); 37 | 38 | is $tx->render_string($in, \%vars), $out or diag $in; 39 | } 40 | 41 | done_testing; 42 | -------------------------------------------------------------------------------- /t/030_kolon/020_chomp.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | 7 | my $tx = Text::Xslate->new(); 8 | 9 | my @data = ( 10 | [<<'T', "A[foo]\nB\n", "prechomp(1)"], 11 | A 12 | <:- "[foo]" :> 13 | B 14 | T 15 | 16 | [<<'T', " A[foo]\n B\n", "prechomp(2)"], 17 | A 18 | <:- "[foo]" :> 19 | B 20 | T 21 | 22 | [<<'T', "A\n[foo]B\n", "postchomp(1)"], 23 | A 24 | <: "[foo]" -:> 25 | B 26 | T 27 | 28 | [<<'T', " A\n [foo] B\n", "postchomp(2)"], 29 | A 30 | <: "[foo]" -:> 31 | B 32 | T 33 | 34 | 35 | [<<'T', "A[foo]B\n", "both(1)"], 36 | A 37 | <:- "[foo]" -:> 38 | B 39 | T 40 | 41 | [<<'T', " A[foo] B\n", "both(2)"], 42 | A 43 | <:- "[foo]" -:> 44 | B 45 | T 46 | 47 | ); 48 | 49 | foreach my $pair(@data) { 50 | my($in, $out, $msg) = @$pair; 51 | my %vars = (lang => 'Xslate', foo => ""); 52 | is $tx->render_string($in, \%vars), $out, $msg or diag $in; 53 | } 54 | 55 | done_testing; 56 | -------------------------------------------------------------------------------- /t/030_kolon/023_bitwise.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | 7 | my $tx = Text::Xslate->new(); 8 | 9 | my @data = ( 10 | ['<: 0x011 +| 0x100 :>', 0x111 ], 11 | ['<: $value0 +| 0x111 :>', 0x111 ], 12 | ['<: $value0x201 +| 0x111 :>', 0x311 ], 13 | ['<: 0x111 +| $value0x201 :>', 0x311 ], 14 | 15 | ['<: 0x011 +& 0x010 :>', 0x010 ], 16 | ['<: $value0 +& 0x111 :>', 0x000 ], 17 | ['<: $value0x201 +& 0x111 :>', 0x001 ], 18 | ['<: 0x111 +& $value0x201 :>', 0x001 ], 19 | 20 | ['<: 0x00101 +^ 0x00100 :>', 0x00001 ], 21 | ['<: 0x10100 +^ 0x10000 :>', 0x00100 ], 22 | ['<: $value0 +^ 0x111 :>', 0x111 ], 23 | ['<: $value0x201 +^ 0x111 :>', 0x310 ], 24 | ['<: 0x111 +^ $value0x201 :>', 0x310 ], 25 | 26 | ['<: +^0 :>', ~0, 'const'], 27 | ['<: +^0x201 :>', ~0x201, 'const'], 28 | ['<: +^$value0 :>', ~0, 'var'], 29 | ['<: +^$value0x201 :>', ~0x201, 'var'], 30 | ); 31 | 32 | my %vars = ( 33 | value0 => 0, 34 | value0x201 => 0x201, 35 | ); 36 | foreach my $d(@data) { 37 | my($in, $out, $msg) = @$d; 38 | 39 | is $tx->render_string($in, \%vars), $out, $msg 40 | or diag $in; 41 | } 42 | 43 | done_testing; 44 | -------------------------------------------------------------------------------- /t/030_kolon/027_lambda.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | use Text::Xslate::Compiler; 8 | use Text::Xslate::Util qw(p); 9 | 10 | my $tx = Text::Xslate->new(); 11 | 12 | my @set = ( 13 | [<<'T', {}, <<'X'], 14 | <: -> $x { $x + 10 }(0) :> 15 | <: -> $x { $x + 10 }(1) :> 16 | <: -> $x { $x + 10 }(2) :> 17 | T 18 | 10 19 | 11 20 | 12 21 | X 22 | [<<'T', {}, <<'X'], 23 | <: my $y = 10; -> $x { $x + $y }(15) :> 24 | T 25 | 25 26 | X 27 | 28 | [<<'T', {}, <<'X'], 29 | <: my $add10 = -> $x { $x + 10 }; $add10(20) :> 30 | T 31 | 30 32 | X 33 | 34 | [<<'T', {}, <<'X'], 35 | <: constant add10 = -> $x { $x + 10 }; add10(20) :> 36 | T 37 | 30 38 | X 39 | 40 | [<<'T', {}, <<'X'], 41 | : for [1, 2, 3] -> $v { 42 | <: my $addv = -> $x { $x + $v }; $addv(20) :> 43 | : } 44 | T 45 | 21 46 | 22 47 | 23 48 | X 49 | 50 | 51 | ); 52 | 53 | foreach my $d(@set) { 54 | my($in, $vars, $out, $msg) = @$d; 55 | 56 | is $tx->render_string($in, $vars), $out, $msg 57 | or diag($in); 58 | } 59 | 60 | 61 | done_testing; 62 | -------------------------------------------------------------------------------- /t/030_kolon/030_xfuncs.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | 7 | my $tx = Text::Xslate->new( 8 | function => { 9 | 'array::count' => sub { 10 | my($a, $cb) = @_; 11 | return scalar grep { $cb->($_) } @{$a}; 12 | }, 13 | }, 14 | ); 15 | 16 | my @data = ( 17 | [<<'T', <<'X'], 18 | : macro upper50 -> $x { $x >= 50 } 19 | <: $a.count(upper50) :> 20 | T 21 | 50 22 | X 23 | [<<'T', <<'X'], 24 | <: $a.count(-> $x { $x >= 0 }) :> 25 | <: $a.count(-> $x { $x >= 50 }) :> 26 | <: $a.count(-> $x { $x >= 100 }) :> 27 | <: $a.count(-> $x { $x == nil }) :> 28 | <: $a.count(-> $x { $x == 42 }) :> 29 | T 30 | 100 31 | 50 32 | 0 33 | 0 34 | 1 35 | X 36 | ); 37 | 38 | my %vars = ( 39 | a => [ 0 .. 99 ], 40 | ); 41 | foreach my $d(@data) { 42 | my($in, $out, $msg) = @$d; 43 | 44 | is $tx->render_string($in, \%vars), $out, $msg 45 | or diag $in; 46 | } 47 | 48 | done_testing; 49 | -------------------------------------------------------------------------------- /t/030_kolon/031_post_if.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | use lib "t/lib"; 7 | use Util; 8 | 9 | my $tx = Text::Xslate->new( 10 | cache => 0, 11 | path => [path], 12 | ); 13 | 14 | my @data = ( 15 | [<<'T', <<'X'], 16 | [<: "foo" if true :>] 17 | T 18 | [foo] 19 | X 20 | 21 | [<<'T', <<'X'], 22 | [<: "foo" if false :>] 23 | T 24 | [] 25 | X 26 | 27 | [<<'T', <<'X'], 28 | [<: "foo" if $a[0] == 0 :>] 29 | T 30 | [foo] 31 | X 32 | 33 | [<<'T', <<'X'], 34 | [<: "foo" if $a[0] == 1 :>] 35 | T 36 | [] 37 | X 38 | 39 | [<<'T', <<'X', 'include-if'], 40 | : include "hello.tx" if true 41 | : include "hello.tx" if true 42 | T 43 | Hello, Xslate world! 44 | Hello, Xslate world! 45 | X 46 | 47 | [<<'T', <<'X'], 48 | : include "hello.tx" if false 49 | : include "hello.tx" if false 50 | T 51 | X 52 | 53 | ); 54 | 55 | my %vars = ( 56 | lang => 'Xslate', 57 | a => [ 0 .. 99 ], 58 | ); 59 | foreach my $d(@data) { 60 | my($in, $out, $msg) = @$d; 61 | 62 | is $tx->render_string($in, \%vars), $out, $msg 63 | or diag $in; 64 | } 65 | 66 | done_testing; 67 | -------------------------------------------------------------------------------- /t/030_kolon/032_include_bareword.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | use Text::Xslate; 5 | 6 | my %vpath = ( 7 | 'hello.tx' => 'Hello, world!', 8 | ); 9 | 10 | my $tx = Text::Xslate->new( 11 | path => \%vpath, 12 | cache => 0, 13 | ); 14 | 15 | is $tx->render('hello.tx'), 'Hello, world!'; 16 | is $tx->render_string(<render_string(< 'Hello, world!', 26 | ); 27 | 28 | $tx = Text::Xslate->new( 29 | path => \%vpath, 30 | cache => 0, 31 | suffix => '.html', 32 | ); 33 | 34 | is $tx->render_string(<new( 8 | function => { 9 | myprint => sub { Text::Xslate->print(@_) }, 10 | mysay => sub { Text::Xslate->print(@_, "\n") }, 11 | }, 12 | 13 | warn_handler => sub { Text::Xslate->print(@_) }, 14 | verbose => 2, 15 | ); 16 | 17 | is $tx->render_string('[<: myprint() :>]'), '[]'; 18 | is $tx->render_string('[<: myprint("") :>]'), 19 | '[<foo>]'; 20 | is $tx->render_string('[<: myprint("", "") :>]'), 21 | '[<foo><bar>]'; 22 | 23 | is $tx->render_string('[<: mysay() :>]'), "[\n]"; 24 | is $tx->render_string('[<: mysay("") :>]'), 25 | "[<foo>\n]"; 26 | is $tx->render_string('[<: mysay("", "") :>]'), 27 | "[<foo><bar>\n]"; 28 | 29 | like $tx->render_string("<: nil :>"), qr/nil/; 30 | like $tx->render_string("<: nil :>"), qr/<string>/; 31 | 32 | done_testing; 33 | 34 | -------------------------------------------------------------------------------- /t/030_kolon/035_include_w_vars.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | 8 | my %vpath = ( 9 | foo => ': include "bar" { $baz }', 10 | bar => 'Hello, <: $lang :> world!', 11 | ); 12 | 13 | my $tx = Text::Xslate->new( 14 | cache => 0, 15 | path => \%vpath, 16 | verbose => 2, 17 | warn_handler => sub { die @_ }, 18 | ); 19 | 20 | is $tx->render(foo => { baz => { lang => 'Xslate' } }), 21 | 'Hello, Xslate world!'; 22 | 23 | is $tx->render(foo => { baz => { lang => 'Template' } }), 24 | 'Hello, Template world!'; 25 | 26 | is $tx->render_string(':include "bar" { $baz }; $lang', 27 | { baz => { lang => 'Xslate' }, lang => '!!' }), 28 | 'Hello, Xslate world!!!'; 29 | 30 | is $tx->render_string(':include "bar" { $baz.merge({ lang => "Xslate"}) }; $lang', 31 | { baz => { }, lang => '!!' }), 32 | 'Hello, Xslate world!!!'; 33 | 34 | eval { $tx->render_string(': include "bar" { a => 42, "b" }' ) }; 35 | like $@, qr/pairs/; 36 | 37 | eval { $tx->render_string(': include "bar" { 42 }' ) }; 38 | like $@, qr/must be a HASH reference/; 39 | 40 | eval { $tx->render('foo', { baz => { }, lang => 'XXX'} ) }; 41 | like $@, qr/nil/; 42 | 43 | done_testing; 44 | -------------------------------------------------------------------------------- /t/030_kolon/036_foreach_else.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | 8 | my %vpath = ( 9 | ); 10 | 11 | my $tx = Text::Xslate->new( 12 | cache => 0, 13 | path => \%vpath, 14 | verbose => 2, 15 | warn_handler => sub { die @_ }, 16 | ); 17 | 18 | is $tx->render_string(<<'T'), <<'X'; 19 | : for [42] -> $it { 20 | * <:$it:> 21 | : } 22 | : else { 23 | nothing 24 | : } 25 | T 26 | * 42 27 | X 28 | 29 | is $tx->render_string(<<'T'), <<'X'; 30 | : for [] -> $it { 31 | * <:$it:> 32 | : } 33 | : else { 34 | nothing 35 | : } 36 | T 37 | nothing 38 | X 39 | 40 | is $tx->render_string(<<'T'), <<'X'; 41 | : for nil -> $it { 42 | * <:$it:> 43 | : } 44 | : else { 45 | nothing 46 | : } 47 | T 48 | nothing 49 | X 50 | 51 | done_testing; 52 | -------------------------------------------------------------------------------- /t/030_kolon/037_loop_ctl.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | 8 | my %vpath = ( 9 | ); 10 | 11 | my $tx = Text::Xslate->new( 12 | cache => 0, 13 | path => \%vpath, 14 | verbose => 2, 15 | warn_handler => sub { die @_ }, 16 | ); 17 | 18 | note 'for'; 19 | is $tx->render_string(<<'T'), <<'X' or die; 20 | : for [42, 43, 44] -> $it { 21 | : last if $it == 43; 22 | * <:$it:> 23 | : } 24 | : else { 25 | nothing 26 | : } 27 | T 28 | * 42 29 | X 30 | 31 | is $tx->render_string(<<'T'), <<'X' or die; 32 | : for [42, 43, 44] -> $it { 33 | : next if $it == 43; 34 | * <:$it:> 35 | : } 36 | : else { 37 | nothing 38 | : } 39 | T 40 | * 42 41 | * 44 42 | X 43 | 44 | note 'while'; 45 | my $iter = do{ my @a = (42, 43, 44); sub { shift @a } }; 46 | is $tx->render_string(<<'T', { iter => $iter }), <<'X'; 47 | : while $iter() -> $it { 48 | : next if $it == 43; 49 | * <:$it:> 50 | : } 51 | T 52 | * 42 53 | * 44 54 | X 55 | 56 | $iter = do{ my @a = (42, 43, 44); sub { shift @a } }; 57 | is $tx->render_string(<<'T', { iter => $iter }), <<'X'; 58 | : while $iter() -> $it { 59 | : last if $it == 43; 60 | * <:$it:> 61 | : } 62 | T 63 | * 42 64 | X 65 | 66 | $iter = do{ my @a = (42, 43, 44); sub { shift @a } }; 67 | is $tx->render_string(<<'T', { iter => $iter }), <<'X'; 68 | : while $iter() -> $it { 69 | * <:$it:> 70 | : last if $it == 43; 71 | : } 72 | T 73 | * 42 74 | * 43 75 | X 76 | 77 | eval { $tx->render_string(': last') }; 78 | like $@, qr/Use of loop control statement \(last\)/; 79 | 80 | eval { $tx->render_string(': next') }; 81 | like $@, qr/Use of loop control statement \(next\)/; 82 | 83 | done_testing; 84 | -------------------------------------------------------------------------------- /t/030_kolon/039_root_vars.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use warnings; 4 | use Test::More; 5 | use Text::Xslate; 6 | 7 | my $tx = Text::Xslate->new(); 8 | 9 | is $tx->render_string('<: __ROOT__["foo"] :>', { foo => 42 }), '42'; 10 | is $tx->render_string('<: __ROOT__.foo :>', { foo => 42 }), '42'; 11 | 12 | is $tx->render_string( 13 | '<: __ROOT__.merge({ foo => "bar"}).foo ~ __ROOT__.foo :>', { foo => 42 }), 14 | 'bar42'; 15 | 16 | done_testing; 17 | 18 | -------------------------------------------------------------------------------- /t/030_kolon/040_external_macro.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | # 3 | # global macros 4 | # 5 | use strict; 6 | use Test::More skip_all => 'external macros are not yet implemented'; 7 | 8 | use Text::Xslate; 9 | 10 | my %vpath = ( 11 | 'macro/common.tx' => <<'T', 12 | : macro hello -> { 13 | Hello, world! 14 | : } 15 | T 16 | 17 | 'foo.tx' => <<'T', 18 | : import macro::common; 19 | : hello() 20 | T 21 | ); 22 | 23 | my $tx = Text::Xslate->new( 24 | path => \%vpath, 25 | ); 26 | 27 | is $tx->render('foo.tx'), "Hello, world!\n"; 28 | 29 | done_testing; 30 | 31 | -------------------------------------------------------------------------------- /t/040_tterse/001_parse.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate::Syntax::TTerse; 6 | use Text::Xslate::Util qw(p); 7 | 8 | my $parser = Text::Xslate::Syntax::TTerse->new(); 9 | 10 | my @data = ( 11 | ['Hello, world!' => qr/Hello, world!/], 12 | ['Hello, [% lang %] world!' => qr/Hello/, qr/\b lang \b/xms, qr/world/], 13 | ['Hello, [% foo %] world!' => qr/\b foo \b/xms], 14 | ['Hello, [% lang %] [% foo %] world!', qr/\b lang \b/xms, qr/\b foo \b/xms], 15 | 16 | [<<'T', qr/\b foo \b/xms, qr/\b if \b/xms], 17 | [% IF foo %] 18 | This is true 19 | [% END %] 20 | T 21 | [<<'T', qr/\b foo \b/xms, qr/\b if \b/xms], 22 | [% IF foo %] 23 | This is true 24 | [% ELSE %] 25 | This is false 26 | [% END %] 27 | T 28 | 29 | [<<'T', qr/\b item \b/xms, qr/\b foo \b/xms, qr/\b for \b/xms], 30 | [% FOREACH item IN foo %] 31 | This is true 32 | [% END %] 33 | T 34 | 35 | ); 36 | 37 | foreach my $d(@data) { 38 | my($str, @patterns) = @{$d}; 39 | 40 | #note($str); 41 | my $code = p($parser->parse($str)); 42 | #note($code); 43 | 44 | foreach my $pat(@patterns) { 45 | like $code, $pat; 46 | } 47 | } 48 | 49 | done_testing; 50 | -------------------------------------------------------------------------------- /t/040_tterse/002_interpolate.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use lib "t/lib"; 6 | use TTSimple; 7 | 8 | my @data = ( 9 | ['Hello, world!' => 'Hello, world!'], 10 | ['Hello, [% lang %] world!' => 'Hello, Xslate world!'], 11 | ['Hello, [% foo %] world!' => 'Hello, <bar> world!'], 12 | ['Hello, [% lang %] [% foo %] world!' 13 | => 'Hello, Xslate <bar> world!'], 14 | 15 | ['Hello, [% $lang %] world!' => 'Hello, Xslate world!'], 16 | ['Hello, [% ${lang} %] world!' => 'Hello, Xslate world!'], 17 | ['Hello, [% ${ lang } %] world!' => 'Hello, Xslate world!'], 18 | 19 | ['Hello, [% $no_such_field %] world!' => 'Hello, world!', 'nil as empty'], 20 | ['Hello, [% $no_such_field or "Default" %] world!' => 'Hello, Default world!', 'empty or default'], 21 | 22 | ['[% $IF %]', 'This is IF' ], 23 | ['[% +IF %]', 'This is IF' ], 24 | 25 | ['[% GET lang %]', 'Xslate'], 26 | ['[% get lang %]', 'Xslate'], 27 | ['[% get IF %]', 'This is IF'], 28 | ); 29 | 30 | my %vars = ( 31 | lang => 'Xslate', 32 | foo => "", 33 | '$lang' => 'XXX', 34 | IF => 'This is IF', 35 | ); 36 | 37 | foreach my $pair(@data) { 38 | my($in, $out, $msg) = @$pair; 39 | 40 | is render_str($in, \%vars), $out, $msg 41 | or diag $in; 42 | } 43 | 44 | done_testing; 45 | -------------------------------------------------------------------------------- /t/040_tterse/006_include.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use lib "t/lib"; 7 | use TTSimple; 8 | 9 | is render_file('include.tt', { lang => "Xslate" }), 10 | "include:\n" . "Hello, Xslate world!\n"; 11 | 12 | is render_file('include2.tt', { file => "hello.tt", lang => "Xslate" }), 13 | "include2:\n" . "Hello, Xslate world!\n"; 14 | 15 | is render_file('include2.tt', { file => "include.tt", lang => "Xslate" }), 16 | "include2:\n" . "include:\n" . "Hello, Xslate world!\n"; 17 | 18 | is render_str(<<'T', { lang => "Xslate" }), <<'X'; 19 | [% INCLUDE "hello.tt" WITH lang = "TTerse" -%] 20 | Hello, [% lang %] world! 21 | T 22 | Hello, TTerse world! 23 | Hello, Xslate world! 24 | X 25 | 26 | is render_str(<<'T', { lang => "Xslate" }), <<'X'; 27 | [% INCLUDE "hello.tt" WITH 28 | pi = 3.14 29 | lang = "TTerse" -%] 30 | Hello, [% lang %] world! 31 | T 32 | Hello, TTerse world! 33 | Hello, Xslate world! 34 | X 35 | 36 | is render_str(<<'T', { lang => "Xslate" }), <<'X'; 37 | [% INCLUDE "hello.tt" WITH pi = 3.14, lang = "TTerse" -%] 38 | Hello, [% lang %] world! 39 | T 40 | Hello, TTerse world! 41 | Hello, Xslate world! 42 | X 43 | 44 | is render_str(<<'T', { lang => "Xslate" }), <<'X', 'lower-cased'; 45 | [% include "hello.tt" with pi = 3.14, lang = "TTerse" -%] 46 | Hello, [% lang %] world! 47 | T 48 | Hello, TTerse world! 49 | Hello, Xslate world! 50 | X 51 | 52 | #is render_str(<<'T', { lang => "Xslate" }), <<'X', 'WITH is optional'; 53 | #[% INCLUDE "hello.tt" pi = 3.14, lang = "TTerse" -%] 54 | #Hello, [% lang %] world! 55 | #T 56 | #Hello, TTerse world! 57 | #Hello, Xslate world! 58 | #X 59 | 60 | done_testing; 61 | -------------------------------------------------------------------------------- /t/040_tterse/008_methods.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | 7 | my $tx = Text::Xslate->new( 8 | syntax => 'TTerse', 9 | cache => 0, 10 | ); 11 | 12 | { 13 | package Obj; 14 | use Mouse; 15 | 16 | sub join :method { 17 | my($self, $sep, @args) = @_; 18 | return join $sep, @args; 19 | } 20 | 21 | sub ok { 42 } 22 | 23 | sub switch { 'switch' } 24 | sub CASE { 'CASE' } 25 | } 26 | 27 | my @data = ( 28 | [ <<'T', <<'X', "method call without args" ], 29 | [% obj.ok() %] 30 | T 31 | 42 32 | X 33 | 34 | [ <<'T', <<'X', "method call" ], 35 | [% obj.join(".") %] 36 | T 37 | 38 | X 39 | 40 | [ <<'T', <<'X', "method call" ], 41 | [% obj.join(".", "foo", "bar") %] 42 | T 43 | foo.bar 44 | X 45 | 46 | [ <<'T', <<'X', "method call" ], 47 | [% obj.join(".", "foo", "bar", "baz") %] 48 | T 49 | foo.bar.baz 50 | X 51 | 52 | [ <<'T', <<'X', "keywords as fields/methods" ], 53 | [% obj.switch %] 54 | [% obj.switch() %] 55 | [% obj.CASE %] 56 | [% obj.CASE() %] 57 | T 58 | switch 59 | switch 60 | CASE 61 | CASE 62 | X 63 | 64 | ); 65 | 66 | foreach my $d(@data) { 67 | my($in, $out, $msg) = @$d; 68 | 69 | my %vars = ( 70 | obj => Obj->new, 71 | ); 72 | is $tx->render_string($in, \%vars), $out, $msg or diag $in; 73 | } 74 | 75 | done_testing; 76 | -------------------------------------------------------------------------------- /t/040_tterse/009_comments.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use lib "t/lib"; 6 | use TTSimple; 7 | 8 | my @data = ( 9 | [ <<'T', <<'X', ], 10 | A[%# foo %]B 11 | T 12 | AB 13 | X 14 | 15 | [ <<'T', <<'X', ], 16 | A[%# 17 | foo 18 | %]B 19 | T 20 | AB 21 | X 22 | 23 | [ <<'T', <<'X', ], 24 | A[%# 25 | [foo] 26 | [bar] 27 | [baz] 28 | %]B 29 | T 30 | AB 31 | X 32 | ); 33 | 34 | foreach my $d(@data) { 35 | my($in, $out, $msg) = @$d; 36 | 37 | my %vars = (); 38 | is render_str($in, \%vars), $out, $msg or diag $in; 39 | } 40 | 41 | done_testing; 42 | -------------------------------------------------------------------------------- /t/040_tterse/010_expr.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use lib "t/lib"; 6 | use TTSimple; 7 | 8 | my @data = ( 9 | ['Hello, [% lang or "Perl" %] world!' => 'Hello, Xslate world!'], 10 | ['Hello, [% empty or "Perl" %] world!' => 'Hello, Perl world!'], 11 | 12 | ['Hello, [% "foo" _ "bar" %] world!' => 'Hello, foobar world!'], 13 | 14 | ['[% ( NOT value == 10 ) ? "true" : "false" %]', "false" ], 15 | ['[% (NOT value AND (value == 10)) ? "true" : "false" %]', "false" ], 16 | ['[% (NOT( value == 10 )) ? "true" : "false" %]', "false", ], 17 | 18 | ['[% (value == 10 AND value == 10) ? "true" : "false" %]', "true" ], 19 | ['[% (value == 10) AND (value == 10) ? "true" : "false" %]', "true" ], 20 | 21 | ['[% (value == 10 AND value == 10 OR value == 10) ? "true" : "false" %]', "true" ], 22 | ['[% ((value == 10) AND (value == 10) OR (value == 10)) ? "true" : "false" %]', "true" ], 23 | ['[% (value == 10 AND value == 10 OR value == 11) ? "true" : "false" %]', "true" ], 24 | ['[% (((value == 10) AND (value == 10)) OR (value == 11)) ? "true" : "false" %]', "true" ], 25 | 26 | # TTerse specific features 27 | ['[% 0x110 +& 0x101 %]', 0x100, undef, 1 ], 28 | ); 29 | 30 | my %vars = ( 31 | lang => 'Xslate', 32 | foo => "", 33 | '$lang' => 'XXX', 34 | value => 10, 35 | ); 36 | foreach my $d(@data) { 37 | my($in, $out, $msg, $is_tterse_specific) = @$d; 38 | 39 | last if $is_tterse_specific; 40 | is render_str($in, \%vars), $out, $msg 41 | or diag $in; 42 | } 43 | 44 | done_testing; 45 | -------------------------------------------------------------------------------- /t/040_tterse/011_objectliterals.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use lib "t/lib"; 6 | use TTSimple; 7 | 8 | my @data = ( 9 | [<<'T', <<'X'], 10 | %% FOR i IN [1, 2, 3] 11 | [% i %] 12 | %% END 13 | T 14 | 1 15 | 2 16 | 3 17 | X 18 | 19 | [<<'T', <<'X'], 20 | [% { a => 1, b => 2, c => 3 }.a %] 21 | [% { a => 1, b => 2, c => 3 }.b %] 22 | [% { a => 1, b => 2, c => 3 }.c %] 23 | T 24 | 1 25 | 2 26 | 3 27 | X 28 | 29 | [<<'T', <<'X', 'with literals'], 30 | [% { "if" => 42 }.if %] 31 | [% { "not" => 43 }.not %] 32 | [% { "for" => 44 }.for %] 33 | [% { "FOR" => 45 }.FOR %] 34 | T 35 | 42 36 | 43 37 | 44 38 | 45 39 | X 40 | 41 | 42 | [<<'T', <<'X', 'with keywords'], 43 | [% { if => 42 }.if %] 44 | [% { not => 43 }.not %] 45 | [% { for => 44 }.for %] 46 | [% { FOR => 45 }.FOR %] 47 | T 48 | 42 49 | 43 50 | 44 51 | 45 52 | X 53 | 54 | ); 55 | 56 | foreach my $pair(@data) { 57 | my($in, $out, $msg) = @$pair; 58 | 59 | my %vars = (lang => 'Xslate', foo => "", '$lang' => 'XXX'); 60 | 61 | is render_str($in, \%vars), $out, $msg 62 | or diag $in; 63 | } 64 | 65 | done_testing; 66 | -------------------------------------------------------------------------------- /t/040_tterse/012_macro.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use lib "t/lib"; 6 | use TTSimple; 7 | 8 | my @data = ( 9 | [<<'T', <<'X'], 10 | [% MACRO foo BLOCK -%] 11 | Hello, [% lang %] world! 12 | [% END -%] 13 | [% foo() -%] 14 | T 15 | Hello, Xslate world! 16 | X 17 | 18 | [<<'T', <<'X'], 19 | [% MACRO foo(lang) BLOCK -%] 20 | foo [% lang %] bar 21 | [% END -%] 22 | [%- foo(42) -%] 23 | Hello, [% lang %] world! 24 | T 25 | foo 42 bar 26 | Hello, Xslate world! 27 | X 28 | 29 | [<<'T', <<'X'], 30 | [% MACRO add(a, b) BLOCK -%] 31 | [% a + b %] 32 | [% END -%] 33 | [%- add(10, 32) -%] 34 | T 35 | 42 36 | X 37 | 38 | ); 39 | 40 | foreach my $pair(@data) { 41 | my($in, $out, $msg) = @$pair; 42 | 43 | my %vars = (lang => 'Xslate', foo => "", '$lang' => 'XXX'); 44 | 45 | is render_str($in, \%vars), $out, $msg 46 | or diag $in; 47 | } 48 | 49 | done_testing; 50 | -------------------------------------------------------------------------------- /t/040_tterse/016_call.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use lib "t/lib"; 6 | use TTSimple; 7 | use Text::Xslate::Util qw(p); 8 | 9 | my @data = ( 10 | [<<'T', <<'X'], 11 | foo 12 | [% CALL lang -%] 13 | [% CALL foo.bar -%] 14 | bar 15 | T 16 | foo 17 | bar 18 | X 19 | 20 | [<<'T', <<'X', 'lower cased'], 21 | foo 22 | [% call lang -%] 23 | [% call foo.bar -%] 24 | bar 25 | T 26 | foo 27 | bar 28 | X 29 | 30 | ); 31 | 32 | my %vars = (lang => 'Xslate', foo => { bar => 43 }); 33 | 34 | foreach my $d(@data) { 35 | my($in, $out, $msg) = @$d; 36 | 37 | is render_str($in, \%vars), $out, $msg 38 | or diag $in; 39 | } 40 | 41 | done_testing; 42 | -------------------------------------------------------------------------------- /t/040_tterse/020_chomp.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use lib "t/lib"; 6 | use TTSimple; 7 | 8 | my @data = ( 9 | [<<'T', 'Xslate' ], 10 | [%- lang -%] 11 | T 12 | 13 | [<<'T', <<'X' ], 14 | - [%- lang -%] - 15 | T 16 | - Xslate - 17 | X 18 | 19 | [<<'T', <<'X' ], 20 | * 21 | [%- lang -%] 22 | * 23 | T 24 | *Xslate * 25 | X 26 | 27 | [<<'T', <<'X' ], 28 | * 29 | 30 | [%- lang -%] 31 | 32 | * 33 | T 34 | * 35 | Xslate 36 | * 37 | X 38 | 39 | [<<'T', <<'X' ], 40 | 41 | [%- lang -%] 42 | 43 | T 44 | Xslate 45 | X 46 | 47 | [<<'T', <<'X' ], 48 | 49 | [%- lang -%] 50 | 51 | T 52 | Xslate 53 | X 54 | 55 | [<<'T', <<'X' ], 56 | 57 | [%- lang -%] 58 | 59 | T 60 | Xslate 61 | X 62 | 63 | [<<'T', <<'X' ], 64 | 65 | [%- lang -%] 66 | 67 | T 68 | Xslate 69 | X 70 | 71 | 72 | # http://github.com/gfx/p5-Text-Xslate/issues#issue/12 73 | #['Hello, [%~ "Xslate" ~%] world!', 'Hello,Xslateworld!'], 74 | ); 75 | 76 | my %vars = ( 77 | lang => 'Xslate', 78 | void => '', 79 | 80 | value => 10, 81 | ); 82 | foreach my $d(@data) { 83 | my($in, $out, $msg) = @$d; 84 | is render_str($in, \%vars), $out, $msg or diag($in); 85 | } 86 | 87 | done_testing; 88 | -------------------------------------------------------------------------------- /t/040_tterse/021_fake_use.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::Requires qw(Template::Plugin::Math); 4 | use Test::More; 5 | 6 | use lib "t/lib"; 7 | use TTSimple; 8 | 9 | use Template::Plugin::String; 10 | 11 | # XXX: TTerse does not support plugins (i.e. USE directive), but grokes 12 | # the USE keyword as an alias to 'CALL', which takes expressions. 13 | 14 | my @data = ( 15 | [<<'T', <<'X'], 16 | [% USE Math -%] 17 | [% Math.abs(-100) %] 18 | [% Math.abs( 100) %] 19 | T 20 | 100 21 | 100 22 | X 23 | 24 | [<<'T', <<'X'], 25 | [% USE String -%] 26 | [% s = String.new("foo") -%] 27 | [% s.upper %] 28 | [% s.repeat(2) %] 29 | T 30 | FOO 31 | FOOFOO 32 | X 33 | 34 | ); 35 | 36 | my %vars = ( 37 | lang => 'Xslate', 38 | void => '', 39 | 40 | value => 10, 41 | 42 | Math => Template::Plugin::Math->new(), # as a namespace 43 | String => Template::Plugin::String->new(), # as a prototype 44 | ); 45 | foreach my $d(@data) { 46 | my($in, $out, $msg) = @$d; 47 | is render_str($in, \%vars), $out, $msg or diag($in); 48 | } 49 | 50 | done_testing; 51 | -------------------------------------------------------------------------------- /t/040_tterse/022_post_if.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use lib "t/lib"; 6 | use TTSimple; 7 | 8 | my @data = ( 9 | [<<'T', <<'X', 'post if'],, 10 | {[% "foo" IF 1 %]} 11 | {[% "bar" IF 1 %]} 12 | T 13 | {foo} 14 | {bar} 15 | X 16 | 17 | [<<'T', <<'X'], 18 | {[% "foo" IF 0 %]} 19 | {[% "bar" IF 0 %]} 20 | T 21 | {} 22 | {} 23 | X 24 | 25 | [<<'T', <<'X', 'post unless'], 26 | {[% "foo" UNLESS 0 %]} 27 | {[% "bar" UNLESS 0 %]} 28 | T 29 | {foo} 30 | {bar} 31 | X 32 | 33 | [<<'T', <<'X'], 34 | {[% "foo" UNLESS 1 %]} 35 | {[% "bar" UNLESS 1 %]} 36 | T 37 | {} 38 | {} 39 | X 40 | 41 | 42 | [<<'T', <<'X', 'include if'], 43 | [% INCLUDE "hello.tt" IF 1 -%] 44 | [% INCLUDE "hello.tt" IF 1 -%] 45 | T 46 | Hello, Xslate world! 47 | Hello, Xslate world! 48 | X 49 | 50 | [<<'T', <<'X'], 51 | [% INCLUDE "hello.tt" IF 0 -%] 52 | [% INCLUDE "hello.tt" IF 0 -%] 53 | T 54 | X 55 | ); 56 | 57 | my %vars = ( 58 | lang => 'Xslate', 59 | value => 10, 60 | ); 61 | foreach my $d(@data) { 62 | my($in, $out, $msg) = @$d; 63 | is render_str($in, \%vars), $out, $msg or diag($in); 64 | } 65 | 66 | done_testing; 67 | -------------------------------------------------------------------------------- /t/040_tterse/023_loop_ctl.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | 8 | my %vpath = ( 9 | ); 10 | 11 | my $tx = Text::Xslate->new( 12 | syntax => 'TTerse', 13 | cache => 0, 14 | path => \%vpath, 15 | verbose => 2, 16 | warn_handler => sub { die @_ }, 17 | ); 18 | 19 | note 'for'; 20 | is $tx->render_string(<<'T'), <<'X' or die; 21 | %% for it in [42, 43, 44] 22 | %% last if it == 43 23 | * [% it %] 24 | %% end 25 | T 26 | * 42 27 | X 28 | 29 | is $tx->render_string(<<'T'), <<'X' or die; 30 | %% for it in [42, 43, 44] 31 | %% NEXT if it == 43 32 | * [% it %] 33 | %% END 34 | T 35 | * 42 36 | * 44 37 | X 38 | 39 | note 'while'; 40 | my $iter = do{ my @a = (42, 43, 44); sub { shift @a } }; 41 | is $tx->render_string(<<'T', { iter => $iter }), <<'X'; 42 | %% while it = iter() 43 | %% NEXT if it == 43 44 | * [% it %] 45 | %% END 46 | T 47 | * 42 48 | * 44 49 | X 50 | 51 | $iter = do{ my @a = (42, 43, 44); sub { shift @a } }; 52 | is $tx->render_string(<<'T', { iter => $iter }), <<'X'; 53 | %% while it = iter() 54 | %% LAST if it == 43 55 | * [% it %] 56 | %% END 57 | T 58 | * 42 59 | X 60 | 61 | $iter = do{ my @a = (42, 43, 44); sub { shift @a } }; 62 | is $tx->render_string(<<'T', { iter => $iter }), <<'X'; 63 | %% while it = iter() 64 | * [% it %] 65 | %% LAST if it == 43 66 | %% END 67 | T 68 | * 42 69 | * 43 70 | X 71 | 72 | done_testing; 73 | -------------------------------------------------------------------------------- /t/040_tterse/024_for_else.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use warnings; 4 | 5 | use Test::More; 6 | use Text::Xslate; 7 | 8 | my $tx = Text::Xslate->new(syntax => 'TTerse'); 9 | 10 | is $tx->render_string(<<'T'), <<'X'; 11 | %% for i in [42] 12 | {[% i %]} 13 | %% else 14 | empty 15 | %% end 16 | T 17 | {42} 18 | X 19 | 20 | is $tx->render_string(<<'T'), <<'X'; 21 | %% for i in [] 22 | {[% i %]} 23 | %% else 24 | empty 25 | %% end 26 | T 27 | empty 28 | X 29 | 30 | is $tx->render_string(<<'T', { y => 'y' }), <<'X'; 31 | %% FOR i IN [] 32 | {[% i %]} 33 | %% ELSE 34 | empt[% $y %] 35 | %% END 36 | T 37 | empty 38 | X 39 | 40 | done_testing; 41 | 42 | -------------------------------------------------------------------------------- /t/040_tterse/025_macro.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use warnings; 4 | 5 | use Test::More; 6 | use Text::Xslate; 7 | 8 | my $tx = Text::Xslate->new(syntax => 'TTerse', macro => ['t/template/macro.tt']); 9 | 10 | is $tx->render_string(<<'T'), <<'X'; 11 | {[% foo() %]} 12 | T 13 | {foo} 14 | X 15 | 16 | done_testing; 17 | 18 | -------------------------------------------------------------------------------- /t/040_tterse/100_not_supported.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use lib "t/lib"; 6 | use TTSimple; 7 | 8 | my @data = ( 9 | [<<'T'], 10 | [% PERL %] 11 | print "Hello, world"\n"; 12 | [% PERL %] 13 | T 14 | 15 | [<<'T'], 16 | [% TRY %] 17 | print "Hello, world"\n"; 18 | [% END %] 19 | T 20 | ); 21 | 22 | my %vars = ( 23 | lang => 'Xslate', 24 | void => '', 25 | 26 | value => 10, 27 | ); 28 | foreach my $d(@data) { 29 | my($in, $out, $msg) = @$d; 30 | eval { render_str($in, \%vars) }; 31 | note $@; 32 | like $@, qr/not supported/; 33 | } 34 | 35 | done_testing; 36 | -------------------------------------------------------------------------------- /t/050_builtins/004_copied.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | use Text::Xslate::Compiler; 8 | 9 | my $tx = Text::Xslate->new( 10 | function => { 11 | 'array::inc' => sub { 12 | my($a, $i) = @_; 13 | $a->[$i]++; 14 | return $a; 15 | }, 16 | } 17 | ); 18 | 19 | my @a = (42); 20 | is $tx->render_string(q{<: $a.merge(3).inc(0).join(',') :>}, { a => \@a}), 21 | '43,3'; 22 | is_deeply \@a, [42]; 23 | 24 | is $tx->render_string(q{<: $a.merge(3).inc(1).join(',') :>}, { a => \@a}), 25 | '42,4'; 26 | 27 | my %h = (foo => 42); 28 | is $tx->render_string(q{<: $h.keys().inc(0).join(',') :>}, { h => \%h}), 29 | do{ my $x = 'foo'; $x++; $x }; 30 | is_deeply \%h, { foo => 42 }; 31 | 32 | is $tx->render_string(q{<: $h.values().inc(0).join(',') :>}, { h => \%h}), 33 | '43'; 34 | is_deeply \%h, { foo => 42 }; 35 | 36 | done_testing; 37 | -------------------------------------------------------------------------------- /t/100_plugin/001_import_from.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate::Util qw(import_from); 6 | 7 | for(1 .. 2) { 8 | my $f = import_from("Scalar::Util" => [qw(blessed looks_like_number)]); 9 | 10 | is_deeply $f, { 11 | blessed => \&Scalar::Util::blessed, 12 | looks_like_number => \&Scalar::Util::looks_like_number, 13 | }; 14 | 15 | $f = import_from( 16 | "Carp", 17 | "Data::Dumper" => [qw(Dumper)], 18 | ); 19 | 20 | is_deeply $f, { 21 | Dumper => \&Data::Dumper::Dumper, 22 | carp => \&Carp::carp, 23 | croak => \&Carp::croak, 24 | confess => \&Carp::confess, 25 | }; 26 | 27 | # for constants 28 | $f = import_from( 29 | "Fcntl" => [qw(:flock)], 30 | ); 31 | 32 | ok exists $f->{LOCK_EX} or diag explain($f); 33 | is $f->{LOCK_EX}->(), Fcntl::LOCK_EX(), 'constant'; 34 | is $f->{LOCK_SH}->(), Fcntl::LOCK_SH(), 'constant'; 35 | } 36 | 37 | 38 | done_testing; 39 | -------------------------------------------------------------------------------- /t/100_plugin/005_html_builder_module.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | 7 | { 8 | package My::HTML::Builder; 9 | use parent qw(Exporter); 10 | our @EXPORT = qw(foo); 11 | sub foo { 12 | return "
"; 13 | 14 | } 15 | $INC{'My/HTML/Builder.pm'} = __FILE__; 16 | } 17 | 18 | my $tx = Text::Xslate->new( 19 | html_builder_module => [ 20 | 'My::HTML::Builder', 21 | ], 22 | ); 23 | 24 | my @set = ( 25 | [ 26 | '<: foo() :>', 27 | { }, 28 | "
", 29 | 'My::HTML::Builder returns a aprt of HTML' 30 | ], 31 | ); 32 | 33 | foreach my $d(@set) { 34 | my($in, $vars, $out, $msg) = @$d; 35 | is $tx->render_string($in, $vars), $out, $msg; 36 | } 37 | 38 | done_testing; 39 | -------------------------------------------------------------------------------- /t/100_plugin/100_error.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | 7 | use warnings FATAL => 'all'; 8 | 9 | eval { 10 | Text::Xslate->new( 11 | module => [ 'Text::Xslate::No::Such::Module' ], 12 | ); 13 | }; 14 | 15 | like $@, qr/Failed to import/; 16 | like $@, qr{Can't locate Text/Xslate/No/Such/Module.pm}; 17 | 18 | eval { 19 | Text::Xslate->new( 20 | module => [ '(^_^)' ], 21 | ); 22 | }; 23 | 24 | like $@, qr/Invalid module name/; 25 | 26 | done_testing; 27 | -------------------------------------------------------------------------------- /t/200_app/001_hello.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More (tests => 8); 3 | use File::Path (); 4 | use FindBin qw($Bin); 5 | 6 | use constant CACHE_DIR => '.xslate/app1'; 7 | sub clean { 8 | File::Path::rmtree( $Bin . "/out" ); 9 | File::Path::rmtree( CACHE_DIR ); 10 | } 11 | clean(); 12 | END{ 13 | clean(); 14 | } 15 | 16 | system $^X, (map { "-I$_" } @INC), "script/xslate", 17 | '--suffix', 'tx=txt', 18 | sprintf('--dest=%s/out', $Bin), 19 | '--cache_dir=' . CACHE_DIR, 20 | '--verbose=1', 21 | '--cache=2', 22 | sprintf('%s/simple/hello.tx', $Bin), 23 | ; 24 | 25 | is $?, 0, "command executed successfully (1)"; 26 | 27 | ok -d CACHE_DIR, 'cache directory created'; 28 | 29 | ok -f sprintf('%s/out/hello.txt', $Bin), 'correct file generated'; 30 | 31 | my $fh; 32 | ok open($fh, '<', sprintf('%s/out/hello.txt', $Bin)), 'file opened'; 33 | 34 | my $content = do { local $/; <$fh> }; 35 | 36 | like $content, qr/Hello, Perl world!/; 37 | 38 | system $^X, (map { "-I$_" } @INC), "script/xslate", 39 | '--suffix', 'tx=txt', 40 | sprintf('--dest=%s/out', $Bin), 41 | '--cache_dir=' . CACHE_DIR, 42 | '--define=lang=Xslate', 43 | '--cache=2', 44 | sprintf('%s/simple/hello.tx', $Bin), 45 | ; 46 | 47 | is $?, 0, "command executed successfully (2)"; 48 | 49 | ok open($fh, '<', sprintf('%s/out/hello.txt', $Bin)), 'file opened'; 50 | 51 | $content = do { local $/; <$fh> }; 52 | 53 | like $content, qr/Hello, Xslate world!/; 54 | -------------------------------------------------------------------------------- /t/200_app/002_tree.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More (tests => 8); 3 | use File::Path (); 4 | use FindBin qw($Bin); 5 | 6 | sub clean { 7 | File::Path::rmtree( ".app_cache2" ); 8 | File::Path::rmtree( ".tree_out" ); 9 | } 10 | 11 | clean(); 12 | END{ 13 | clean(); 14 | } 15 | 16 | system $^X, (map { "-I$_" } @INC), "script/xslate", 17 | '--suffix', 'tx=txt', 18 | '--cache_dir=.xslate_cache/app2', 19 | '--dest=.tree_out', 20 | '--ignore=dont_touch', 21 | "$Bin/simple", 22 | ; 23 | 24 | if (is $?, 0, "command executed successfully") { 25 | { 26 | ok -f '.tree_out/hello.txt', 'correct file generated'; 27 | my $fh; 28 | ok open($fh, '<', '.tree_out/hello.txt'), 'file opened'; 29 | 30 | my $content = do { local $/; <$fh> }; 31 | like $content, qr/Hello, Perl world!/; 32 | } 33 | 34 | { 35 | ok -f '.tree_out/goodbye.txt', 'correct file generated'; 36 | my $fh; 37 | ok open($fh, '<', '.tree_out/goodbye.txt'), 'file opened'; 38 | 39 | my $content = do { local $/; <$fh> }; 40 | like $content, qr/Goodbye, Cruel world!/; 41 | } 42 | 43 | { 44 | ok !-f '.tree_out/dont_touch.tx', '--ignore works'; 45 | } 46 | } 47 | 48 | 49 | -------------------------------------------------------------------------------- /t/200_app/004_encoding.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use warnings; 4 | use utf8; 5 | 6 | use Test::More; 7 | use SelectSaver; 8 | use Text::Xslate::Runner; 9 | 10 | use Encode qw(encode decode); 11 | 12 | sub capture(&) { 13 | my($block) = @_; 14 | 15 | my $s = ''; 16 | 17 | { 18 | open my $out, '>', \$s; 19 | my $saver = SelectSaver->new($out); 20 | $block->(); 21 | } 22 | return $s; 23 | } 24 | 25 | my $app = Text::Xslate::Runner->new( 26 | define => { name => '' }, 27 | cache_dir => '.xslate_cache/app3', 28 | ); 29 | is capture { $app->run('t/template/hello_utf8.tx') }, 30 | encode("UTF-8", "こんにちは! <Xslate>!\n"); 31 | 32 | $app->output_encoding('Shift_JIS'); 33 | is capture { $app->run('t/template/hello_utf8.tx') }, 34 | encode("Shift_JIS", "こんにちは! <Xslate>!\n"); 35 | 36 | $app->input_encoding('Shift_JIS'); 37 | $app->output_encoding('utf-8'); 38 | is capture { $app->run('t/template/hello_sjis.tx') }, 39 | encode("UTF-8", "こんにちは! <Xslate>!\n"); 40 | 41 | $app->input_encoding('Shift_JIS'); 42 | $app->output_encoding('Shift_JIS'); 43 | is capture { $app->run('t/template/hello_sjis.tx') }, 44 | encode("Shift_JIS", "こんにちは! <Xslate>!\n"); 45 | 46 | done_testing; 47 | -------------------------------------------------------------------------------- /t/200_app/simple/dont_touch.tx: -------------------------------------------------------------------------------- 1 | Thiis file will be ignored by the --ignore option. 2 | -------------------------------------------------------------------------------- /t/200_app/simple/goodbye.tx: -------------------------------------------------------------------------------- 1 | Goodbye, <: $world_type // "Cruel" :> world! -------------------------------------------------------------------------------- /t/200_app/simple/hello.tx: -------------------------------------------------------------------------------- 1 | Hello, <: $lang // "Perl" :> world! -------------------------------------------------------------------------------- /t/300_examples/001_basic.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | 7 | my $tx = Text::Xslate->new(syntax => 'Kolon'); 8 | 9 | my $tmpl; 10 | 11 | $tmpl = <<'TX'; 12 | Hello, <: $dialect :> world! 13 | TX 14 | 15 | is $tx->render_string($tmpl, { dialect => 'Kolon' }), "Hello, Kolon world!\n", "Hello, world"; 16 | 17 | $tmpl = <<'TX'; 18 | : if $var == nil { 19 | $var is nil. 20 | : } 21 | : else if $var != "foo" { 22 | $var is not nil nor "foo". 23 | : } 24 | : else { 25 | $var is "foo". 26 | : } 27 | TX 28 | 29 | is $tx->render_string($tmpl, { var => undef }), " \$var is nil.\n"; 30 | is $tx->render_string($tmpl, { var => 0 }), qq{ \$var is not nil nor "foo".\n}; 31 | is $tx->render_string($tmpl, { var => "foo" }), qq{ \$var is "foo".\n}; 32 | 33 | $tmpl = <<'TX'; 34 | : if( $var >= 1 && $var <= 10 ) { 35 | $var is 1 .. 10 36 | : } 37 | TX 38 | 39 | is $tx->render_string($tmpl, { var => 5 }), " \$var is 1 .. 10\n"; 40 | is $tx->render_string($tmpl, { var => 0 }), ""; 41 | is $tx->render_string($tmpl, { var => 11 }), ""; 42 | 43 | $tmpl = <<'TX'; 44 | := $var.value == nil ? "nil" : $var.value 45 | TX 46 | 47 | is $tx->render_string($tmpl, { var => {} }), "nil"; 48 | is $tx->render_string($tmpl, { var => { value => "" }}), "<foo>"; 49 | 50 | $tmpl = <<'TX'; 51 | : for $data ->($item) { 52 | [<:= $item + 5 :>] 53 | : } # end for 54 | TX 55 | 56 | is $tx->render_string($tmpl, { data => [1 .. 100] }), 57 | join('', map{ sprintf "[%d]\n", $_ + 5 } 1 .. 100); 58 | 59 | 60 | done_testing; 61 | -------------------------------------------------------------------------------- /t/300_examples/002_cascade.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | use lib "t/lib"; 7 | use Util; 8 | 9 | { 10 | package BlogEntry; 11 | use Mouse; 12 | has title => (is => 'rw'); 13 | has body => (is => 'rw'); 14 | } 15 | 16 | my @blog_entries = map{ BlogEntry->new($_) } ( 17 | { 18 | title => 'Entry one', 19 | body => 'This is my first entry.', 20 | }, 21 | { 22 | title => 'Entry two', 23 | body => 'This is my second entry.', 24 | }, 25 | ); 26 | 27 | my $tx = Text::Xslate->new( 28 | cache => 0, 29 | path => [path], 30 | ); 31 | 32 | my $gold = <<'T'; 33 | 35 | 36 | 37 | 38 | My amazing blog 39 | 40 | 41 | 42 | 48 | 49 |
50 |

Entry one

51 |

This is my first entry.

52 |

Entry two

53 |

This is my second entry.

54 |
55 | 56 | 57 | T 58 | 59 | is $tx->render('eg/child.tx', { blog_entries => \@blog_entries }), $gold, 'example/cascade.pl' 60 | for 1 .. 2; 61 | 62 | 63 | done_testing; 64 | -------------------------------------------------------------------------------- /t/300_examples/003_metakolon.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | 7 | my $tx = Text::Xslate->new(syntax => 'Metakolon'); 8 | 9 | my $tmpl; 10 | 11 | $tmpl = <<'TX'; 12 | Hello, [% $dialect %] world! 13 | TX 14 | 15 | is $tx->render_string($tmpl, { dialect => 'Kolon' }), "Hello, Kolon world!\n", "Hello, world"; 16 | 17 | $tmpl = <<'TX'; 18 | %% if $var == nil { 19 | $var is nil. 20 | %% } 21 | %% else if $var != "foo" { 22 | $var is not nil nor "foo". 23 | %% } 24 | %% else { 25 | $var is "foo". 26 | %% } 27 | TX 28 | 29 | is $tx->render_string($tmpl, { var => undef }), " \$var is nil.\n"; 30 | is $tx->render_string($tmpl, { var => 0 }), qq{ \$var is not nil nor "foo".\n}; 31 | is $tx->render_string($tmpl, { var => "foo" }), qq{ \$var is "foo".\n}; 32 | 33 | $tmpl = <<'TX'; 34 | %% if( $var >= 1 && $var <= 10 ) { 35 | $var is 1 .. 10 36 | %% } 37 | TX 38 | 39 | is $tx->render_string($tmpl, { var => 5 }), " \$var is 1 .. 10\n"; 40 | is $tx->render_string($tmpl, { var => 0 }), ""; 41 | is $tx->render_string($tmpl, { var => 11 }), ""; 42 | 43 | $tmpl = <<'TX'; 44 | %%= $var.value == nil ? "nil" : $var.value 45 | TX 46 | 47 | is $tx->render_string($tmpl, { var => {} }), "nil"; 48 | is $tx->render_string($tmpl, { var => { value => "" }}), "<foo>"; 49 | 50 | $tmpl = <<'TX'; 51 | %% for $data ->($item) { 52 | [[%= $item + 5 %]] 53 | %% } # end for 54 | TX 55 | 56 | is $tx->render_string($tmpl, { data => [1 .. 100] }), 57 | join('', map{ sprintf "[%d]\n", $_ + 5 } 1 .. 100); 58 | 59 | 60 | done_testing; 61 | -------------------------------------------------------------------------------- /t/300_examples/004_tterse.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | 7 | my $tx = Text::Xslate->new(syntax => 'TTerse'); 8 | 9 | my $tmpl; 10 | 11 | $tmpl = <<'TX'; 12 | Hello, [% dialect %] world! 13 | TX 14 | 15 | is $tx->render_string($tmpl, { dialect => 'Kolon' }), "Hello, Kolon world!\n", "Hello, world"; 16 | 17 | $tmpl = <<'TX'; 18 | [% IF var == nil -%] 19 | $var is nil. 20 | [% ELSIF var != "foo" -%] 21 | $var is not nil nor "foo". 22 | [% ELSE -%] 23 | $var is "foo". 24 | [% END -%] 25 | TX 26 | 27 | is $tx->render_string($tmpl, { var => undef }), " \$var is nil.\n"; 28 | is $tx->render_string($tmpl, { var => 0 }), qq{ \$var is not nil nor "foo".\n}; 29 | is $tx->render_string($tmpl, { var => "foo" }), qq{ \$var is "foo".\n}; 30 | 31 | $tmpl = <<'TX'; 32 | [% IF var >= 1 && var <= 10 -%] 33 | $var is 1 .. 10 34 | [% END -%] 35 | TX 36 | 37 | is $tx->render_string($tmpl, { var => 5 }), " \$var is 1 .. 10\n"; 38 | is $tx->render_string($tmpl, { var => 0 }), ""; 39 | is $tx->render_string($tmpl, { var => 11 }), ""; 40 | 41 | $tmpl = <<'TX'; 42 | [% var.value == nil ? "nil" : var.value -%] 43 | TX 44 | 45 | is $tx->render_string($tmpl, { var => {} }), "nil"; 46 | is $tx->render_string($tmpl, { var => { value => "" }}), "<foo>"; 47 | 48 | $tmpl = <<'TX'; 49 | [% FOREACH item IN data -%] 50 | [[% item + 5 %]] 51 | [% END -%] 52 | TX 53 | 54 | is $tx->render_string($tmpl, { data => [1 .. 100] }), 55 | join('', map{ sprintf "[%d]\n", $_ + 5 } 1 .. 100); 56 | 57 | 58 | done_testing; 59 | -------------------------------------------------------------------------------- /t/900_bugs/001_super.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use warnings; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | 8 | my %vpath = ( 9 | 'component.tx' => <<'T', 10 | : around body -> { 11 | 12 | 13 | Welcome 14 | 15 | : super 16 | 17 | 18 | : } 19 | T 20 | 21 | 'main.tx' => <<'T', 22 | : cascade with component 23 | 24 | : block body -> { 25 |

<: $message :>

26 | This page was generated from the template 27 | : } 28 | T 29 | ); 30 | 31 | my $tx = Text::Xslate->new(path => \%vpath, cache => 0); 32 | my $out = $tx->render('main.tx', { message => 'OK'}); 33 | like $out, qr//; 34 | like $out, qr/

/; 35 | like $out, qr/OK/; 36 | 37 | done_testing; 38 | 39 | -------------------------------------------------------------------------------- /t/900_bugs/002_addfunc.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use warnings; 4 | use Test::More; 5 | 6 | use File::Path qw(rmtree); 7 | use constant CACHE => '.900-02'; 8 | use Text::Xslate; 9 | 10 | my %vpath = ( 11 | 'foo.tx' => <<'T', 12 | %% foo() 13 | T 14 | ); 15 | 16 | BEGIN { 17 | rmtree(CACHE); 18 | mkdir(CACHE); 19 | } 20 | END { rmtree(CACHE) } 21 | 22 | { 23 | my $tx = Text::Xslate->new( 24 | syntax => 'TTerse', 25 | path => \%vpath, 26 | cache_dir => CACHE, 27 | verbose => 0, 28 | ); 29 | is $tx->render('foo.tx'), ''; 30 | } 31 | { 32 | my $tx = Text::Xslate->new( 33 | syntax => 'TTerse', 34 | path => \%vpath, 35 | cache_dir => CACHE, 36 | verbose => 0, 37 | function => { foo => sub { 'OK' } }, 38 | ); 39 | is $tx->render('foo.tx'), 'OK'; 40 | } 41 | 42 | done_testing; 43 | 44 | -------------------------------------------------------------------------------- /t/900_bugs/003_frame_access.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | # recursive INCLUDE broke the stack frame 3 | use strict; 4 | use warnings; 5 | use Test::More; 6 | 7 | use Text::Xslate; 8 | 9 | my %vpath = ( 10 | main => <<'T', 11 | [% FOR entry IN entries -%] 12 | + [% entry.name %] 13 | [% INCLUDE 'entry' WITH entry = entry -%] 14 | [% END -%] 15 | T 16 | 17 | entry => <<'T', 18 | [% FOR child IN entry.next -%] 19 | - [% child.name %] 20 | [% INCLUDE 'entry' WITH entry = child ; -%] 21 | [% END -%] 22 | T 23 | ); 24 | 25 | my @entries = ( 26 | { 27 | name => 'hoge', 28 | next => [ 29 | { name => 'fuga' }, 30 | { name => 'foobar' } 31 | ] 32 | } 33 | ); 34 | 35 | my $tx = Text::Xslate->new( syntax => 'TTerse', cache => 0, path => \%vpath ); 36 | 37 | ok $tx->render('entry', { entry => $entries[0] }); 38 | 39 | my $s = $tx->render('main', { entries => \@entries }); 40 | note $s; 41 | ok $s; 42 | like $s, qr/hoge/; 43 | like $s, qr/fuga/; 44 | like $s, qr/foobar/; 45 | 46 | done_testing; 47 | 48 | -------------------------------------------------------------------------------- /t/900_bugs/004_errorhandling.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Text::Xslate; 3 | use Test::More; 4 | 5 | my $view = Text::Xslate->new( 6 | verbose => 1, 7 | syntax => 'TTerse', 8 | function => { f => sub { die } }, 9 | ); 10 | 11 | close STDERR; 12 | open STDERR, '>', \my $stderr; 13 | 14 | { 15 | 16 | package MyMap; 17 | 18 | sub wrapper { 19 | my ( $self, $res ) = @_; 20 | return $res; 21 | } 22 | 23 | sub call { 24 | my ( $self, $env ) = @_; 25 | 26 | my $app = sub { 27 | my $html = $view->render_string( <<'...' ); 28 | [% f() -%] 29 | [% f() -%] 30 | [% f() -%] 31 | [% f() -%] 32 | [% f() -%] 33 | ... 34 | 35 | return [ 200, [], [$html] ]; 36 | }; 37 | return $self->wrapper( 38 | $app->($env) 39 | ); 40 | } 41 | } 42 | 43 | my $res = MyMap->call( {} ); 44 | is ref($res), 'ARRAY'; 45 | note $stderr; 46 | done_testing; 47 | 48 | -------------------------------------------------------------------------------- /t/900_bugs/005_rec_include.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | # copied from t/030_kolon/009_include.t 3 | use strict; 4 | #use warnings FATAL => 'all'; 5 | 6 | use Test::More; 7 | 8 | use Text::Xslate; 9 | use lib "t/lib"; 10 | use Util; 11 | 12 | my $tx = Text::Xslate->new( cache => 0, path => [path] ); 13 | 14 | eval { 15 | $tx->render('include2.tx', { file => 'include2.tx', lang => 'Xslate' }); 16 | }; 17 | 18 | like $@, qr/too deep/, 'first'; 19 | note $@; 20 | 21 | eval { 22 | $tx->render('include2.tx', { file => 'include2.tx', lang => 'Xslate' }); 23 | }; 24 | 25 | like $@, qr/too deep/, 'second'; 26 | note $@; 27 | 28 | done_testing; 29 | 30 | -------------------------------------------------------------------------------- /t/900_bugs/006_complex_lex.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | # vim:ft=perl: 3 | # reported by cho45 4 | # modified from https://github.com/gfx/p5-Text-Xslate/issues#issue/27 5 | use strict; 6 | use warnings; 7 | use Test::More; 8 | use Text::Xslate; 9 | use Text::Xslate::Parser; 10 | 11 | my $template = sprintf <<'EOF', qq{ "a",\n} x 5000; 12 | [%% JS = [ 13 | %s 14 | ] %%] 15 | foobar 16 | EOF 17 | 18 | my $XSLATE = Text::Xslate->new( 19 | syntax => 'TTerse', 20 | cache => 0, 21 | ); 22 | 23 | like $XSLATE->render_string($template), qr/foobar/; 24 | 25 | done_testing; 26 | -------------------------------------------------------------------------------- /t/900_bugs/007_uuv.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | #https://github.com/gfx/p5-Text-Xslate/issues/issue/28 3 | use strict; 4 | use warnings; 5 | use utf8; 6 | use Text::Xslate; 7 | use if exists $INC{'Text/Xslate/PP.pm'}, 'Test::More', skip_all => 'NYI in PP'; 8 | use Test::More; 9 | 10 | 11 | my @warnings; 12 | my $xslate = Text::Xslate->new( 13 | syntax => 'TTerse', 14 | warn_handler => sub { push @warnings, @_ }, 15 | verbose => 2, 16 | ); 17 | $xslate->render_string('[% IF others.size() > 0 %][% END %]', {}); 18 | 19 | note @warnings; 20 | like "@warnings", qr/\b nil \b/xms; 21 | like "@warnings", qr/\b lhs \b/xms; 22 | unlike "@warnings", qr/Use of uninitialized value/; 23 | 24 | done_testing; 25 | 26 | -------------------------------------------------------------------------------- /t/900_bugs/008_include_var.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | use Text::Xslate; 5 | 6 | my %vpath = ( 7 | 'foo.tx' => 'foo.tx', 8 | ); 9 | 10 | my $tx = Text::Xslate->new( 11 | function => { f => sub { 'foo.tx' } }, 12 | path => \%vpath, 13 | cache => 0, 14 | ); 15 | 16 | is eval { $tx->render_string(': include f() ') }, 'foo.tx'; 17 | is eval { $tx->render_string(': my $x = f(); include "" ~ $x;') }, 'foo.tx'; 18 | is eval { $tx->render_string(': my $x = f(); include $x; ') }, 'foo.tx'; 19 | 20 | done_testing; 21 | 22 | -------------------------------------------------------------------------------- /t/900_bugs/009_quote_in_comments.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use warnings; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | 8 | note 'Kolon'; 9 | 10 | my $tx = Text::Xslate->new(type => 'text'); 11 | 12 | is $tx->render_string(<<'T'), "\n"; 13 | <: # it's a comment! :> 14 | T 15 | 16 | is $tx->render_string(<<'T'), "# it's a comment!\n"; 17 | <: "# it's a comment!" :> 18 | T 19 | 20 | is $tx->render_string(<<'T'), "# it's a comment!\n"; 21 | <: '# it\'s a comment!' :> 22 | T 23 | 24 | is $tx->render_string(<<'T'), "\n"; 25 | <: '' # it's a comment! :> 26 | T 27 | 28 | is $tx->render_string(<<'T'), "\n"; 29 | <:''# it's a comment! :> 30 | T 31 | 32 | note 'TTerse'; 33 | 34 | $tx = Text::Xslate->new(type => 'text', syntax => 'TTerse'); 35 | 36 | is $tx->render_string(<<'T'), "\n"; 37 | [% # it's a comment! %] 38 | T 39 | 40 | is $tx->render_string(<<'T'), "# it's a comment!\n"; 41 | [% "# it's a comment!" %] 42 | T 43 | 44 | is $tx->render_string(<<'T'), "# it's a comment!\n"; 45 | [% '# it\'s a comment!' %] 46 | T 47 | 48 | is $tx->render_string(<<'T'), "\n"; 49 | [% '' # it's a comment! %] 50 | T 51 | 52 | is $tx->render_string(<<'T'), "\n"; 53 | [%''# it's a comment! %] 54 | T 55 | 56 | is $tx->render_string(<<'T'), "Hello, world!\n"; 57 | [%# it's a comment! 58 | it is also a comment! 59 | -%] 60 | Hello, world! 61 | T 62 | 63 | done_testing; 64 | 65 | -------------------------------------------------------------------------------- /t/900_bugs/010_widechar_for_md5.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | # reported by @kazeuro 3 | # http://twitter.com/#!/kazeburo/status/55456855496994816 4 | use strict; 5 | use warnings; 6 | use utf8; 7 | use Text::Xslate; 8 | use Encode qw(encode_utf8); 9 | 10 | use Test::More tests => 5; 11 | 12 | use lib "t/lib"; 13 | use Util qw(path); 14 | 15 | 16 | eval { 17 | local $SIG{__WARN__} = sub { die @_ }; 18 | my $templates = { 19 | 'index' => encode_utf8(<<'EOF'), 20 | ほげ<: $bar :> 21 | EOF 22 | }; 23 | 24 | my $tx = Text::Xslate->new( 25 | path => [ $templates ], 26 | cache_dir => path, 27 | ); 28 | ok $tx->render('index', { bar => 'ふが' } ), '1-1'; 29 | ok $tx->render('index', { bar => 'ふが' } ), '1-2'; 30 | 31 | $tx = Text::Xslate->new( 32 | path => [ $templates ], 33 | cache_dir => path, 34 | ); 35 | ok $tx->render('index', { bar => 'ふが' } ), '2-1'; 36 | ok $tx->render('index', { bar => 'ふが' } ), '2-2'; 37 | }; 38 | 39 | my $w = $@; 40 | is $w, '' or diag $w; 41 | 42 | done_testing; 43 | 44 | -------------------------------------------------------------------------------- /t/900_bugs/011_reserved_words.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | use Text::Xslate; 5 | 6 | my $tx = Text::Xslate->new( 7 | syntax => 'TTerse', 8 | ); 9 | 10 | is eval { 11 | $tx->render_string(<<'T'); 12 | [% block = "Hello" -%] 13 | [% block %] 14 | T 15 | }, undef; 16 | isnt $@, ''; 17 | like $@, qr/not a lexical variable/; 18 | 19 | done_testing; 20 | 21 | -------------------------------------------------------------------------------- /t/900_bugs/012_ltgt_in_comments.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | use Text::Xslate; 5 | 6 | my $tx = Text::Xslate->new(); 7 | local $TODO = 'not yet fixed'; 8 | ok eval { 9 | $tx->render_string(<<'T', { text => 'foo' }); 10 | <: $text :>!! 11 | :# > 12 | T 13 | }; 14 | is $@, ''; 15 | 16 | done_testing; 17 | 18 | -------------------------------------------------------------------------------- /t/900_bugs/013_comments_lineno.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | use Text::Xslate; 5 | 6 | my $tx = Text::Xslate->new( 7 | syntax => 'TTerse', 8 | ); 9 | 10 | is $tx->render_string(<<'T'), "1\n5\n"; 11 | [% __LINE__ ~ "\n"; 12 | # 2 13 | # 3 14 | # 4 15 | __LINE__ 16 | %] 17 | T 18 | 19 | is $tx->render_string(<<'T'), "1\n5\n"; 20 | %%__LINE__ ~ "\n" 21 | %% # 2 22 | %% # 3 23 | %% # 4 24 | %% __LINE__ ~ "\n" 25 | T 26 | 27 | done_testing; 28 | 29 | -------------------------------------------------------------------------------- /t/900_bugs/014_too_large_int.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | use Text::Xslate; 5 | 6 | my $tx = Text::Xslate->new(); 7 | 8 | is $tx->render_string(': "10" x 100'), 9 | "10" x 100; 10 | 11 | is $tx->render_string(': "1000000000000000000000000000" '), 12 | "1000000000000000000000000000"; 13 | 14 | done_testing; 15 | 16 | -------------------------------------------------------------------------------- /t/900_bugs/015_my_scope.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | 7 | my $tx = Text::Xslate->new(); 8 | 9 | # for strings 10 | my $template = <<'T'; 11 | : my $foo = 'Hello, world!'; 12 | : for [1] -> $i { 13 | : $foo 14 | : } 15 | T 16 | 17 | is $tx->render_string($template), 'Hello, world!'; 18 | done_testing; 19 | 20 | -------------------------------------------------------------------------------- /t/900_bugs/016_x_in_tterse.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | 5 | use Text::Xslate; 6 | 7 | my $tt = Text::Xslate->new( 8 | syntax => 'TTerse', 9 | cache => 0, 10 | ); 11 | 12 | is $tt->render_string(<<'T'), "- 42\n"; 13 | %% foreach x in [42] 14 | - [% x %] 15 | %% end 16 | T 17 | 18 | is $tt->render_string(<<'T'), "- 42\n"; 19 | %% foreach in in [42] 20 | - [% in %] 21 | %% end 22 | T 23 | 24 | done_testing; 25 | 26 | -------------------------------------------------------------------------------- /t/900_bugs/018_lvar_in_macro.pl: -------------------------------------------------------------------------------- 1 | # This code caused SEGV in Perl 5.12 by Xslate 1.5 2 | use strict; 3 | use warnings; 4 | use Test::More; 5 | use Text::Xslate; 6 | 7 | my $engine = Text::Xslate->new( 8 | 'syntax' => 'TTerse', 9 | ); 10 | 11 | $engine->render_string( <<'...') ; 12 | [% kogaidan = kogaidan -%] 13 | [% tomyhero = dankogai %] 14 | 15 | [% MACRO satoshi BLOCK %] 16 | [% tomyhero # caused SEGV %] 17 | [% END %] 18 | 19 | [% satoshi() %] 20 | ... 21 | 22 | pass; 23 | done_testing; 24 | 25 | -------------------------------------------------------------------------------- /t/900_bugs/019_nested_mm.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | # issues/45: nested macro modifiers 3 | use strict; 4 | use warnings; 5 | use utf8; 6 | use Test::More; 7 | use File::Temp qw(tempdir); 8 | 9 | use Text::Xslate; 10 | 11 | my %vpath = ( 12 | X => <<'T', 13 | : block sugyan -> { 14 | This is X. 15 | : } 16 | T 17 | 18 | Y => <<'T', 19 | : cascade "X" 20 | : around sugyan -> { 21 | This is Y. 22 | : } 23 | T 24 | Z => <<'T', 25 | : cascade "Y" 26 | : around sugyan -> { 27 | This is Z. 28 | : } 29 | T 30 | ); 31 | 32 | my $xslate = Text::Xslate->new( 33 | path => [\%vpath], 34 | cache_dir => tempdir(CLEANUP => 1), 35 | ); 36 | is eval { $xslate->render('Z') }, "This is Z.\n"; 37 | is $@, ''; 38 | done_testing; 39 | -------------------------------------------------------------------------------- /t/900_bugs/020_switch_foo.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | # http://twitter.com/#!/ryochin/status/137041211054768128 3 | use strict; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | 8 | my $tx = Text::Xslate->new( 9 | syntax => 'TTerse', 10 | cache => 0, 11 | ); 12 | 13 | eval { 14 | $tx->render_string(<<'T'); 15 | %% if 16 | T 17 | }; 18 | ok $@; 19 | 20 | eval { 21 | $tx->render_string(<<'T'); 22 | %% switch.foo 23 | T 24 | }; 25 | ok $@; 26 | 27 | done_testing; 28 | 29 | -------------------------------------------------------------------------------- /t/900_bugs/022_empty_if_block.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use warnings; 4 | use Test::More; 5 | use Text::Xslate; 6 | 7 | my %vpath = ( 8 | simple_if => <<'T', 9 | %% IF hoge 10 | %% ELSE; 11 | %% IF fuga 12 | %% ELSE 13 | %% END 14 | %% END 15 | hi 16 | T 17 | 18 | declare_if => <<'T', 19 | [% IF hoge %] 20 | [% ELSE %] 21 | [% IF fuga %] 22 | [% ELSE %] 23 | [% END %] 24 | [% SET hoge = fuga %] 25 | [% END %] 26 | hi 27 | T 28 | 29 | problem_case => <<'T', 30 | %% IF hogex 31 | %% ELSE; 32 | %% IF fugax 33 | %% ELSE 34 | %% END 35 | %% SET hogex = fugax; 36 | %% END 37 | hi 38 | T 39 | ); 40 | 41 | my $xslate = Text::Xslate->new( 42 | syntax => 'TTerse', 43 | path => \%vpath, 44 | cache => 0, 45 | ); 46 | 47 | ok $xslate->render('simple_if'), 'simple_if'; 48 | ok $xslate->render('declare_if'), 'declare_if'; 49 | ok $xslate->render('problem_case'), 'problem_case'; 50 | 51 | done_testing; 52 | 53 | -------------------------------------------------------------------------------- /t/900_bugs/023_deploy_problem.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use warnings; 4 | use Fatal qw(open close utime); 5 | use Test::More; 6 | use File::Basename qw(basename); 7 | use File::Temp qw(tempdir); 8 | use File::Copy qw(copy); 9 | use File::stat qw(stat); 10 | use Text::Xslate; 11 | 12 | # problems on deploying 13 | # 14 | # 1. mtime of foo.tx@stage is 12:00 15 | # 2. there's access to the service and then mtime of foo.txc@service gets 12:00 16 | # 3. to deploy foo.tx, mtime of which@service is 12:00 17 | # 4. there's access to the service; mtime of foo.tx and foo.txc is the same 18 | 19 | my $content0 = <<'T'; 20 | Hello, world! 21 | T 22 | 23 | my $content1 = <<'T'; 24 | modified 25 | T 26 | 27 | { 28 | my $service = tempdir(CLEANUP => 1); 29 | 30 | my $tx = Text::Xslate->new( 31 | cache_dir => "$service/cache", 32 | path => [$service], 33 | ); 34 | write_file("$service/foo.tx", $content0); 35 | 36 | sleep 1; # time goes 37 | 38 | is $tx->render("foo.tx"), $content0; 39 | 40 | write_file("$service/foo.tx", $content1); 41 | 42 | is $tx->render("foo.tx"), $content1; 43 | } 44 | 45 | done_testing; 46 | exit; 47 | 48 | sub write_file { 49 | my($file, $content) = @_; 50 | 51 | open my($fh), ">", $file; 52 | print $fh $content; 53 | eval { 54 | $fh->flush; 55 | $fh->sync; 56 | }; 57 | close $fh; 58 | return; 59 | } 60 | 61 | -------------------------------------------------------------------------------- /t/900_bugs/025_clobber-macro-args.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | # https://gist.github.com/3499604 3 | # reported by ktat 4 | # modified by gfx 5 | 6 | use strict; 7 | use warnings; 8 | 9 | use Test::More; 10 | 11 | use Text::Xslate; 12 | 13 | my %vpath; 14 | $vpath{"test.tt"} = <<'_TMPL_'; 15 | [%- MACRO hoge1 (aaa) BLOCK -%] 16 | [%- END -%] 17 | [%- SET foo = 42 -%] 18 | [%- MACRO hoge2 (bbb) BLOCK -%] 19 | Calling this macro clobbered "foo". 20 | [%- END -%] 21 | [%- hoge2("a") -%] 22 | [% hoge1("b") %] 23 | 24 | [% foo %] should be 42. 25 | _TMPL_ 26 | 27 | my $t = Text::Xslate->new( 28 | syntax => 'TTerse', 29 | cache => 0, 30 | path => \%vpath 31 | ); 32 | 33 | my $text = $t->render("test.tt"); 34 | note $text; 35 | like $text, qr/^42 \s+ should \s+ be \s+ 42\.$/xms; 36 | 37 | done_testing; 38 | 39 | -------------------------------------------------------------------------------- /t/900_bugs/026_issue61.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | # https://github.com/xslate/p5-Text-Xslate/issues/61 3 | 4 | use strict; 5 | use warnings; 6 | use Fatal qw(open close); 7 | use Test::More; 8 | use File::Temp qw(tempdir); 9 | use File::Spec (); 10 | use Text::Xslate; 11 | 12 | # problems on depend cache 13 | # 14 | # 1. main.tx cascade to base.tx 15 | # 2. mtime of main.tx is 12:00 16 | # 3. mtime of base.tx is 12:01(newer than main.tx) 17 | # 4. render main.tx 18 | # 5. render main.tx again, but don't use cache 19 | 20 | { 21 | package MyXslate; 22 | use parent qw(Text::Xslate); 23 | 24 | sub _load_source { 25 | my ($self, $fi) = @_; 26 | my $fullpath = $fi->{fullpath}; 27 | 28 | $self->{_load_source_count}{$fullpath}++; 29 | 30 | $self->SUPER::_load_source($fi); 31 | } 32 | } 33 | 34 | 35 | my $content_main = <<'T'; 36 | : cascade base 37 | T 38 | 39 | my $content_base = <<'T'; 40 | I am base 41 | T 42 | 43 | { 44 | my $service = tempdir(CLEANUP => 1); 45 | 46 | my $tx = MyXslate->new( 47 | cache_dir => "$service/cache", 48 | path => [$service], 49 | ); 50 | write_file("$service/main.tx", $content_main); 51 | sleep 2; # time goes 52 | write_file("$service/base.tx", $content_base); 53 | 54 | is $tx->render("main.tx"), $content_base; 55 | is $tx->render("main.tx"), $content_base; 56 | 57 | my $path = File::Spec->catfile($service, "main.tx"); 58 | is $tx->{_load_source_count}{$path} => 1; 59 | 60 | } 61 | 62 | done_testing; 63 | exit; 64 | 65 | sub write_file { 66 | my($file, $content) = @_; 67 | 68 | open my($fh), ">", $file; 69 | print $fh $content; 70 | close $fh; 71 | return; 72 | } 73 | -------------------------------------------------------------------------------- /t/900_bugs/028_issue68.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | # https://github.com/xslate/p5-Text-Xslate/issues/68 3 | # "Logic for constant folding unary ops isn't quite right" 4 | use strict; 5 | use warnings; 6 | use Test::More; 7 | 8 | use Text::Xslate; 9 | 10 | my $tx = Text::Xslate->new(cache => 0); 11 | { 12 | my $warnings = ''; 13 | local $SIG{__WARN__} = sub { $warnings .= $_[0] }; 14 | my $result = $tx->render_string(': -(1 + $a)', { a => 2 }); 15 | is($result, "-3"); 16 | is($warnings, ''); 17 | } 18 | 19 | done_testing; 20 | -------------------------------------------------------------------------------- /t/900_bugs/030_issue71.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | # https://github.com/xslate/p5-Text-Xslate/issues/71 3 | use strict; 4 | use warnings; 5 | use Text::Xslate; 6 | use Test::More; 7 | 8 | my $tx = Text::Xslate->new('syntax' => 'TTerse',); 9 | my $CLEANUP_OK; 10 | 11 | { 12 | package MyHandle; 13 | sub new { bless {}, shift } 14 | sub DESTROY { 15 | $CLEANUP_OK++; 16 | } 17 | } 18 | 19 | { 20 | my $dbh = MyHandle->new(); 21 | my @book = ( 22 | {title => "foo", dbh => $dbh}, 23 | {title => "bar", dbh => $dbh}, 24 | ); 25 | my $template = q{ 26 |

[% title %]

27 |
    28 | [% FOREACH book IN books %] 29 | [% SET test = book %] 30 |
  • [% book.title %]
  • 31 | [% END %] 32 |
33 | }; 34 | my $body = $tx->render_string($template, { 35 | books => \@book, 36 | }); 37 | isnt $body, '', 'render_string() succeeded'; 38 | } 39 | 40 | ok($CLEANUP_OK, '$dbh is released'); 41 | 42 | done_testing; 43 | -------------------------------------------------------------------------------- /t/900_bugs/031_yappo.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | # contributed by Yappo https://gist.github.com/4336310 3 | use strict; 4 | use warnings; 5 | use Test::More; 6 | 7 | use Text::Xslate; 8 | 9 | { 10 | my $tx = Text::Xslate->new({ 11 | syntax => 'TTerse' 12 | }); 13 | is eval { 14 | $tx->render_string(<<'TMPL', { yappo => 'hoge' }); 15 | [% SET yappo = "" -%] 16 | [% IF true -%] 17 | [% yappo = 'osawa' -%] 18 | [% yappo -%] 19 | [% ELSIF 0 -%] 20 | [% END -%] 21 | TMPL 22 | }, "osawa"; 23 | is $@, ''; 24 | } 25 | 26 | { 27 | my $tx = Text::Xslate->new({ 28 | syntax => 'TTerse' 29 | }); 30 | is eval { 31 | $tx->render_string(<<'TMPL', { yappo => 'hoge' }); 32 | [% SET yappo = 'fuga' -%] 33 | [% IF true -%] 34 | [% yappo = 'seiidaishogun' -%] 35 | [% ELSIF false -%] 36 | [% yappo = 'osawa' -%] 37 | [% END -%] 38 | [% yappo -%] 39 | TMPL 40 | }, "seiidaishogun"; 41 | is $@, ''; 42 | } 43 | 44 | { 45 | my $tx = Text::Xslate->new({ 46 | syntax => 'TTerse' 47 | }); 48 | is eval { 49 | $tx->render_string(<<'TMPL', { yappo => 'hoge' }); 50 | [% IF true -%] 51 | [% yappo = 'seiidaishogun' -%] 52 | [% ELSIF false -%] 53 | [% yappo = 'osawa' -%] 54 | [% END -%] 55 | [% yappo -%] 56 | TMPL 57 | }, "hoge"; 58 | is $@, ''; 59 | } 60 | 61 | done_testing; 62 | -------------------------------------------------------------------------------- /t/900_bugs/032_issue79.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use warnings; 4 | 5 | use Fatal qw(utime); 6 | 7 | use FindBin qw($Bin); 8 | use File::Path qw(rmtree); 9 | use Config (); 10 | use Test::More; 11 | 12 | use Text::Xslate; 13 | 14 | my $dir = "$Bin/issue79"; 15 | 16 | rmtree "$dir/cache"; 17 | 18 | my $run_cmd = qq{$^X "$dir/xslate.pl"}; 19 | note $run_cmd; 20 | 21 | my @tmpls = qw/ contentA.tt contentB.tt /; 22 | 23 | note 'run with cache'; 24 | my $expected = `$run_cmd`; 25 | is $?, 0, 'process succeed'; 26 | utime time()+2, time()+2, map { "$dir/tmpl/$_" } @tmpls; 27 | 28 | my $got = `$run_cmd`; 29 | is $?, 0, 'process succeed'; 30 | is $got, $expected; 31 | 32 | done_testing; 33 | -------------------------------------------------------------------------------- /t/900_bugs/033_ex_safe_render.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use Test::More; 5 | use File::Temp qw(tempdir); 6 | 7 | { 8 | package MyCounter; 9 | sub new { my $class = shift;bless {@_} => $class } 10 | sub incr { shift->{count}++ } 11 | sub decr { shift->{count}-- } 12 | sub count { shift->{count} } 13 | } 14 | 15 | use Text::Xslate; 16 | my $tx = Text::Xslate->new( 17 | cache => 1, 18 | syntax => 'TTerse', 19 | path => { 20 | 'recurse.tt' => q{ 21 | [%- MACRO mymacro BLOCK -%] 22 | [%- CALL recurse_count.decr -%] 23 | [%- IF recurse_count.count -%] 24 | [%- mymacro() -%] 25 | [%- END -%] 26 | [%- END -%] 27 | [%- mymacro() -%] 28 | }, 29 | }, 30 | cache_dir => tempdir(CLEANUP => 1), 31 | ); 32 | 33 | ok $tx->render('recurse.tt', { recurse_count => MyCounter->new(count => 101) }); 34 | eval { 35 | $tx->render('recurse.tt', { recurse_count => MyCounter->new(count => 102) }); 36 | }; 37 | 38 | ok $tx->render('recurse.tt', { recurse_count => MyCounter->new(count => 101) }); 39 | 40 | done_testing; 41 | -------------------------------------------------------------------------------- /t/900_bugs/034_hash_key_utf8.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use utf8; 4 | use Test::More; 5 | use File::Temp qw(tempdir); 6 | use File::Spec; 7 | use File::Basename; 8 | use Carp (); 9 | use File::Spec (); 10 | use Text::Xslate 1.6001; 11 | use Encode; 12 | 13 | my $tmp = tempdir(CLEANUP => 1); 14 | 15 | sub spew { 16 | my $fname = shift; 17 | open my $fh, '>', $fname 18 | or Carp::croak("Can't open '$fname' for writing: '$!'"); 19 | print {$fh} $_[0]; 20 | } 21 | 22 | sub make_view { 23 | my $view = Text::Xslate->new(+{ 24 | path => [ $tmp ], 25 | cache_dir => $tmp, 26 | function => { 27 | foo => sub { 28 | my($args) = @_; 29 | Encode::is_utf8([keys %$args]->[0]) ? 1 : 0; 30 | } 31 | }, 32 | }); 33 | return $view; 34 | } 35 | 36 | 37 | spew("$tmp/index.tt", "<: foo({page => 1}) :>\n"); 38 | { 39 | my $p1 = make_view()->render('index.tt'); 40 | my $p2 = make_view()->render('index.tt'); 41 | 42 | is $p1, "1\n", 'p1'; 43 | is $p2, "1\n", 'p2'; 44 | } 45 | done_testing; 46 | 47 | -------------------------------------------------------------------------------- /t/900_bugs/035_issue81_tiedhash.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use warnings; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | 8 | use Tie::Hash; 9 | 10 | tie my %vars, 'Tie::StdHash'; 11 | %vars = ( 12 | bar => 'Hello', 13 | ); 14 | my $tx = Text::Xslate->new(cache => 0); 15 | my $out = $tx->render_string( 16 | '<: $foo.bar ~ ", world!" :>', 17 | { foo => \%vars }); 18 | is $out, "Hello, world!"; 19 | done_testing; 20 | -------------------------------------------------------------------------------- /t/900_bugs/036_vpath_utf8.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | # "Strings with code points over 0xFF may not be mapped into in-memory file handles" 3 | use strict; 4 | use warnings; 5 | use utf8; 6 | use Test::More; 7 | 8 | use Text::Xslate; 9 | 10 | my %vpath = ( 11 | entry => 'あいう' 12 | ); 13 | 14 | my $tx = Text::Xslate->new( cache => 0, path => \%vpath ); 15 | is $tx->render('entry'), 'あいう'; 16 | 17 | done_testing; 18 | 19 | -------------------------------------------------------------------------------- /t/900_bugs/037_text_str_key.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use utf8; 4 | use Test::More; 5 | 6 | use Text::Xslate; 7 | 8 | 9 | my $tx = Text::Xslate->new( 10 | cache => 0, 11 | ); 12 | 13 | is $tx->render_string(<<'T'), 10; 14 | : my $h = { "こんにちは" => 10}; 15 | := $h["こんにちは"] 16 | T 17 | 18 | is $tx->render_string(<<'T', { key => "こんにちは"}), 10; 19 | : my $h = { "こんにちは" => 10}; 20 | := $h[$key] 21 | T 22 | 23 | done_testing; 24 | -------------------------------------------------------------------------------- /t/900_bugs/038_conbine_flaged_utf8_and_other.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | 3 | use strict; 4 | use warnings; 5 | 6 | BEGIN{ 7 | # $ENV{XSLATE} = ' dump=ast '; # @@@ @@@ 8 | } 9 | use Text::Xslate; 10 | 11 | use Test::More; 12 | use File::Temp qw(tempdir); 13 | 14 | my %vpath = ( 15 | ascii => "あ<: foo('i') :>う", 16 | utf => "あ<: foo('い') :>う", 17 | ); 18 | 19 | my $tmpdir = tempdir(DIR => ".", CLEANUP => 1); 20 | 21 | my %opts = ( 22 | path => \%vpath, 23 | cache => 1, 24 | cache_dir => $tmpdir, 25 | type => 'html', # enable auto escape 26 | input_layer => ':bytes', 27 | function => { 28 | foo => sub { 29 | my $s = shift; 30 | # warn "[$s][", (utf8::is_utf8($s) ? 'utf' : 'binary'), "]\n"; 31 | Text::Xslate::mark_raw($s); 32 | }, 33 | }, 34 | ); 35 | 36 | my $tx = Text::Xslate->new(\%opts); 37 | 38 | foreach my $type ('original', 'cached'){ 39 | my $tx = Text::Xslate->new(%opts); 40 | 41 | foreach my $try (1, 2){ 42 | is($tx->render('ascii'), q{あiう}, "$type $try ascii"); 43 | is($tx->render('utf'), q{あいう}, "$type $try utf"); 44 | } 45 | } 46 | 47 | $tmpdir = tempdir(DIR => ".", CLEANUP => 1); 48 | 49 | done_testing; 50 | 51 | -------------------------------------------------------------------------------- /t/900_bugs/039_issue96.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use warnings; 4 | 5 | use Test::More; 6 | 7 | use Text::Xslate::Syntax::Kolon; 8 | 9 | { 10 | my $content = <<'T'; 11 | : cascade bar { 12 | : hoge => 'fuga' 13 | : } 14 | T 15 | my $parser = Text::Xslate::Syntax::Kolon->new(); 16 | 17 | local $/; 18 | $parser->parse($content); 19 | } 20 | 21 | pass; 22 | 23 | done_testing; 24 | -------------------------------------------------------------------------------- /t/900_bugs/041_cachedir_other_process.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use warnings; 4 | use Test::More; 5 | use File::Path qw(rmtree); 6 | use lib "t/lib"; 7 | use Util; 8 | use File::Spec; 9 | use Time::HiRes qw(sleep); 10 | use Text::Xslate; 11 | 12 | plan skip_all => 'fork emulation does not work' if $^O eq 'MSWin32'; 13 | 14 | rmtree(cache_dir); 15 | 16 | my $tx = Text::Xslate->new( 17 | path => [path], 18 | cache => 1, 19 | cache_dir => cache_dir, 20 | ); 21 | 22 | my $cache_dir = do { 23 | my $fi = $tx->find_file("hello.tx"); 24 | my($volume, $dir) = File::Spec->splitpath($fi->{cachepath}); 25 | File::Spec->catpath($volume, $dir, ''); 26 | }; 27 | 28 | my $mkpath = File::Path->can('mkpath'); 29 | no warnings 'redefine'; 30 | local *File::Path::mkpath = sub { 31 | my ($path) = @_; 32 | if ($path eq $cache_dir) { 33 | note 'waiting child process'; 34 | sleep 0.2; 35 | ok $cache_dir, 'cache_dir seems created on child process'; 36 | note 'mkpath on parent pwaiting child process'; 37 | } 38 | $mkpath->(@_); 39 | }; 40 | 41 | ok ! -e $cache_dir, 'cache directory does not exists'; 42 | 43 | my $pid = fork; 44 | 45 | BAIL_OUT 'fork failed' unless defined $pid; 46 | 47 | if ($pid) { 48 | my $fi = $tx->load_file("hello.tx"); 49 | ok -e $cache_dir, 'cache directory exists'; 50 | done_testing; 51 | rmtree(cache_dir); 52 | } else { 53 | note 'waiting if(not -e $cachedir) {'; 54 | sleep 0.1; 55 | note 'mkpath on child process'; 56 | $mkpath->($cache_dir); 57 | } 58 | -------------------------------------------------------------------------------- /t/900_bugs/042_perl59_issue.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | # https://github.com/xslate/p5-Text-Xslate/issues/105 3 | use strict; 4 | use warnings; 5 | use utf8; 6 | use Test::More 0.96; 7 | 8 | # This code cause segmentation fault on Perl 5.19.[79]. 9 | 10 | use Text::Xslate; 11 | 12 | my @warn; 13 | { 14 | local $SIG{__WARN__} = sub { push @warn, @_ }; 15 | my $xslate = Text::Xslate->new(); 16 | $xslate->render_string(<<'...'); 17 | : '/' ~ uri('a') 18 | : for 3 -> $n { } 19 | ... 20 | } 21 | note $_ for @warn; 22 | 23 | pass; 24 | 25 | done_testing; 26 | -------------------------------------------------------------------------------- /t/900_bugs/043_issue107.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | # https://github.com/xslate/p5-Text-Xslate/issues/107 3 | use strict; 4 | use warnings; 5 | use Test::More; 6 | 7 | use Text::Xslate; 8 | use Text::Xslate::Util qw(hash_with_default); 9 | 10 | 11 | my $tx = Text::Xslate->new(); 12 | is($tx->render_string('[<: $oops :>]', hash_with_default(+{}, sub { 'null' })), '[null]'); 13 | is($tx->render_string('[<: $oops :>]', hash_with_default(+{ oops => undef }, sub { 'null' })), '[]'); 14 | 15 | done_testing; 16 | -------------------------------------------------------------------------------- /t/900_bugs/044_empty_result.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | # https://github.com/xslate/p5-Text-Xslate/issues/111 3 | use strict; 4 | use warnings; 5 | use Test::More; 6 | use Text::Xslate; 7 | 8 | my $tx = Text::Xslate->new(); 9 | 10 | is($tx->render_string('1'), '1'); 11 | is($tx->render_string('0'), '0'); 12 | 13 | done_testing; 14 | -------------------------------------------------------------------------------- /t/900_bugs/045_issue130.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | use Text::Xslate; 5 | 6 | my $tx = Text::Xslate->new( 7 | path => { 8 | 'body.tx' => ': block body | reverse -> { include text }', 9 | 'text.tx' => 'Text::Xslate', 10 | }, 11 | function => { 12 | reverse => sub { scalar reverse $_[0]; }, 13 | }, 14 | cache => 0, 15 | ); 16 | 17 | { 18 | my $warnings = ''; 19 | is($tx->render('body.tx'), 'etalsX::txeT'); 20 | is($warnings, ''); 21 | } 22 | 23 | done_testing; 24 | -------------------------------------------------------------------------------- /t/900_bugs/046_issue156.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use Test::More; 4 | use Text::Xslate; 5 | 6 | my $base = <<'EOF'; 7 | : if 0 { 8 | : my $var = [ ]; 9 | : } 10 | : for ['default'] -> $t { 11 | : } 12 | : block content -> { } 13 | Good 14 | EOF 15 | 16 | my $xslate1 = Text::Xslate->new( 17 | path => { 18 | 'base.tx' => $base, 19 | 'page.tx' => q{: cascade "base.tx"}, 20 | }, 21 | warn_handler => sub { die @_ }, 22 | cache => 0, 23 | ); 24 | my $res1 = $xslate1->render('page.tx', { }); 25 | is $res1, "Good\n"; 26 | 27 | done_testing; 28 | -------------------------------------------------------------------------------- /t/900_bugs/046_issue88.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | # https://github.com/xslate/p5-Text-Xslate/issues/88 3 | use strict; 4 | use warnings; 5 | use Test::More; 6 | 7 | use utf8; 8 | use Text::Xslate 'mark_raw'; 9 | my $xslate = Text::Xslate->new(); 10 | 11 | is $xslate->render_string('<: $string :>', {string => "Ä"}) => 'Ä'; 12 | is $xslate->render_string('<: $string :>', {string => "\x{c4}"}) => 'Ä'; 13 | 14 | is $xslate->render_string('あ<: $string :>', {string => "Ä"}) => 'あÄ'; 15 | is $xslate->render_string('あ<: $string :>', {string => "\x{c4}"}) => 'あÄ'; 16 | 17 | is $xslate->render_string('<: $string :>', {string => mark_raw("Ä")}) => 'Ä'; 18 | is $xslate->render_string('<: $string :>', {string => mark_raw("\x{c4}")}) => 'Ä'; 19 | 20 | is $xslate->render_string('あ<: $string :>', {string => mark_raw("Ä")}) => 'あÄ'; 21 | is $xslate->render_string('あ<: $string :>', {string => mark_raw("\x{c4}")}) => 'あÄ'; 22 | 23 | done_testing(); 24 | -------------------------------------------------------------------------------- /t/900_bugs/047_undef_concat.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | # https://github.com/xslate/p5-Text-Xslate/issues/xx 3 | use strict; 4 | use warnings; 5 | use Test::More; 6 | 7 | use utf8; 8 | use Text::Xslate 'mark_raw'; 9 | my $xslate = Text::Xslate->new(); 10 | 11 | # without the fix, these fail with 12 | # "Use of uninitialized value in subroutine entry" 13 | 14 | 15 | my $t = sub { 16 | is $xslate->render_string('<: $s1 ~ $s2 :>', shift), shift; 17 | }; 18 | 19 | $t->( {s1 => 'A', s2 => 'B' } => 'AB'); 20 | $t->( {s1 => 'A', s2 => undef } => 'A'); 21 | $t->( {s1 => undef, s2 => 'B' } => 'B'); 22 | 23 | $t->( {s1 => mark_raw('A'), s2 => undef } => 'A'); 24 | $t->( {s1 => undef, s2 => mark_raw('B') } => 'B'); 25 | 26 | 27 | # the automatic html-escaping that xslate does 28 | $t->({s1 => 'A', s2 => '' } => 'A<B>'); 29 | $t->({s1 => 'A', s2 => mark_raw('') } => 'A' ); 30 | 31 | $t->({s1 => '', s2 => 'B' } => '<A>B'); 32 | $t->({s1 => mark_raw(''), s2 => 'B' } => 'B' ); 33 | 34 | # those two again with undefs 35 | $t->({s1 => '', s2 => undef } => '<A>'); 36 | $t->({s1 => mark_raw(''), s2 => undef } => ''); 37 | 38 | $t->({s1 => undef, s2 => '' } => '<B>'); 39 | $t->({s1 => undef, s2 => mark_raw('') } => ''); 40 | 41 | # undef on both sides 42 | $t->({s1 => undef, s2 => undef } => ''); 43 | 44 | done_testing(); 45 | -------------------------------------------------------------------------------- /t/900_bugs/048_issue172.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | use Test::Requires qw(Data::Section::Simple); 5 | use Text::Xslate; 6 | use Text::Xslate::Util; 7 | use Test::More; 8 | 9 | my $vpath = Data::Section::Simple->new()->get_data_section(); 10 | my $vars = { foo => 'hoge', bar => 'fuga' }; 11 | 12 | my $xslate = Text::Xslate->new(path => [$vpath], cache => 0); 13 | 14 | my $tied = Text::Xslate::Util::hash_with_default($vars, sub { "FILL @_" }); 15 | my $got = $xslate->render('base.tx', $tied); 16 | is($got, "foo:hoge, bar:fuga\n", "localize tied hash"); 17 | 18 | done_testing; 19 | 20 | __DATA__ 21 | @@ base.tx 22 | :include _partial { foo => $foo } 23 | @@ _partial.tx 24 | foo:<: $foo :>, bar:<: $bar :> 25 | -------------------------------------------------------------------------------- /t/900_bugs/issue79/tmpl/contentA.tt: -------------------------------------------------------------------------------- 1 | [% WRAPPER 'wrapperB.tt' %] 2 | CONTENT A 3 | [% END %] 4 | -------------------------------------------------------------------------------- /t/900_bugs/issue79/tmpl/contentB.tt: -------------------------------------------------------------------------------- 1 | [% WRAPPER 'wrapperB.tt' %] 2 | CONTENT B 3 | [% END %] 4 | -------------------------------------------------------------------------------- /t/900_bugs/issue79/tmpl/wrapperA.tt: -------------------------------------------------------------------------------- 1 | BEGIN WRAPPER A { 2 | [% content %] 3 | } END WRAPPER A 4 | -------------------------------------------------------------------------------- /t/900_bugs/issue79/tmpl/wrapperB.tt: -------------------------------------------------------------------------------- 1 | [% WRAPPER 'wrapperA.tt' %] 2 | BEGIN WRAPPER B { 3 | [% content %] 4 | } END WRAPPER B 5 | [% END %] 6 | -------------------------------------------------------------------------------- /t/900_bugs/issue79/xslate.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | use strict; 3 | use warnings; 4 | 5 | use Text::Xslate; 6 | use FindBin qw($Bin); 7 | 8 | my $xslate = Text::Xslate->new({ 9 | syntax => 'TTerse', 10 | path => "$Bin/tmpl", 11 | cache_dir => "$Bin/cache", 12 | }); 13 | 14 | my @tmpls = qw/ contentA.tt contentB.tt /; 15 | for my $tmpl (@tmpls) { 16 | print "\@\@$tmpl\n"; 17 | print $xslate->render($tmpl); 18 | } 19 | -------------------------------------------------------------------------------- /t/lib/MyBridge2.pm: -------------------------------------------------------------------------------- 1 | package MyBridge2; 2 | use strict; 3 | use parent qw(Text::Xslate::Bridge); 4 | 5 | __PACKAGE__->bridge( 6 | scalar => { bar => sub { 'scalar bar' } }, 7 | array => { bar => sub { 'array bar' } }, 8 | hash => { bar => sub { 'hash bar' } }, 9 | ); 10 | 11 | 12 | 1; 13 | -------------------------------------------------------------------------------- /t/lib/TTSimple.pm: -------------------------------------------------------------------------------- 1 | package TTSimple; 2 | 3 | use strict; 4 | use constant USE_TT => scalar(grep { $_ eq '--tt' } @ARGV) || $ENV{USE_TT}; 5 | use Carp; 6 | 7 | use parent qw(Exporter); 8 | our @EXPORT = qw(render_str render_file); 9 | 10 | use lib "t/lib"; 11 | use Util; 12 | 13 | my $tt; 14 | 15 | if(USE_TT) { 16 | require Test::More; 17 | Test::More::note('use Template::Toolkit'); 18 | 19 | require Template; 20 | $tt = Template->new( 21 | INCLUDE_PATH => path, 22 | ANYCASE => 1, 23 | ); 24 | } 25 | else { 26 | require Text::Xslate; 27 | require Text::Xslate::Syntax::TTerse; 28 | 29 | our %Func; 30 | $tt = Text::Xslate->new( 31 | path => [path], 32 | cache_dir => path, 33 | cache => 0, 34 | syntax => 'TTerse', 35 | warn_handler => \&Carp::confess, 36 | die_handler => \&Carp::confess, 37 | 38 | function => \%Func, 39 | ); 40 | } 41 | 42 | sub render_file { 43 | my($in, $vars) = @_; 44 | 45 | if(USE_TT) { 46 | my $out; 47 | $tt->process($in, $vars, \$out) or do { 48 | require Data::Dumper; 49 | croak Data::Dumper::Dumper($tt->error); 50 | }; 51 | return $out; 52 | } 53 | else { 54 | return $tt->render($in, $vars); 55 | } 56 | } 57 | 58 | sub render_str { 59 | my($in, $vars) = @_; 60 | 61 | if(USE_TT) { 62 | my $out; 63 | $tt->process(\$in, $vars, \$out) or croak $tt->error, "($in)"; 64 | return $out; 65 | } 66 | else { 67 | return $tt->render_string($in, $vars); 68 | } 69 | } 70 | 71 | -------------------------------------------------------------------------------- /t/lib/Text/Xslate/Syntax/Foo.pm: -------------------------------------------------------------------------------- 1 | package Text::Xslate::Syntax::Foo; 2 | use Mouse; 3 | 4 | extends qw(Text::Xslate::Parser); 5 | 6 | sub _build_line_start { undef } 7 | sub _build_tag_start { '<%' } 8 | sub _build_tag_end { '%>' } 9 | 10 | no Mouse; 11 | 12 | __PACKAGE__->meta->make_immutable(); 13 | -------------------------------------------------------------------------------- /t/lib/Util.pm: -------------------------------------------------------------------------------- 1 | package Util; 2 | use strict; 3 | 4 | use parent qw(Exporter); 5 | our @EXPORT = qw(path cache_dir); 6 | 7 | use File::Temp qw(tempdir); 8 | use File::Find qw(find); 9 | 10 | use Test::Requires "File::Copy::Recursive"; 11 | $File::Copy::Recursive::KeepMode = 0; 12 | 13 | my $path; 14 | my $cache_dir; 15 | 16 | sub reinit { 17 | $path = tempdir CLEANUP => 1; 18 | File::Copy::Recursive::rcopy("t/template", $path) or die $!; 19 | if ($^O eq 'MSWin32') { 20 | # On windows, File::Copy::copy equals Win32::CopyFile, which preserves mtime. 21 | # In our case, we need to ensure that mtime of copied files are now 22 | my $wanted = sub { 23 | return unless -f; 24 | $_ = $1 if /(.+)/; # untaint 25 | utime $^T, $^T, $_; 26 | }; 27 | find({wanted => $wanted, no_chdir => 1}, $path); 28 | } 29 | $cache_dir = tempdir CLEANUP => 1; 30 | } 31 | 32 | sub path () { $path } 33 | sub cache_dir () { $cache_dir } 34 | 35 | reinit(); 36 | 37 | 1; 38 | -------------------------------------------------------------------------------- /t/lib/UtilNoleak.pm: -------------------------------------------------------------------------------- 1 | package UtilNoleak; 2 | use strict; 3 | 4 | use parent qw(Exporter); 5 | our @EXPORT = qw(path cache_dir); 6 | 7 | use FindBin qw($Bin); 8 | use File::Basename qw(dirname); 9 | 10 | use constant path => dirname($Bin) . "/template"; 11 | use constant cache_dir => ".xslate_cache/$0"; 12 | 1; 13 | 14 | -------------------------------------------------------------------------------- /t/template/common.tx: -------------------------------------------------------------------------------- 1 | : macro foo -> { 2 | This is ! 3 | : } 4 | 5 | 6 | : before bar -> { 7 | before ! 8 | : } 9 | 10 | : block baz -> { 11 | This is ! 12 | : } 13 | -------------------------------------------------------------------------------- /t/template/config.tt: -------------------------------------------------------------------------------- 1 | [% DEFAULT lang = 'TTerse' -%] 2 | [% MACRO em(s) BLOCK -%] 3 | [% s %] 4 | [%- END -%] 5 | -------------------------------------------------------------------------------- /t/template/eg/base.tx: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | <: block title -> { :>My amazing site<: } :> 7 | 8 | 9 | 10 | 18 | 19 |
20 | : block content -> { ; } 21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /t/template/eg/child.tx: -------------------------------------------------------------------------------- 1 | : cascade eg::base 2 | 3 | <: around title ->{ :>My amazing blog<: } :> 4 | 5 | : around content -> { 6 | : for $blog_entries -> ($entry) { 7 |

<:= $entry.title :>

8 |

<:= $entry.body :>

9 | : } # end for 10 | : } # end content 11 | -------------------------------------------------------------------------------- /t/template/error/bad_include.tx: -------------------------------------------------------------------------------- 1 | : for [42] -> $it { 2 | <: include "no_such_file" :> 3 | : } 4 | -------------------------------------------------------------------------------- /t/template/error/bad_method.tx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | : for [42] -> $it { 5 | <: $it.foobar() :> 6 | : } 7 | -------------------------------------------------------------------------------- /t/template/error/bad_syntax.tx: -------------------------------------------------------------------------------- 1 | 2 | 3 | : for [42] -> $it { 4 | <: dump($it)) :> 5 | : } 6 | -------------------------------------------------------------------------------- /t/template/error/bad_tags.tx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | : for [42] -> $it { 7 | <: $it 8 | : } 9 | -------------------------------------------------------------------------------- /t/template/footer1.tt: -------------------------------------------------------------------------------- 1 | footer1 2 | -------------------------------------------------------------------------------- /t/template/footer2.tt: -------------------------------------------------------------------------------- 1 | footer2 2 | -------------------------------------------------------------------------------- /t/template/for.tx: -------------------------------------------------------------------------------- 1 | : for $books ->($item) { 2 | [<:= $item.title :>] 3 | : } 4 | -------------------------------------------------------------------------------- /t/template/func.tx: -------------------------------------------------------------------------------- 1 | Hello, <:= f($lang) :> world! 2 | -------------------------------------------------------------------------------- /t/template/header1.tt: -------------------------------------------------------------------------------- 1 | header1 2 | -------------------------------------------------------------------------------- /t/template/header2.tt: -------------------------------------------------------------------------------- 1 | header2 2 | -------------------------------------------------------------------------------- /t/template/hello.tt: -------------------------------------------------------------------------------- 1 | Hello, [% lang %] world! 2 | -------------------------------------------------------------------------------- /t/template/hello.tx: -------------------------------------------------------------------------------- 1 | Hello, <:= $lang :> world! 2 | -------------------------------------------------------------------------------- /t/template/hello.tx.mod: -------------------------------------------------------------------------------- 1 | Hi, <:= $lang :>. 2 | -------------------------------------------------------------------------------- /t/template/hello_sjis.tx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xslate/p5-Text-Xslate/a7908ee382ed01f3a407fe826f2741de76287d36/t/template/hello_sjis.tx -------------------------------------------------------------------------------- /t/template/hello_utf8.tx: -------------------------------------------------------------------------------- 1 | こんにちは! <:= $name :>! 2 | -------------------------------------------------------------------------------- /t/template/include.tt: -------------------------------------------------------------------------------- 1 | include: 2 | [% INCLUDE "hello.tt" -%] 3 | -------------------------------------------------------------------------------- /t/template/include.tx: -------------------------------------------------------------------------------- 1 | include: 2 | :include "hello.tx" 3 | -------------------------------------------------------------------------------- /t/template/include2.tt: -------------------------------------------------------------------------------- 1 | include2: 2 | [% INCLUDE $file -%] 3 | -------------------------------------------------------------------------------- /t/template/include2.tx: -------------------------------------------------------------------------------- 1 | include2: 2 | :include $file 3 | -------------------------------------------------------------------------------- /t/template/macro.tt: -------------------------------------------------------------------------------- 1 | [% MACRO foo() BLOCK %]foo[% END -%] 2 | -------------------------------------------------------------------------------- /t/template/myapp/bad_redefine.tx: -------------------------------------------------------------------------------- 1 | : cascade myapp::base 2 | 3 | : block hello -> { 4 | BAD REDEFINE 5 | : } 6 | -------------------------------------------------------------------------------- /t/template/myapp/base.tx: -------------------------------------------------------------------------------- 1 | HEAD 2 | : block hello -> { 3 | Hello, <:= $lang :> world! 4 | : } 5 | FOOT 6 | -------------------------------------------------------------------------------- /t/template/myapp/base.tx.mod: -------------------------------------------------------------------------------- 1 | HEAD 2 | : block hello -> { 3 | Modified version of base.tx 4 | : } 5 | FOOT 6 | -------------------------------------------------------------------------------- /t/template/myapp/cbar.tx: -------------------------------------------------------------------------------- 1 | : # This is a cascading component 2 | 3 | : after hello -> { 4 | BAR 5 | : } 6 | -------------------------------------------------------------------------------- /t/template/myapp/cfoo.tx: -------------------------------------------------------------------------------- 1 | : # This is a cascading component 2 | 3 | : before hello -> { 4 | FOO 5 | : } 6 | -------------------------------------------------------------------------------- /t/template/myapp/derived.tx: -------------------------------------------------------------------------------- 1 | : cascade myapp::base 2 | 3 | : before hello -> { 4 | D-BEFORE 5 | : } 6 | : after hello -> { 7 | D-AFTER 8 | : } 9 | -------------------------------------------------------------------------------- /t/template/oi/bad_base.tx: -------------------------------------------------------------------------------- 1 | HEAD 2 | : block hello -> { 3 | Hello, <:= $a.bad_code_foo() :> world! 4 | : } 5 | FOOT 6 | -------------------------------------------------------------------------------- /t/template/oi/bad_component.tx: -------------------------------------------------------------------------------- 1 | : # This is a cascading component 2 | 3 | : after hello -> { 4 | <: $a.bad_code_bar() :> 5 | : } 6 | -------------------------------------------------------------------------------- /t/template/other/hello.tx: -------------------------------------------------------------------------------- 1 | Hello, <:= $lang :> world! 2 | -------------------------------------------------------------------------------- /t/template/taint.tx: -------------------------------------------------------------------------------- 1 | foo foo foo foo foo foo foo foo foo foo foo foo 2 | foo foo foo foo foo foo foo foo foo foo foo foo 3 | foo foo foo foo foo foo foo foo foo foo foo foo 4 | foo foo foo foo foo foo foo foo foo foo foo foo 5 | foo foo foo foo foo foo foo foo foo foo foo foo 6 | <:= $scalar_variable :> 7 | <:= $hash_variable.hash_value_key :> 8 | <:= $array_variable[ 2 ] :> 9 | <:= $this.is.a.very.deep.hash.structure :> 10 | <: for $array_loop ->($i) { :><:= $i :><: } :> 11 | <: for $hash_loop.keys() ->($k) { :> 12 | <:= $k :>: <:= $hash_loop[ $k ] :> 13 | <: } :> 14 | <: for $records_loop ->($r) { :> 15 | <:= $r.name :>: <:= $r.age :> 16 | <: } :> 17 | <: for $array_loop ->($i) { :> 18 | <:= $i :> 19 | <: } :> 20 | <: for $hash_loop.keys() ->($k) { :> 21 | <:= $k :>: <:= $hash_loop[ $k ] :> 22 | <: } :> 23 | <: for $records_loop ->($r) { :> 24 | <:= $r.name :>: <:= $r.age :> 25 | <: } :> 26 | <: if 1 { :>true<: } :> 27 | <: if $variable_if { :>true<: } :> 28 | <: if 1 { :>true<: } else { :>false<: } :> 29 | <: if $variable_if_else { :>true<: } else { :>false<: } :> 30 | <: if 1 { :><:= $template_if_true :><: } :> 31 | <: if $variable_if { :><:= $template_if_true :><: } :> 32 | <: if 1 { :><:= $template_if_true :><: } else { :><:= $template_if_false :><: } :> 33 | <: if $variable_if_else { :><:= $template_if_true :><: } else { :><:= $template_if_false :><: } :> 34 | <:= 10 + 12 :> 35 | <:= $variable_expression_a * $variable_expression_b :> 36 | <:= ( ( $variable_expression_a * $variable_expression_b ) + $variable_expression_a - $variable_expression_b ) / $variable_expression_b :> 37 | <:= substr( 'this has a substring.', 11, 9 ) :> 38 | <:= substr( $variable_function_arg, 4, 2 ) :> 39 | -------------------------------------------------------------------------------- /t/template/wrapper.tt: -------------------------------------------------------------------------------- 1 | ------------------ 2 | [% content -%] 3 | ------------------ 4 | -------------------------------------------------------------------------------- /t/template/wrapper_div.tt: -------------------------------------------------------------------------------- 1 |
2 | [% content -%] 3 |
4 | -------------------------------------------------------------------------------- /tool/disasm.pl: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Data::MessagePack; 4 | use Data::Dumper; 5 | use File::Slurp qw(slurp); 6 | 7 | $Data::Dumper::Indent = 0; 8 | $Data::Dumper::Terse = 1; 9 | $Data::Dumper::Useqq = 1; 10 | 11 | foreach my $file(@ARGV) { 12 | my $data = slurp($file); 13 | my $unpacker = Data::MessagePack::Unpacker->new(); 14 | 15 | my $offset = $unpacker->execute($data); 16 | my $is_utf8 = $unpacker->data(); 17 | $unpacker->reset(); 18 | $unpacker->utf8($is_utf8); 19 | 20 | while($offset < length($data)) { 21 | $offset = $unpacker->execute($data, $offset); 22 | my $c = $unpacker->data(); 23 | $unpacker->reset(); 24 | print Dumper($c), "\n"; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tool/uri_unsafe.PL: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use warnings; 4 | 5 | my $uri_unsafe = qr/[^A-Za-z0-9\-\._~]/; # defined by RFC 3986 6 | 7 | print "/* Check whether a character is unsafe by the definition of RFC 3986 */\n"; 8 | print "/* This file is automatically generated by $0 */\n"; 9 | 10 | print <<'C'; 11 | static int 12 | is_uri_unsafe(char const c) { 13 | switch((unsigned char)c) { 14 | C 15 | 16 | for(my $i = 0; $i < 256; $i++) { 17 | my $c = chr $i; 18 | 19 | if($c !~ $uri_unsafe) { 20 | printf " case 0x%02X: return 0;%s\n", 21 | $i, 22 | ($c =~ /[[:print:]]/ ? " /* $c */" : ''); 23 | } 24 | } 25 | 26 | print <<'C'; 27 | default: return 1; 28 | } 29 | } /* is_uri_unsafe */ 30 | C 31 | -------------------------------------------------------------------------------- /xt/01_podspell.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | eval q{ use Test::Spellunker }; 7 | 8 | plan skip_all => q{Test::Spellunker are not available.} 9 | if $@; 10 | 11 | add_stopwords(map { split /[\s\:\-]/ } ); 12 | all_pod_files_spelling_ok('lib'); 13 | 14 | __DATA__ 15 | Text::Xslate 16 | xslate 17 | todo 18 | str 19 | Opcode 20 | cpan 21 | render 22 | Kolon 23 | Metakolon 24 | TTerse 25 | syntaxes 26 | pre 27 | namespaces 28 | plugins 29 | html 30 | acknowledgement 31 | iff 32 | EscapedString 33 | sandboxing 34 | APIs 35 | runtime 36 | autoboxing 37 | backend 38 | TT 39 | adaptor 40 | overridable 41 | inline 42 | Toolkit's 43 | FillInForm 44 | uri 45 | CLI 46 | PSGI 47 | XSS 48 | Mojo 49 | SCALARs 50 | namespace 51 | CGI 52 | xml 53 | RT 54 | XS 55 | Exportable 56 | Misc 57 | callback 58 | callbacks 59 | RFC 60 | colorize 61 | Pre 62 | IRC 63 | irc 64 | org 65 | WAF 66 | WAFs 67 | JavaScript 68 | fallbacks 69 | UTF 70 | preforking 71 | github 72 | Mojolicious 73 | HTMLTemplate 74 | blog 75 | 76 | # personal name 77 | lestrrat 78 | tokuhirom 79 | gardejo 80 | jjn 81 | Goro Fuji 82 | gfx 83 | Douglas Crockford 84 | makamaka 85 | Hannyaharamitu 86 | Maki 87 | Daisuke 88 | typester 89 | clouder 90 | punytan 91 | chiba 92 | turugina 93 | hirose 94 | kane 95 | taka 96 | cho 97 | shmorimo 98 | ueda 99 | parens 100 | opcodes 101 | ing 102 | vs 103 | metacharacters 104 | metacharacters 105 | expressionsi 106 | name-coderef 107 | cb 108 | cb 109 | render 110 | render_string 111 | tx 112 | newlines 113 | -------------------------------------------------------------------------------- /xt/02_pod.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | eval q{use Test::Pod 1.14}; 6 | plan skip_all => 'Test::Pod 1.14 required for testing PODU' 7 | if $@; 8 | 9 | all_pod_files_ok(); 10 | -------------------------------------------------------------------------------- /xt/03_pod-coverage.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use Test::More; 4 | eval q{use Test::Pod::Coverage 1.04}; 5 | 6 | plan skip_all => '(todo)'; 7 | 8 | plan skip_all => 'Test::Pod::Coverage 1.04 required for testing POD coverage' 9 | if $@; 10 | 11 | all_pod_coverage_ok({ 12 | also_private => [qw(unimport BUILD DEMOLISH)], 13 | }); 14 | -------------------------------------------------------------------------------- /xt/04_synopsis.t: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use Test::More; 4 | use Test::Requires 'Test::Synopsis'; 5 | use File::Find (); 6 | 7 | my @file; 8 | my $wanted = sub { push @file, $_ if -f && /\.(?:pm|pod)$/ }; 9 | File::Find::find({wanted => $wanted, no_chdir => 1}, "lib"); 10 | 11 | synopsis_ok(@file); 12 | 13 | done_testing; 14 | -------------------------------------------------------------------------------- /xt/05_vars.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | 3 | use strict; 4 | use Test::More; 5 | 6 | use Test::Requires qw(Test::Vars); 7 | use File::Find (); 8 | 9 | my @file; 10 | my $wanted = sub { push @file, $_ if -f && /\.pm$/ }; 11 | File::Find::find({wanted => $wanted, no_chdir => 1}, "lib"); 12 | 13 | vars_ok $_, ignore_vars => [qw($parser $symbol $)] for @file; 14 | 15 | done_testing; 16 | -------------------------------------------------------------------------------- /xt/200_depended.t: -------------------------------------------------------------------------------- 1 | #!perl -w 2 | use strict; 3 | use Test::More; 4 | use File::Path qw(rmtree); 5 | use Test::Requires 'File::Which'; 6 | plan skip_all => 'disable on windows' if $^O eq 'MSWin32'; 7 | 8 | use constant LDIR => '.test_deps'; 9 | BEGIN{ rmtree(LDIR) } 10 | END { rmtree(LDIR) } 11 | 12 | my $cpanm = File::Which::which('cpanm') or plan skip_all => 'no cpanm'; 13 | 14 | my @modules = qw( 15 | Text::Xslate::Bridge::TT2Like 16 | Catalyst::View::Xslate 17 | ); 18 | 19 | foreach my $mod(@modules) { 20 | note $mod; 21 | is system($^X, $cpanm, -l => LDIR, qw(-nq --installdeps), $mod), 0, $mod; 22 | is system($^X, $cpanm, -l => LDIR, qw(-q --test-only), $mod), 0, $mod; 23 | } 24 | 25 | done_testing; 26 | 27 | --------------------------------------------------------------------------------