File Coverage

blib/lib/Cake/View/TT.pm
Criterion Covered Total %
statement 114 158 72.1
branch 23 42 54.7
condition 8 25 32.0
subroutine 13 15 86.6
pod 0 10 0.0
total 158 250 63.2


line stmt bran cond sub pod time code
1             package Cake::View::TT;
2 1     1   5 use strict;
  1         2  
  1         30  
3 1     1   5 use warnings;
  1         3  
  1         24  
4 1     1   6 use Carp;
  1         1  
  1         65  
5 1     1   6 use Encode;
  1         2  
  1         2493  
6             #=======================================================================
7             # Global REGex
8             #=======================================================================
9             my $L = "[%";
10             my $T = "%]";
11             my $PRE = qr/\Q$L/;
12             my $POST = qr/\Q$T/;
13            
14             my $CODE = qr{
15             $PRE
16             \s* # optional leading whitespace
17             CODE # required BLOCK token
18             \s* # optional whitespace
19             $POST
20             (.*?) # grab block content
21             $PRE
22             \s*
23             END
24             \s*
25             $POST
26             }xs;
27            
28             my $VAR = qr {
29             $PRE
30             \s*
31             (.*?)
32             \s*
33             $POST
34             }xs;
35            
36             my $REP = qr {
37             \{\{
38             \s
39             (\d+)
40             \s
41             \}\}
42             }x;
43            
44             my $PROCESS = qr{
45             ($PRE
46             \s*
47             PROCESS
48             \s+
49             .*?
50             \s*
51             $POST)
52             }x;
53            
54             my $PROCESS_URL = qr{
55             $PRE
56             \s*
57             PROCESS
58             \s+
59             (.*?)
60             \s*
61             $POST
62             }x;
63            
64             my $MAIN = qr {
65             $PRE
66             \s*
67             main
68             \s*
69             $POST
70             }x;
71            
72             my $INCLUDE = qr{
73             ($PRE
74             \s*
75             INCLUDE
76             \s+
77             .*?
78             \s*
79             $POST)
80             }x;
81            
82             my $INCLUDE_URL = qr{
83             $PRE
84             \s*
85             INCLUDE
86             \s+
87             (.*?)
88             \s*
89             $POST
90             }x;
91            
92             my $SETTINGS = qr{
93             ($PRE
94             \s* # optional leading whitespace
95             SETTINGS # required SETTINGS token
96             \s* # optional whitespace
97             $POST
98             .*? # grab block content
99             $PRE
100             \s*
101             END
102             \s*
103             $POST)
104             }xs;
105            
106             my $SETTINGS_CONTENT = qr{
107             $PRE
108             \s* # optional leading whitespace
109             SETTINGS # required SETTINGS token
110             \s* # optional whitespace
111             $POST
112             (.*?) # grab block content
113             $PRE
114             \s*
115             END
116             \s*
117             $POST
118             }xs;
119            
120             =head1 Name
121            
122             Cake::View::TT
123            
124             =cut
125            
126             =head1 SYNOPSIS
127            
128             use Cake::View::TT;
129            
130             my $temp = Cake::View::TT->new({
131             path => '/some/global/path/to/template/folder',
132             layout => 'layout.tmpl'
133             });
134            
135             $temp->render('file.tmpl',{
136             fname => 'Mamod',
137             lname => 'Mehyar',
138             options => ['opt1','opt2','opt3'],
139             nested => {
140             test => 'something',
141             another => []
142             }
143             });
144            
145             =cut
146            
147             my $DEBUG = 0;
148 3     3 0 12 sub DEBUG {$DEBUG};
149            
150             sub new {
151            
152 1     1 0 2 my $class = shift;
153 1         2 my $options = shift;
154 1 50       4 croak "You have to specify full path of your templat files location"
155             if !$options->{path};
156            
157 1         2 $DEBUG = $options->{DEBUG};
158            
159 1         5 my $self = bless({
160             path => $options->{path},
161             layout => $options->{layout},
162             },$class);
163            
164 1         4 return $self;
165             }
166            
167             sub render {
168 1     1 0 3 my $self = shift;
169 1         2 my $file = shift;
170 1         1 my $data = shift;
171 1         3 $data->{me} = $self;
172            
173 1         11 $self->{obj} = bless($data,'Cake::View::Object');
174 1         4 my @matches = $self->loadMatches($file);
175 1         2 my $temp = $self->{temp};
176            
177 1         4 foreach my $match (@matches){
178 3         5 my $perl = '';
179 3         5 my $content = $match->{content};
180            
181 3         22 $self->{current} = {
182             pos => $match->{start},
183             index => $match->{index},
184             file => $match->{file},
185             content => $match->{content}
186             };
187            
188 3 50       13 if ($match->{from} eq 'VAR'){
189 3         7 $content =~ s/\n+//g;
190 3         12 my $return = $self->setVar("$content","eval");
191 3 100       14 if ($return){
192 2         6 $self->pushData($return);
193             }
194             } else {
195 0         0 $content =~ s/print\s+(.+?)\s*;/$self->_varsFromPrint($1)/ges;
  0         0  
196 0         0 $content =~ s/$VAR/\$self->setVar("$1","eval")/g;
197 0         0 eval $content;
198             }
199            
200             ##better error handling
201 3 0 33     8 if (DEBUG && $@){
202 0         0 die {
203             content => $self->{current}->{content},
204             file => $self->{current}->{file},
205             pos => $self->{current}->{pos},
206             message => $@
207             }
208             }
209             }
210            
211 1         81 while (my ($id) = $temp =~ m{$REP}){
212 3         4 my $replace;
213 3 100       11 if (my $data = $self->{data}->{$id}){
214 2         3 $replace = join '', @{$data};
  2         6  
215             } else {
216 1         3 $replace = '';
217             }
218            
219 3         11 my $length = $+[0] - $-[0];
220 3         33 substr ($temp,$-[0],$length,$replace);
221             }
222            
223 1         6 return $temp;
224 0         0 $self->{temp} = $temp;
225 0         0 $temp =~ s/\t+//g;
226 0         0 return $self;
227            
228             }
229            
230             sub _varsFromPrint {
231 0     0   0 my $self = shift;
232 0         0 my $data = shift;
233 0         0 $data =~ s/\n\s*//g;
234 0         0 $data =~ s/(['"])\./$1,/g;
235            
236 0         0 my @captures;
237 0         0 while ($data =~ s/((['"])(?:(?:.*?)*)$VAR(?:(?:.*?)*)\2)/\{\{POS\}\}/){
238 0         0 my $capture = $1;
239 0         0 my $ter = $2;
240 0         0 $capture =~ s/$PRE/$ter,\$self->setVar($ter/g;
241 0         0 $capture =~ s/$POST/$ter,"eval"),$ter/g;
242 0         0 push @captures, $capture;
243             };
244            
245 0         0 map {
246 0         0 $data =~ s/\{\{POS\}\}/$_/;
247             } @captures;
248            
249 0         0 return '$self->pushData('.($data).');';
250             }
251            
252            
253             sub loadMatches {
254 1     1 0 2 my $self = shift;
255 1         17 my $file = shift;
256 1         5 my $temp = $self->load($file);
257 1 50       5 if (my $layout = $self->{layout}){
258 0         0 $layout = $self->load($layout);
259 0         0 $layout =~ s/\{\{ main \}\}/$temp/g;
260 0         0 $temp = $layout;
261             }
262            
263 1         2 $self->{temp} = $temp;
264 1 50       4 return ref $self->{matches} eq 'ARRAY' ? @{$self->{matches}} : ();
  1         3  
265             }
266            
267             sub load {
268 1     1 0 3 my ($self,$file) = @_;
269 1         2 my ($data);
270 1         3 $file = $self->{path}."/$file";
271            
272 1 50       61 if (open(my $fh,'<',$file)) {
273 1         2 $data = do { local $/; <$fh> };
  1         4  
  1         29  
274 1         10 close($fh);
275 1         7 $data = Encode::decode_utf8($data);
276            
277 1         70 my @files;
278 1         10 $data =~ s/$MAIN/{{ main }}/g;
279 1         3 my $counter = 0;
280 1         89 while (my ($settings,$include,$process,$code,$var) = $data =~ m{$SETTINGS|$INCLUDE|$PROCESS|$CODE|$VAR}){
281 4         5 $counter++;
282 4 50       9 if ($counter > 1000){
283 0         0 die "recrusive loop $process";
284             }
285            
286 4         18 my $length = $+[0] - $-[0];
287 4         9 my $start = $-[0];
288 4 100 66     29 if ($code || $var){
    50          
    50          
    0          
289 3   33     11 my $content = $code || $var;
290 3 50       3 push @{$self->{matches}},{
  3         39  
291             index => ++$self->{i},
292             content => $content,
293             start => $start,
294             from => $code ? 'CODE' : 'VAR',
295             file => $file
296             };
297 3         54 substr ($data,$start,$length,"{{ $self->{i} }}");
298             } elsif ($process){
299 0         0 my ($url) = $process =~ m/$PROCESS_URL/g;
300 0         0 substr ($data,$start,$length,$self->load($url));
301             } elsif($settings){
302 1         12 my ($content) = $settings =~ m/$SETTINGS_CONTENT/g;
303 1         4 substr ($data,$start,$length,$self->settings($content));
304             } elsif ($include){
305 0         0 my ($url) = $include =~ m/$INCLUDE_URL/g;
306 0         0 push @files,$url;
307 0         0 substr ($data,$start,$length,'{{% INC %}}');
308             }
309             }
310            
311 1         4 foreach my $f (@files){
312 0         0 $data =~ s{\{\{% INC %\}\}}{ { $self->load($f) }}e;
  0         0  
  0         0  
313             }
314            
315 1         6 return $data;
316             }
317            
318             else {
319 0         0 croak "Can't open file $file: $!";
320             }
321             }
322            
323             sub settings {
324 1     1 0 2 my $self = shift;
325 1         2 my $settings = shift;
326 1         6 my @settings = split "\n",$settings;
327 2         8 map {
328 1         2 my ($m,$m2) = $_ =~ m/\s*(.*?)\s*:\s*(.*)\s*/g;
329 2 100       8 if ($m){
330 1 50       3 if ($m eq 'layout'){
331 1         5 $self->{layout} = $m2;
332             }else {
333 0   0     0 $self->{obj}->{$m} = $self->setVar($m2,'eval') || $m2;
334             }
335             }
336             } @settings;
337 1         97 return '';
338             }
339            
340             sub pushData {
341 2     2 0 4 my $self = shift;
342 2         4 my $cur = $self->{cur};
343 2         3 push @{ $self->{data}->{$self->{current}->{index}} },@_;
  2         14  
344             }
345            
346             sub setVar {
347 3     3 0 6 my $self = shift;
348 3         4 my $content = shift;
349 3         3 my $eval = shift;
350 3         4 my $local = shift;
351            
352 3         5 my $perl = '';
353 3         22 my ($var,$val) = $content =~ m/\s*((?:\.*\w+(?:\(.*?\))*)+)(?:\s*=\s*(.*))*/;
354            
355 3 50       8 if ($val){
356             ##set value
357 0         0 my $var = $self->getVar($var);
358 0         0 my $val = $self->getVar($val);
359 0         0 $perl = "$var = $val;";
360 0 0 0     0 eval $perl and return undef if $eval;
361            
362             } else {
363 3         12 $content =~ s{(.*)}{$self->getVar($1)}e;
  3         9  
364 3         5 $perl = $content;
365             }
366            
367 3 50       207 $eval ? return eval $perl :
368             return $perl;
369             }
370            
371             sub getVar {
372            
373 3     3 0 4 my $self = shift;
374 3         8 my $var = shift;
375            
376 3 50 33     27 if ($var =~ /^\$/ || $var =~ /^\s*['"]/){
377 0         0 return $var;
378             }
379            
380 3         4 my @sp;
381             my $newsp;
382 3         17 while ($var =~ s/(?: (?: (\w+(?:\(.*?\))*) | \.(\(.*?\)) ) )//x){
383 6   33     22 push @sp,$1 || $2;
384 6   33     17 my $match = $1 || $2;
385            
386 6 100       20 if ($match =~ m/\(.*?\)$/){
387 3         14 $newsp .= '->'.$match;
388             } else {
389 3         25 $newsp .= '->{'.$match.'}';
390             }
391             }
392            
393 3         7 my $newvar = '$self->{obj}'.$newsp;
394 3   50     22 return $newvar || '';
395             }
396            
397             sub toHTML {
398 0     0 0   my $self = shift;
399 0           return join '', @{$self->{data}};
  0            
400             }
401            
402             package Cake::View::Object;
403            
404             1;
405            
406             __END__