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