File Coverage

blib/lib/HTML/Template/Preload.pm
Criterion Covered Total %
statement 37 39 94.8
branch n/a
condition n/a
subroutine 13 13 100.0
pod n/a
total 50 52 96.1


line stmt bran cond sub pod time code
1             package HTML::Template::Preload;
2             our $VERSION = '0.01';
3              
4             =head1 NAME
5              
6             HTML::Template::Preload - Preload HTML::Template templates into cache.
7              
8             =head1 SYNOPSIS
9              
10             Preload HTML::Template templates into cache:
11              
12             use HTML::Template::Preload qw(-path => '/some/path', cache => 1);
13              
14             =head1 DESCRIPTION
15              
16             HTML::Template supports the concept of a 'cache' which is use to hold
17             pre-parsed templates. At the same time, HTML::Template supports a
18             number of different types of caching mechanisms.
19              
20             In a Apache/ModPerl environment, there may be a small but significant
21             performance benefit having Apache pre-load the templates, so as to
22             avoid needing the Apache-child-instances parse the templates, as they
23             will have inherited the parent instance cache. To make this work, you
24             would call this module from your startup.pl in Apache/mod_perl.
25              
26             Thus this module pre-parses all the templates, then places them into
27             the selected cache.
28              
29             =head1 USAGE
30              
31             You can use this module in one of two ways, either:
32             a) In your 'use' statement, provide it with your
33             HTML::Template cache info, as in:
34              
35             use HTML::Template::Preload qw(
36             -path => '/some/path',
37             -path => 'some/other/path',
38             cache => 1,
39             );
40              
41             b) Or inside your program code:
42              
43             use HTML::Template::Preload;
44             ...
45             my %ht_options;
46             $ht_options{path} = \@search_paths;
47             $ht_options{global_vars} = 1;
48             $ht_options{strict} = 0;
49             $ht_options{cache} = 1;
50             ...
51             HTML::Template::Preload->preload(%ht_options);
52              
53             HTML::Template::Preload takes a hash of named arguments:
54              
55             -extension
56             The filename extension you use for all of your
57             templates. Defaults to: .tmpl
58              
59             -file Name a specific file or files to cache. Takes
60             a scalar or an array of filenames. This uses
61             the search path to find the template files; as
62             implemented by HTML::Template.
63              
64             -path Name a specific search path or paths. Takes a
65             scalar or an array of paths. This will usually
66             be the same list as would be passed to the 'path'
67             argument to HTML::Template.
68              
69             -function
70             sub's to functions that need to be registered
71             for HTML::Template::Expr.
72              
73             All other arguments (that dont begin with a '-') are passed
74             to the HTML::Template caching-instance.
75              
76             Note that you dont need to specify the "-xxx" variation of
77             these arguments -> you can simply use the same hash-options
78             as given to HTML::Template. The point of these extra options
79             is to allow for explicitly caching a specific template.
80              
81             =cut
82              
83 1     1   1740 use strict;
  1         2  
  1         43  
84 1     1   6 use warnings FATAL => 'all';
  1         2  
  1         41  
85 1     1   5 use utf8;
  1         1  
  1         5  
86 1     1   17 use Exporter;
  1         1  
  1         26  
87 1     1   5 use Carp;
  1         1  
  1         50  
88 1     1   487 use HTML::Template::ESCAPE;
  1         3  
  1         23  
89 1     1   584 use HTML::Template::ESCAPE::URL;
  1         2  
  1         22  
90 1     1   498 use HTML::Template::ESCAPE::JS;
  1         2  
  1         24  
91 1     1   439 use HTML::Template::ESCAPE::TEXT;
  1         3  
  1         24  
92 1     1   435 use HTML::Template::ESCAPE::STRIP_NEWLINE;
  1         2  
  1         25  
93 1     1   499 use HTML::Template::ESCAPE::DOUBLE_QUOTE;
  1         2  
  1         21  
94 1     1   1492 use HTML::Template;
  1         3  
  1         36  
95 1     1   710 use HTML::Template::Expr;
  0            
  0            
96             use HTML::Template::Filters;
97             use HTML::Template::Bundle;
98             use vars qw($DEBUG);
99             $DEBUG = 0;
100              
101             # Helper functions
102             {
103             sub left {
104             my ($string, $num) = @_;
105             $num = 1 unless defined $num;
106             return substr($string,0,$num);
107             }
108              
109             sub find {
110             my ($path, $regex_pattern) = @_;
111             unless (-d $path) {
112             if ($regex_pattern) {
113             return [ $path ] if ($path =~ /$regex_pattern/);
114             return undef;
115             } else {
116             return [ $path ];
117             }
118             }
119             return undef if ($path =~ /.*\.$/ or $path =~ /.*\.\.$/ );
120              
121             return undef unless (opendir(DIR, $path));
122             my @entries;
123             while ($_ = readdir(DIR)) {
124             push @entries, $_;
125             }
126             closedir(DIR);
127              
128             my @files;
129             foreach my $entry (@entries) {
130             my $files = find($path .'/'. $entry,$regex_pattern);
131             next unless $files;
132             push @files, @$files;
133             }
134             return \@files if (@files > 0);
135             return undef;
136             }
137              
138             sub strip_path {
139             my ($path,$files) = @_;
140             my @files;
141             foreach my $file (@$files) {
142             s/^$path\///;
143             push @files, $file;
144             }
145             return \@files if (@files > 0);
146             return undef;
147             }
148              
149             sub get_value {
150             my $val = shift;
151             $val =~ s/,$//;
152             if ($val =~ /^(?:
153             "([^"]*)" # double-quoted value
154             |
155             '([^']*)' # single-quoted value
156             )$/sx) {
157             $val = $1 ? $1 : $2 ? $2 : "";
158             }
159             return $val;
160             }
161              
162             our $DIE_FROM_CALLER = 0;
163              
164             sub die_from_caller {
165             if ($DEBUG) {
166             require Carp;
167             Carp::confess "Locale::MakePhrase detected an error:";
168             }
169             my $caller_count = 0;
170             while (1) {
171             $caller_count++;
172             my $caller = caller($caller_count);
173             last if (!defined $caller || $caller !~ /^HTML::Template/);
174             }
175             my ($caller,$file,$line) = caller($caller_count);
176             if (defined $caller) {
177             for (1..$DIE_FROM_CALLER) {
178             $caller_count++;
179             ($caller,$file,$line) = caller($caller_count);
180             last unless defined $caller;
181             }
182             }
183             $caller = "main" unless defined $caller;
184             $file = "(unknown)" unless defined $file;
185             $line = "(unknown)" unless defined $line;
186             my $msg = "Fatal: ". caller() ." detected an error in: $caller$/";
187             $msg .= "File: $file$/";
188             $msg .= "Line: $line$/";
189             @_ and $msg .= join (" ", @_) . $/;
190             die $msg;
191             }
192              
193             sub resolve_function {
194             my $val = shift;
195             return $val if (HTML::Template::reftype($val) eq 'CODE');
196             my $caller_count = 0;
197             while (1) {
198             $caller_count++;
199             my $caller = caller($caller_count);
200             last if (!defined $caller || $caller !~ /^HTML::Template/);
201             }
202             my $caller = caller($caller_count) || "main";
203             return "$caller->$val" if ($caller->can($val));
204             die_from_caller("Cannot find a reference to function: $val");
205             }
206             }
207              
208             # provide facility to allow use of module within 'use' statemtent
209             sub import {
210             return unless (@_ > 1);
211             my $pkg = shift;
212             my $extension = '.tmpl';
213             my @paths;
214             my @files;
215             my @expr_functions;
216             my %ht_options;
217              
218             # get arguments
219             foreach my $arg (@_) {
220             next unless $arg;
221             $arg =~ s/,$//;
222             next unless $arg;
223             croak "Incorrect syntax for '$arg'" unless (@_ > 1);
224             shift;
225             my $val = shift;
226             if (left($arg) eq '-') {
227             my $type = substr($arg,1);
228             my $ref = HTML::Template::reftype($val);
229              
230             if (left($type,9) eq 'extension') {
231             $extension = get_value($val);
232              
233             } elsif (left($type,4) eq 'path') {
234             if ($ref eq 'ARRAY') {
235             push @paths, @$val,
236             } else {
237             push @paths, get_value($val);
238             }
239              
240             } elsif (left($type,4) eq 'file') {
241             if ($ref eq 'ARRAY') {
242             push @files, @$val;
243             } else {
244             push @files, get_value($val);
245             }
246              
247             } elsif (left($type,) eq 'function') {
248             if ($ref eq 'ARRAY') {
249             foreach (@$val) {
250             push @expr_functions, resolve_function($_);
251             }
252             } else {
253             push @expr_functions, resolve_function($val);
254             }
255             } else {
256             croak "Unknown argument: $arg";
257             }
258             } else {
259             $ht_options{$arg} = $val;
260             }
261             }
262              
263             # register any Expr functions
264             foreach my $expr_func (@expr_functions) {
265             HTML::Template::Expr->register_function($expr_func);
266             }
267              
268             # Since we are trying to preload stuff, we need to make sure
269             # a cache option is actually enabled...
270             if (exists $ht_options{cache} or
271             exists $ht_options{share_cache} or
272             exists $ht_options{double_cache} or
273             exists $ht_options{blind_cache} or
274             exists $ht_options{file_cache} or
275             exists $ht_options{double_file_cache} ){
276             # If no files or paths specified, grab the paths from the H::T
277             # options, if we can...
278             if (@paths == 0 and @files == 0) {
279             exists $ht_options{path} and @paths = @{$ht_options{path}};
280             }
281            
282             # Lookup the files in the specified paths
283             if (@paths > 0) {
284             print STDERR "Preloading files from ". scalar(@paths) ." paths.\n" if $DEBUG > 1;
285             my $file_spec = undef;
286             if (defined $extension and length $extension) {
287             $file_spec = $extension .'$';
288             $file_spec =~ s/\./\\./g;
289             };
290             my $files;
291             foreach my $path (@paths) {
292             print STDERR "Preloading templates from: $path" if $DEBUG > 3;
293             $files = find ($path, $file_spec);
294             next unless $files;
295             $files = strip_path ($path, $files);
296             next unless $files;
297             push @files, @$files;
298             }
299             }
300            
301             # Try loading the templates into the cache
302             (@files == 0) and croak "Failed to load any templates.";
303             foreach my $file (@files) {
304             print STDERR "Preloading template: $file\n" if $DEBUG > 2;
305             eval {
306             my $ht = HTML::Template->new(
307             filename => $file,
308             %ht_options,
309             );
310             };
311             $@ and croak "Error caching file: $file.";
312             }
313             print STDERR "Preloaded ". scalar(@files) ." templates\n" if $DEBUG;
314              
315             } elsif (@expr_functions == 0) {
316             croak "Using $pkg, while not enabling any cache, is pointless.\nEither enable a cache, or disable HTML::Tempate::Preload.";
317             }
318              
319             }
320              
321             # Allow static function call
322             #sub preload { shift->import(@_) }
323             no warnings 'once';
324             *preload = *import;
325             use warnings 'once';
326              
327             1;
328             __END__