File Coverage

blib/lib/Template/Swig.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Template::Swig;
2              
3 1     1   29414 use strict;
  1         3  
  1         44  
4 1     1   6 use warnings;
  1         2  
  1         30  
5              
6 1     1   6 use Carp;
  1         6  
  1         99  
7              
8             our $VERSION = '0.05';
9              
10 1     1   1198 use File::Slurp qw(read_file);
  1         21125  
  1         97  
11 1     1   483 use JavaScript::V8;
  0            
  0            
12             use JSON::XS;
13              
14             sub new {
15              
16             my ($class, %params) = @_;
17              
18             my $self = bless {}, $class;
19              
20             $self->{context} = JavaScript::V8::Context->new();
21             $self->{json} = JSON::XS->new->allow_nonref;
22             $self->{json}->canonical(1); # Make sure that a given struct is always encoded the same way
23             $self->{extends_callback} = $params{extends_callback};
24             $self->{template_dir} = $params{template_dir};
25              
26             $self->{template_names} = {};
27              
28             $self->_load_swig;
29              
30             return $self;
31             }
32              
33             sub _load_swig {
34              
35             my ($self) = @_;
36              
37             if ( my $cb = $self->{extends_callback} ) {
38             $self->{context}->eval(<
39             var fs = {
40             readFileSync: function(file, encoding){
41             return perl_callback(file, encoding);
42             }
43             };
44             EOT
45             $self->{context}->bind('perl_callback' => sub {
46             my ($filename, $encoding) = @_;
47             my $template;
48             my $error = do {
49             local $@;
50             eval {
51             $filename = join('/',$self->{template_dir}, $filename) if $self->{template_dir};
52             $template = $cb->($filename, $encoding);
53             };
54             $@;
55             };
56             carp $error if $error;
57             return $template;
58             });
59             }
60              
61             local $/ = undef;
62             my $swig_source = ;
63              
64             $self->{context}->eval($swig_source);
65             confess $@ if $@;
66             }
67              
68             sub compileFromFile {
69              
70             my ($self, $f) = @_;
71             my $filename = ref($f) ? $f->{filename} : $f;
72             $filename = join('/',$self->{template_dir}, $filename) if $self->{template_dir};
73             if ( -e $filename ) {
74             my $template = read_file($filename);
75             $self->compile($f, $template);
76             } else {
77             die "Unable to locate $filename";
78             }
79             }
80              
81             sub compile {
82              
83             my ($self, $template_name, $template_string) = @_;
84              
85             die "need a template_string" unless $template_string;
86             my $template_string_json = $self->{json}->encode($template_string);
87              
88             die "need a template_name" unless $template_name;
89             my $template_name_json = $self->{json}->encode($template_name);
90              
91             if (ref $template_name) {
92             $template_name_json =~ s/'/\\'/g;
93             $template_name_json = "'$template_name_json'";
94             }
95              
96             $self->{context}->eval(<
97             var template_string = $template_string_json;
98             var template = swig.compile(template_string, { filename: $template_name_json });
99             templates[$template_name_json] = template;
100              
101             false;
102             EOT
103             confess $@ if $@;
104              
105             $self->{is_compiled}{$template_name_json} = 1;
106             }
107              
108             sub render {
109              
110             my ($self, $template_name, $data) = @_;
111              
112             die "need a template_name" unless $template_name;
113             my $template_name_json = $self->{json}->encode($template_name);
114             if (ref $template_name) {
115             $template_name_json =~ s/'/\\'/g;
116             $template_name_json = "'$template_name_json'";
117             }
118              
119             die "couldn't find template: $template_name" unless $self->{is_compiled}{$template_name_json};
120              
121             my $data_json = $self->{json}->encode($data);
122              
123             my $output = $self->{context}->eval(<
124             var output = templates[$template_name_json]($data_json);
125              
126             output;
127             EOT
128             confess $@ if $@;
129             return $output;
130             }
131              
132             1;
133              
134             =pod
135              
136             =head1 NAME
137              
138             Template::Swig - Perl interface to Django-inspired Swig templating engine.
139              
140             =head1 SYNOPSIS
141              
142             my $swig = Template::Swig->new;
143              
144             # Compile and render an inline template:
145             $swig->compile('message', 'Welcome, {{name}}');
146             my $output = $swig->render('message', { name => 'Arthur' });
147              
148             # Compile and render a file:
149             $swig->compileFromFile('path/to/file.html');
150             my $output = $swig->render('path/to/file.html', { some_param => 'foo' });
151              
152             =head1 DESCRIPTION
153              
154             Template::Swig uses L and L templating engine to provide fast Django-inspired templating in a Perl context. Templates are compiled to JavaScript functions and stored in memory, then executed each time they're rendered.
155              
156             Swig's feature list includes multiple inheritance, formatter and helper functions, macros, auto-escaping, and custom tags. See the L for more.
157              
158             =head1 METHODS
159              
160             =head2 new( template_dir => $path, extends_callback => sub { } )
161              
162             Initialize a swig instance, given the following parameters
163              
164             =head3 template_dir
165              
166             Optional path where templates live
167              
168             =head3 extends_callback
169              
170             Optional callback to be run when Swig encounters an extends tag; receives filename and its encoding as parameters
171              
172             =head2 compile($template_name, $swig_source)
173              
174             Compile a template given, given a template name and swig template source as a string.
175              
176             =head2 compileFromFile($f)
177              
178             Will compile a file from the filesystem. C<$f> may be either a scalar filename,
179             or a reference to a hash with at least one key, C.
180              
181             =head2 compile($string)
182              
183             Will compile data supplied in the scalar C<$string>.
184              
185             =head2 render($template_name, $data)
186              
187             Render a template, given a name and a reference to a hash of data to interpolate.
188              
189             =head1 TEMPLATE EXAMPLES
190              
191             Iterate through a list:
192              
193             {% for image in images %}
194            
195             {% else %}
196            
No images to show
197             {% endfor %}
198              
199             Custom helpers / filters:
200              
201             {{ created|date('r') }}
202              
203             =head3 Inheritance:
204              
205             In main.html:
206            
207             {% block 'greeting' %}
208             Hi, there.
209             {% endblock %}
210            
211             In custom.html:
212            
213             {% extends 'main.html' %}
214            
215             {% block 'greeting' %}
216             Welcome, {{ name }}
217             {% endblock %}
218              
219             =head1 SEE ALSO
220              
221             L, L, L
222              
223             =head1 COPYRIGHT AND LICENSE
224              
225             Copyright (c) 2012, David Chester
226              
227             This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
228              
229             =cut
230              
231             __DATA__