File Coverage

blib/lib/DTL/Fast.pm
Criterion Covered Total %
statement 116 117 99.1
branch 21 38 55.2
condition 26 64 40.6
subroutine 17 17 100.0
pod 8 9 88.8
total 188 245 76.7


line stmt bran cond sub pod time code
1             package DTL::Fast;
2 98     98   1398907 use strict; use warnings FATAL => 'all';
  98     98   179  
  98         2661  
  98         341  
  98         123  
  98         3747  
3 98     98   342 use Exporter 'import';
  98         113  
  98         3304  
4 98     98   338 use Digest::MD5 qw(md5_hex);
  98         132  
  98         5167  
5              
6 98     98   1786 use 5.010;
  98         225  
7             our $VERSION = '1.623'; # ==> ALSO update the version in the pod text below!
8              
9             # loaded modules
10             our %TAG_HANDLERS;
11             our %FILTER_HANDLERS;
12             our %OPS_HANDLERS;
13              
14             # known but not loaded modules
15             our %KNOWN_TAGS; # plain map tag => module
16             our %KNOWN_SLUGS; # reversed module => tag
17             our %KNOWN_FILTERS; # plain map filter => module
18             our %KNOWN_OPS; # complex map priority => operator => module
19             our %KNOWN_OPS_PLAIN; # plain map operator => module
20             our @OPS_RE = ();
21              
22             # modules hash to avoid duplicating on deserializing
23             our %LOADED_MODULES;
24              
25             require XSLoader;
26             XSLoader::load('DTL::Fast', $VERSION);
27              
28             our $RUNTIME_CACHE;
29              
30             our @EXPORT_OK = qw(
31             count_lines
32             get_template
33             select_template
34             register_tag
35             preload_operators
36             register_operator
37             preload_tags
38             register_filter
39             preload_filters
40             );
41              
42             sub get_template
43             {
44 92     92 1 28345 my( $template_name, %kwargs ) = @_;
45            
46 92 50       217 die "Template name was not specified"
47             if not $template_name;
48              
49             die "Template directories array was not specified"
50             if
51             not defined $kwargs{'dirs'}
52             or ref $kwargs{'dirs'} ne 'ARRAY'
53 92 50 33     571 or not scalar @{$kwargs{'dirs'}}
  92   33     301  
54             ;
55              
56 92         247 my $cache_key = _get_cache_key( $template_name, %kwargs );
57              
58 92         124 my $template;
59              
60 92   66     299 $RUNTIME_CACHE //= DTL::Fast::Cache::Runtime->new();
61              
62 92 100 66     452 if(
63             $kwargs{'no_cache'}
64             or not defined ( $template = $RUNTIME_CACHE->get($cache_key))
65             )
66             {
67 68         173 $template = read_template($template_name, %kwargs );
68            
69 55 100       95 if( defined $template )
70             {
71 53         299 $RUNTIME_CACHE->put($cache_key, $template);
72             }
73             else
74             {
75 2         4 die sprintf( <<'_EOT_', $template_name, join("\n", @{$kwargs{'dirs'}}));
  2         22  
76             Unable to find template %s in directories:
77             %s
78             _EOT_
79             }
80             }
81            
82 77         226 return $template;
83             }
84              
85             sub _get_cache_key
86             {
87 160     160   252 my( $template_name, %kwargs ) = @_;
88            
89             return md5_hex(
90             sprintf( '%s:%s:%s:%s'
91             , __PACKAGE__
92             , $template_name
93 160         343 , join( ',', @{$kwargs{'dirs'}} )
94 160   100     194 , join( ',', @{$kwargs{'ssi_dirs'}//[]})
  160         1417  
95             # shouldn't we pass uri_handler here?
96             )
97             )
98             ;
99             }
100              
101             sub read_template
102             {
103 68     68 0 125 my( $template_name, %kwargs ) = @_;
104            
105 68         104 my $template = undef;
106 68         68 my $template_path = undef;
107              
108             die "Template directories array was not specified"
109             if not defined $kwargs{'dirs'}
110             or not ref $kwargs{'dirs'}
111 68 50 33     373 or not scalar @{$kwargs{'dirs'}}
  68   33     166  
112             ;
113            
114 68         148 my $cache_key = _get_cache_key( $template_name, %kwargs );
115              
116 68 0 33     410 if(
      33        
      33        
      0        
117             $kwargs{'no_cache'}
118             or not exists $kwargs{'cache'}
119             or not $kwargs{'cache'}
120             or not $kwargs{'cache'}->isa('DTL::Fast::Cache')
121             or not defined ($template = $kwargs{'cache'}->get($cache_key))
122             )
123             {
124 68         177 ($template, $template_path) = _read_file($template_name, $kwargs{'dirs'});
125            
126 68 100       198 if( defined $template )
127             {
128 66         125 $kwargs{'file_path'} = $template_path;
129 66         472 $template = DTL::Fast::Template->new( $template, %kwargs);
130            
131             $kwargs{'cache'}->put( $cache_key, $template )
132             if
133             defined $template
134             and exists $kwargs{'cache'}
135             and $kwargs{'cache'}
136 53 0 33     240 and $kwargs{'cache'}->isa('DTL::Fast::Cache')
      33        
      0        
137             ;
138             }
139             }
140            
141 55 100       109 if( defined $template )
142             {
143 53 50       109 $template->{'cache'} = $kwargs{'cache'} if $kwargs{'cache'};
144 53 50       101 $template->{'url_source'} = $kwargs{'url_source'} if $kwargs{'url_source'};
145             }
146            
147 55         110 return $template;
148             }
149              
150             sub _read_file
151             {
152 68     68   83 my $template_name = shift;
153 68         82 my $dirs = shift;
154 68         62 my $template;
155             my $template_path;
156            
157 68         140 foreach my $dir (@$dirs)
158             {
159 73         294 $dir =~ s/[\/\\]+$//xgsi;
160 73         190 $template_path = sprintf '%s/%s', $dir, $template_name;
161 73 50 66     2548 if(
      66        
162             -e $template_path
163             and -f $template_path
164             and -r $template_path
165             )
166             {
167 66         143 $template = __read_file( $template_path );
168 66         132 last;
169             }
170             }
171            
172 68         180 return ($template, $template_path);
173             }
174              
175              
176             sub __read_file
177             {
178 68     68   91 my( $file_name ) = @_;
179 68         69 my $result;
180            
181 68 50       2124 if( open my $IF, '<', $file_name )
182             {
183 68         287 local $/ = undef;
184 68         1308 $result = <$IF>;
185 68         546 close $IF;
186             }
187             else
188             {
189 0         0 die sprintf(
190             'Error opening file %s, %s'
191             , $file_name
192             , $!
193             );
194             }
195 68         260 return $result;
196             }
197              
198             # result should be cached with full list of params
199             sub select_template
200             {
201 2     2 1 7 my( $template_names, %kwargs ) = @_;
202            
203 2 50 33     20 die "First parameter must be a template names array reference"
      33        
204             if(
205             not ref $template_names
206             or ref $template_names ne 'ARRAY'
207             or not scalar @$template_names
208             );
209              
210 2         2 my $result = undef;
211            
212 2         4 foreach my $template_name (@$template_names)
213             {
214 2 50       5 if( ref ( $result = get_template( $template_name, %kwargs )) eq 'DTL::Fast::Template' )
215             {
216 2         3 last;
217             }
218             }
219            
220 2         5 return $result;
221             }
222              
223             # registering tag as known
224             sub register_tag
225             {
226 196     196 1 1814 my( %tags ) = @_;
227            
228 196         767 while( my( $slug, $module) = each %tags )
229             {
230 2940         3007 $DTL::Fast::KNOWN_TAGS{lc($slug)} = $module;
231 2940         6300 $DTL::Fast::KNOWN_SLUGS{$module} = $slug;
232             }
233            
234 196         569 return;
235             }
236              
237             # registering tag as known
238             sub preload_tags
239             {
240 1     1 1 559 require Module::Load;
241            
242 1         899 while( my( $keyword, $module) = each %KNOWN_TAGS )
243             {
244 30         109 Module::Load::load($module);
245 30         469 $LOADED_MODULES{$module} = time;
246 30 50 33     301 delete $TAG_HANDLERS{$keyword} if exists $TAG_HANDLERS{$keyword} and $TAG_HANDLERS{$keyword} ne $module;
247             }
248            
249 1         5 return 1;
250             }
251              
252              
253             # registering filter as known
254             sub register_filter
255             {
256 196     196 1 3322 my( %filters ) = @_;
257            
258 196         770 while( my( $slug, $module) = each %filters )
259             {
260 5880         4739 $DTL::Fast::KNOWN_FILTERS{$slug} = $module;
261 5880 50 33     12957 delete $FILTER_HANDLERS{$slug} if exists $FILTER_HANDLERS{$slug} and $FILTER_HANDLERS{$slug} ne $module;
262             }
263            
264 196         673 return;
265             }
266              
267             sub preload_filters
268             {
269 1     1 1 5 require Module::Load;
270            
271 1         7 while( my( undef, $module) = each %KNOWN_FILTERS )
272             {
273 60         135 Module::Load::load($module);
274 60         1080 $LOADED_MODULES{$module} = time;
275             }
276            
277 1         6 return 1;
278             }
279              
280             # invoke with parameters:
281             #
282             # '=' => [ priority, module ]
283             #
284             sub register_operator
285             {
286 98     98 1 1031 my %ops = @_;
287            
288 98         196 my %recompile = ();
289 98         537 foreach my $operator (keys %ops)
290             {
291 2058         1333 my($priority, $module) = @{$ops{$operator}};
  2058         2143  
292            
293 2058 50       4204 die "Operator priority must be a number from 0 to 8"
294             if $priority !~ /^[012345678]$/;
295            
296 2058   100     4095 $KNOWN_OPS{$priority} //= {};
297 2058         2054 $KNOWN_OPS{$priority}->{$operator} = $module;
298 2058         1547 $recompile{$priority} = 1;
299 2058         1757 $KNOWN_OPS_PLAIN{$operator} = $module;
300 2058 50 33     3818 delete $OPS_HANDLERS{$operator} if exists $OPS_HANDLERS{$operator} and $OPS_HANDLERS{$operator} ne $module;
301             }
302            
303 98         469 foreach my $priority (keys(%recompile))
304             {
305 882         691 my @ops = sort{ length $b <=> length $a } keys(%{$KNOWN_OPS{$priority}});
  2005         1991  
  882         2075  
306 882         928 my $ops = join '|', map{ "\Q$_\E" } @ops;
  2058         2542  
307 882         1870 $OPS_RE[$priority] = $ops;
308             }
309             }
310              
311              
312             sub preload_operators
313             {
314 1     1 1 7 require Module::Load;
315            
316 1         8 while( my( undef, $module) = each %KNOWN_OPS_PLAIN )
317             {
318 21         46 Module::Load::load($module);
319 21         560 $LOADED_MODULES{$module} = time;
320             }
321            
322 1         9 return 1;
323             }
324              
325              
326             require DTL::Fast::Template;
327             require DTL::Fast::Cache::Runtime;
328              
329             1;
330              
331             __END__