File Coverage

blib/lib/Jemplate.pm
Criterion Covered Total %
statement 95 179 53.0
branch 44 134 32.8
condition 12 47 25.5
subroutine 11 19 57.8
pod 4 13 30.7
total 166 392 42.3


line stmt bran cond sub pod time code
1             # ToDo:
2             # - Use TT:Simple in Makefiles
3              
4             package Jemplate;
5 22     22   72854 use strict;
  22         52  
  22         1003  
6 22     22   122 use warnings;
  22         45  
  22         788  
7 22     22   28093 use Template 2.14;
  22         169964  
  22         1383  
8 22     22   37128 use Getopt::Long;
  22         359429  
  22         220  
9              
10             our $VERSION = '0.30'; # VERSION
11              
12 22     22   28718 use Jemplate::Parser;
  22         114  
  22         78254  
13              
14             #-------------------------------------------------------------------------------
15             sub usage {
16 0     0 0 0 <<'...';
17             Usage:
18              
19             jemplate --runtime [runtime-opt]
20              
21             jemplate --compile [compile-opt]
22              
23             jemplate --runtime [runtime-opt] --compile [compile-opt]
24              
25             jemplate --list
26              
27             Where "--runtime" and "runtime-opt" can include:
28              
29             --runtime Equivalent to --ajax=ilinsky --json=json2
30             --runtime=standard
31              
32             --runtime=lite Same as --ajax=none --json=none
33             --runtime=jquery Same as --ajax=jquery --json=none
34             --runtime=yui Same as --ajax=yui --json=yui
35             --runtime=legacy Same as --ajax=gregory --json=json2
36              
37             --json By itself, equivalent to --json=json2
38             --json=json2 Include http://www.json.org/json2.js for parsing/stringifying
39             --json=yui Use YUI: YAHOO.lang.JSON (requires external YUI)
40             --json=none Doesn't provide any JSON functionality except a warning
41              
42             --ajax By itself, equivalent to --ajax=xhr
43             --ajax=jquery Use jQuery for Ajax get and post (requires external jQuery)
44             --ajax=yui Use YUI: yui/connection/connection.js (requires external YUI)
45             --ajax=xhr Use XMLHttpRequest (will automatically use --xhr=ilinsky if --xhr is not set)
46             --ajax=none Doesn't provide any Ajax functionality except a warning
47              
48             --xhr By itself, equivalent to --xhr=ilinsky
49             --xhr=ilinsky Include http://code.google.com/p/xmlhttprequest/
50             --xhr=gregory Include http://www.scss.com.au/family/andrew/webdesign/xmlhttprequest/
51              
52             --xxx Include XXX and JJJ helper functions
53              
54             --compact Use the YUICompressor compacted version of the runtime
55              
56             Where "compile-opt" can include:
57              
58             --start-tag
59             --end-tag
60             --pre-chomp
61             --post-chomp
62             --trim
63             --any-case
64             --eval
65             --noeval
66             -s, --source
67             --exclude
68              
69             For more information use:
70             perldoc jemplate
71             ...
72             }
73              
74             sub main {
75 3     3 0 6 my $class = shift;
76              
77 3         7 my @argv = @_;
78              
79 3         11 my ($template_options, $jemplate_options) = get_options(@argv);
80 3         13 my ($runtime, $compile, $list) = @$jemplate_options{qw/runtime compile list/};
81              
82 3 50       7 if ($runtime) {
83 0         0 print runtime_source_code(@$jemplate_options{qw/runtime ajax json xhr xxx compact/});
84 0 0       0 return unless $compile;
85             }
86              
87 3         11 my $templates = make_file_list($jemplate_options->{exclude}, @argv);
88 3 50       10 print_usage_and_exit() unless @$templates;
89              
90 3 50       6 if ($list) {
91 3         6 foreach (@$templates) {
92 12         46 print STDOUT $_->{short} . "\n";
93             }
94 3         46 return;
95             }
96              
97 0 0       0 if ($compile) {
98 0         0 my $jemplate = Jemplate->new(%$template_options);
99 0         0 print STDOUT $jemplate->_preamble;
100 0         0 foreach my $template (@$templates) {
101 0         0 my $content = slurp($template->{full});
102 0 0       0 if ($content) {
103 0         0 print STDOUT $jemplate->compile_template_content(
104             $content,
105             $template->{short},
106             );
107             }
108             }
109 0         0 return;
110             }
111              
112 0         0 print_usage_and_exit();
113             }
114              
115             sub get_options {
116 6     6 0 2141 local @ARGV = @_;
117              
118 6         11 my $runtime;
119 6         10 my $compile = 0;
120 6         10 my $list = 0;
121              
122 6 100       28 my $start_tag = exists $ENV{JEMPLATE_START_TAG}
123             ? $ENV{JEMPLATE_START_TAG}
124             : undef;
125 6 100       22 my $end_tag = exists $ENV{JEMPLATE_END_TAG}
126             ? $ENV{JEMPLATE_END_TAG}
127             : undef;
128 6 50       18 my $pre_chomp = exists $ENV{JEMPLATE_PRE_CHOMP}
129             ? $ENV{JEMPLATE_PRE_CHOMP}
130             : undef;
131 6 50       17 my $post_chomp = exists $ENV{JEMPLATE_POST_CHOMP}
132             ? $ENV{JEMPLATE_POST_CHOMP}
133             : undef;
134 6 50       16 my $trim = exists $ENV{JEMPLATE_TRIM}
135             ? $ENV{JEMPLATE_TRIM}
136             : undef;
137 6 50       19 my $anycase = exists $ENV{JEMPLATE_ANYCASE}
138             ? $ENV{JEMPLATE_ANYCASE}
139             : undef;
140 6 50       18 my $eval_javascript = exists $ENV{JEMPLATE_EVAL_JAVASCRIPT}
141             ? $ENV{JEMPLATE_EVAL_JAVASCRIPT}
142             : 1;
143              
144 6         9 my $source = 0;
145 6         11 my $exclude = 0;
146 6         10 my ($ajax, $json, $xxx, $xhr, $compact, $minify);
147              
148 6         8 my $help = 0;
149              
150 6 50       56 GetOptions(
151             "compile|c" => \$compile,
152             "list|l" => \$list,
153             "runtime|r:s" => \$runtime,
154              
155             "start-tag=s" => \$start_tag,
156             "end-tag=s" => \$end_tag,
157             "trim=s" => \$trim,
158             "pre-chomp" => \$pre_chomp,
159             "post-chomp" => \$post_chomp,
160             "any-case" => \$anycase,
161             "eval!" => \$eval_javascript,
162              
163             "source|s" => \$source,
164             "exclude=s" => \$exclude,
165              
166             "ajax:s" => \$ajax,
167             "json:s" => \$json,
168             "xxx" => \$xxx,
169             "xhr:s" => \$xhr,
170              
171             "compact" => \$compact,
172             "minify:s" => \$minify,
173              
174             "help|?" => \$help,
175             ) or print_usage_and_exit();
176              
177 6 50       8083 if ($help) {
178 0         0 print_usage_and_exit();
179             }
180              
181 6 50 33     12 ($runtime, $ajax, $json, $xxx, $xhr, $minify) = map { defined $_ && ! length $_ ? 1 : $_ } ($runtime, $ajax, $json, $xxx, $xhr, $minify);
  36         105  
182 6 50 33     28 $runtime = "standard" if $runtime && $runtime eq 1;
183              
184 6 50 33     21 print_usage_and_exit("Don't understand '--runtime $runtime'") if defined $runtime && ! grep { $runtime =~ m/$_/ } qw/standard lite jquery yui legacy/;
  0         0  
185 6 50 33     30 print_usage_and_exit("Can't specify --list with a --runtime and/or the --compile option") if $list && ($runtime || $compile);
      66        
186 6 50 66     48 print_usage_and_exit() unless $list || $runtime || $compile;
      66        
187              
188 6 50       27 my $command =
    100          
    50          
189             $runtime ? 'runtime' :
190             $compile ? 'compile' :
191             $list ? 'list' :
192             print_usage_and_exit();
193              
194 6         14 my $options = {};
195 6 100       19 $options->{START_TAG} = $start_tag if defined $start_tag;
196 6 100       20 $options->{END_TAG} = $end_tag if defined $end_tag;
197 6 50       30 $options->{PRE_CHOMP} = $pre_chomp if defined $pre_chomp;
198 6 50       16 $options->{POST_CHOMP} = $post_chomp if defined $post_chomp;
199 6 50       15 $options->{TRIM} = $trim if defined $trim;
200 6 50       15 $options->{ANYCASE} = $anycase if defined $anycase;
201 6 50       23 $options->{EVAL_JAVASCRIPT} = $eval_javascript if defined $eval_javascript;
202              
203             return (
204 6         80 $options,
205             { compile => $compile, runtime => $runtime, list => $list,
206             source => $source,
207             exclude => $exclude,
208             ajax => $ajax, json => $json, xxx => $xxx, xhr => $xhr,
209             compact => $compact, minify => $minify },
210             );
211             }
212              
213              
214             sub slurp {
215 0     0 0 0 my $filepath = shift;
216 0 0       0 open(F, '<', $filepath) or die "Can't open '$filepath' for input:\n$!";
217 0         0 my $contents = do {local $/; };
  0         0  
  0         0  
218 0         0 close(F);
219 0         0 return $contents;
220             }
221              
222             sub recurse_dir {
223 3     3 0 2508 require File::Find::Rule;
224              
225 3         19409 my $dir = shift;
226 3         7 my @files;
227 3         106 foreach ( File::Find::Rule->file->in( $dir ) ) {
228 10 50       3273 if ( m{/\.[^\.]+} ) {} # Skip ".hidden" files or directories
229             else {
230 10         24 push @files, $_;
231             }
232             }
233 3         30 return @files;
234             }
235              
236             sub make_file_list {
237 3     3 0 7 my ($exclude, @args) = @_;
238              
239 3         9 my @list;
240              
241 3         7 foreach my $arg (@args) {
242 8 100       122 unless (-e $arg) { next; } # file exists
  3         5  
243 5 50 33     61 unless (-s $arg or -d $arg) { next; } # file size > 0 or directory (for Win platform)
  0         0  
244 5 50 33     12 if ($exclude and $arg =~ m/$exclude/) { next; } # file matches exclude regex
  0         0  
245              
246 5 100       120 if (-d $arg) {
247 2         8 foreach my $full ( recurse_dir($arg) ) {
248 9         80 $full =~ /$arg(\/|)(.*)/;
249 9         15 my $short = $2;
250 9         38 push(@list, {full=>$full, short=>$short} );
251             }
252             }
253             else {
254 3         6 my $full = $arg;
255 3         5 my $short = $full;
256 3         10 $short =~ s/.*[\/\\]//;
257 3         12 push(@list, {full=>$arg, short=>$short} );
258             }
259             }
260              
261 3         17 return [ sort { $a->{short} cmp $b->{short} } @list ];
  13         34  
262             }
263              
264             sub print_usage_and_exit {
265 0 0   0 0 0 print STDOUT join "\n", "", @_, "Aborting!", "\n" if @_;
266 0         0 print STDOUT usage();
267 0         0 exit;
268             }
269              
270             sub runtime_source_code {
271 0     0 0 0 require Jemplate::Runtime;
272 0         0 require Jemplate::Runtime::Compact;
273              
274 0 0       0 unshift @_, "standard" unless @_;
275              
276 0 0       0 my ($runtime, $ajax, $json, $xhr, $xxx, $compact) = map { defined $_ ? lc $_ : "" } @_[0 .. 5];
  0         0  
277              
278 0 0       0 my $Jemplate_Runtime = $compact ? "Jemplate::Runtime::Compact" : "Jemplate::Runtime";
279              
280 0 0       0 if ($runtime eq "standard") {
    0          
    0          
    0          
    0          
281 0   0     0 $ajax ||= "xhr";
282 0   0     0 $json ||= "json2";
283 0   0     0 $xhr ||= "ilinsky";
284             }
285             elsif ($runtime eq "jquery") {
286 0   0     0 $ajax ||= "jquery";
287             }
288             elsif ($runtime eq "yui") {
289 0   0     0 $ajax ||= "yui";
290 0   0     0 $json ||= "yui";
291             }
292             elsif ($runtime eq "legacy") {
293 0   0     0 $ajax ||= "xhr";
294 0   0     0 $json ||= "json2";
295 0   0     0 $xhr ||= "gregory";
296 0         0 $xxx = 1;
297             }
298             elsif ($runtime eq "lite") {
299             }
300              
301 0 0       0 $ajax = "xhr" if $ajax eq 1;
302 0 0 0     0 $xhr ||= 1 if $ajax eq "xhr";
303 0 0       0 $json = "json2" if $json eq 1;
304 0 0       0 $xhr = "ilinsky" if $xhr eq 1;
305              
306 0         0 my @runtime;
307              
308 0 0       0 push @runtime, $Jemplate_Runtime->kernel if $runtime;
309              
310 0 0       0 push @runtime, $Jemplate_Runtime->json2 if $json =~ m/^json2?$/i;
311              
312 0 0       0 push @runtime, $Jemplate_Runtime->ajax_xhr if $ajax eq "xhr";
313 0 0       0 push @runtime, $Jemplate_Runtime->ajax_jquery if $ajax eq "jquery";
314 0 0       0 push @runtime, $Jemplate_Runtime->ajax_yui if $ajax eq "yui";
315              
316 0 0       0 push @runtime, $Jemplate_Runtime->json_json2 if $json =~ m/^json2?$/i;
317 0 0       0 push @runtime, $Jemplate_Runtime->json_json2_internal if $json =~ m/^json2?[_-]?internal$/i;
318 0 0       0 push @runtime, $Jemplate_Runtime->json_yui if $json eq "yui";
319              
320 0 0       0 push @runtime, $Jemplate_Runtime->xhr_ilinsky if $xhr eq "ilinsky";
321 0 0       0 push @runtime, $Jemplate_Runtime->xhr_gregory if $xhr eq "gregory";
322              
323 0 0       0 push @runtime, $Jemplate_Runtime->xxx if $xxx;
324              
325 0         0 return join ";", @runtime;
326             }
327              
328             #-------------------------------------------------------------------------------
329              
330             sub new {
331 6     6 0 1128 my $class = shift;
332 6         47 return bless { @_ }, $class;
333             }
334              
335             sub compile_module {
336 0     0 1 0 my ($self, $module_path, $template_file_paths) = @_;
337 0 0       0 my $result = $self->compile_template_files(@$template_file_paths)
338             or return;
339 0 0       0 open MODULE, "> $module_path"
340             or die "Can't open '$module_path' for output:\n$!";
341 0         0 print MODULE $result;
342 0         0 close MODULE;
343 0         0 return 1;
344             }
345              
346             sub compile_module_cached {
347 0     0 1 0 my ($self, $module_path, $template_file_paths) = @_;
348 0         0 my $m = -M $module_path;
349 0 0       0 return 0 unless grep { -M($_) < $m } @$template_file_paths;
  0         0  
350 0         0 return $self->compile_module($module_path, $template_file_paths);
351             }
352              
353             sub compile_template_files {
354 0     0 1 0 my $self = shift;
355 0         0 my $output = $self->_preamble;
356 0         0 for my $filepath (@_) {
357 0         0 my $filename = $filepath;
358 0         0 $filename =~ s/.*[\/\\]//;
359 0 0       0 open FILE, $filepath
360             or die "Can't open '$filepath' for input:\n$!";
361 0         0 my $template_input = do {local $/; };
  0         0  
  0         0  
362 0         0 close FILE;
363 0         0 $output .=
364             $self->compile_template_content($template_input, $filename);
365             }
366 0         0 return $output;
367             }
368              
369             sub compile_template_content {
370 15 50   15 1 139 die "Invalid arguments in call to Jemplate->compile_template_content"
371             unless @_ == 3;
372 15         37 my ($self, $template_content, $template_name) = @_;
373 15 100       154 my $parser = Jemplate::Parser->new( ref($self) ? %$self : () );
374 15 100       113 my $parse_tree = $parser->parse(
375             $template_content, {name => $template_name}
376             ) or die $parser->error;
377 14         1152 my $output =
378             "Jemplate.templateMap['$template_name'] = " .
379             $parse_tree->{BLOCK} .
380             "\n";
381 14         26 for my $function_name (sort keys %{$parse_tree->{DEFBLOCKS}}) {
  14         77  
382 2         13 $output .=
383             "Jemplate.templateMap['$function_name'] = " .
384             $parse_tree->{DEFBLOCKS}{$function_name} .
385             "\n";
386             }
387 14         253 return $output;
388             }
389              
390             sub _preamble {
391 0     0     return <<'...';
392             /*
393             This JavaScript code was generated by Jemplate, the JavaScript
394             Template Toolkit. Any changes made to this file will be lost the next
395             time the templates are compiled.
396              
397             Copyright 2006-2014 - Ingy döt Net - All rights reserved.
398             */
399              
400             var Jemplate;
401             if (typeof(exports) == 'object') {
402             Jemplate = require("jemplate").Jemplate;
403             }
404              
405             if (typeof(Jemplate) == 'undefined')
406             throw('Jemplate.js must be loaded before any Jemplate template files');
407              
408             ...
409             }
410              
411             1;
412              
413             __END__