File Coverage

blib/lib/CGI/okTemplate.pm
Criterion Covered Total %
statement 129 142 90.8
branch 23 44 52.2
condition 4 9 44.4
subroutine 15 15 100.0
pod 3 3 100.0
total 174 213 81.6


line stmt bran cond sub pod time code
1             package CGI::okTemplate;
2              
3 1     1   22012 use 5.008004;
  1         4  
  1         34  
4 1     1   5 use strict;
  1         2  
  1         29  
5 1     1   4 use Carp;
  1         5  
  1         95  
6 1     1   825 use English;
  1         4121  
  1         5  
7 1     1   464 use warnings;
  1         2  
  1         25  
8 1     1   5 use File::Spec;
  1         2  
  1         1668  
9              
10             require Exporter;
11              
12             our @ISA = qw(Exporter);
13              
14             # Items to export into callers namespace by default. Note: do not export
15             # names by default without a very good reason. Use EXPORT_OK instead.
16             # Do not simply export all your public functions/methods/constants.
17              
18             # This allows declaration use CGI::okTemplate ':all';
19             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
20             # will save memory.
21             our %EXPORT_TAGS = ( 'all' => [ qw(
22            
23             ) ] );
24              
25             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
26              
27             our @EXPORT = qw(
28            
29             );
30              
31             our $VERSION = '0.03';
32              
33             our $debug=0;
34              
35             # Preloaded methods go here.
36              
37             sub new {
38 1     1 1 539 my $class = shift;
39 1         2 my $self = {};
40              
41 1         3 bless($self,$class);
42 1         7 $self->{___params} = {@_};
43 1 50       4 $debug = 1 if $self->{___params}->{Debug};
44 1 50       5 $self->{___params}->{BlockTag} = 'TemplateBlock' unless $self->{___params}->{BlockTag};
45 1 50       4 if($self->{___params}->{RootDir}) {
46 0         0 $self->{___params}->{RootDir} = File::Spec->rel2abs($self->{___params}->{RootDir});
47             } else {
48 1         61 $self->{___params}->{RootDir} = File::Spec->rel2abs(File::Spec->curdir());
49             }
50 1         7 $self->{___params}->{RootDir} = ___clean_up_dirs(File::Spec->canonpath($self->{___params}->{RootDir}));
51 1 50       20 confess "Root Dir '$self->{___params}->{RootDir}' does not exists\n" unless (-d $self->{___params}->{RootDir});
52 1 50       5 $self->read_template($self->{___params}->{File}) if($self->{___params}->{File});
53              
54 1         6 return $self;
55             }
56              
57             sub read_template {
58 1     1 1 1 my $self = shift;
59 1         2 my $file = shift;
60              
61 1 50       5 unless($file) {
62 0         0 confess "Parameter 'File' undefined in function 'read_template.'\n";
63             }
64              
65 1 50       8 unless(File::Spec->file_name_is_absolute( $file )) { # make absolute path to file
66 1         14 $file = File::Spec->catfile($self->{___params}->{RootDir},$file);
67             }
68              
69 1         7 $file = ___clean_up_dirs(File::Spec->canonpath($file)); # delete up dirs from path
70 1 50       24 confess "File '$file' does not exists\n" unless (-r $file);
71              
72 1 50       4 unless(___under_root($self->{___params}->{RootDir},$file)) {
73 0         0 confess "File '$file' does not under root dir '$self->{___params}->{RootDir}'\n";
74             }
75            
76 1         3 $self->{___params}->{File} = $file;
77              
78 1         3 local($/) = undef;
79 1         38 open IN, $file;
80 1         17 my $in = ;
81 1         10 close IN;
82              
83 1         3 my $cur_path = ___get_dir($file);
84 1         4 $in = ___read_includes($in,$cur_path,$self->{___params}->{RootDir});
85              
86 1         5 $self->{___template___} = ___parse_template($in,$self->{___params}->{BlockTag},$cur_path,$self->{___params}->{RootDir});
87             }
88              
89             sub ___read_includes {
90 5     5   7 my $text = shift;
91 5         16 my $cur_path = shift;
92 5         5 my $root_path = shift;
93 5         19 while($text =~ m//) {
94 4         8 my $pre_inc = $PREMATCH; # text before include
95 4         7 my $post_inc = $POSTMATCH; # text after include
96 4         5 my $include_filename = $1; # got include filename
97 4 50       18 unless(File::Spec->file_name_is_absolute( $include_filename )) {
98 4         29 $include_filename = File::Spec->catfile($cur_path,$include_filename);
99             }
100 4         15 $include_filename = ___clean_up_dirs(File::Spec->canonpath($include_filename)); # delete up dirs from path
101 4 50 33     9 unless(___under_root($root_path,$include_filename) && (-r $include_filename)) {
102 0         0 $text = $pre_inc;
103 0 0       0 if($debug) {
104 0         0 $text .=
105             "File '$include_filename' can't be included" .
106             " in this document because of wrong file path";
107             }
108 0         0 $text .= $post_inc;
109             } else {
110 4         5 my $in = '';
111 4         10 local($/) = undef;
112 4         118 open IN, "< $include_filename";
113 4         55 $in = ;
114 4         29 close IN;
115 4         7 my $new_cur_path = ___get_dir($include_filename);
116 4         13 $text = $pre_inc . ___read_includes($in,$new_cur_path,$root_path) . $post_inc;
117             }
118             }
119 5         28 return $text;
120             }
121              
122             sub ___clean_up_dirs {
123 6     6   10 my $path = shift;
124 6         569 my ($vol, $dirs, $file) = File::Spec->splitpath( $path, 1 );
125 6         38 my @path = File::Spec->splitdir( $dirs );
126 6         9 my $i;
127              
128 6         14 for($i = 0; $i<$#path; $i++) {
129 32 50       90 if($path[$i] eq File::Spec->updir()) {
130 0 0       0 if($i) { # not first folder
131 0         0 $i--; # go 1 step up
132             } else { # first folder
133 0         0 shift @path;
134             }
135 0         0 redo;
136             }
137 32 50       134 if($path[$i+1] eq File::Spec->updir()) {
138 0         0 splice @path, $i, 2;
139 0         0 redo;
140             }
141             }
142              
143 6         62 return File::Spec->catpath($vol,File::Spec->catdir(@path),$file);
144             }
145              
146              
147             sub ___under_root {
148 5     5   6 my $root = shift;
149 5         5 my $file = shift;
150              
151 5         128 return $file =~ /^($root)/;
152             }
153              
154             sub ___get_dir {
155 5     5   6 my $path = shift;
156 5         44 my ($vol, $dirs, $file) = File::Spec->splitpath( $path );
157              
158 5         26 return File::Spec->catpath($vol,$dirs,''); # remove file
159             }
160              
161              
162              
163             sub parse {
164 1     1 1 2 my $self = shift;
165 1   50     3 my $data = shift || {};
166 1         2 my $res;
167              
168 1         4 $res = ___parse_data($self->{___template___},$data,$self->{___params}->{BlockTag});
169 1 50       5 $res =~ s/<%.*?%>//g unless $self->{___params}->{NoClean};
170              
171 1         5 return $res;
172             }
173              
174             sub ___parse_template {
175 5     5   6 my $text = shift;
176 5         6 my $block_tag = shift;
177 5         6 my $cur_path = shift;
178 5         5 my $root_path = shift;
179 5         21 my $tmp = {___text___=>'',___blocks___=>{}};
180 5         11 while($text) {
181 9 100       60 if($text =~ m/(.*?)/s) {
182 4         6 my $block_name = $2;
183 4         5 my $block_text = $3;
184 4         8 $tmp->{___text___} .= $PREMATCH;
185 4         6 $text = $POSTMATCH;
186 4         9 $tmp->{___text___} .= "";
187 4         11 $tmp->{___blocks___}->{$block_name} = ___parse_template($block_text,$block_tag);
188             } else {
189 5         7 $tmp->{___text___} .= $text;
190 5         12 $text = undef;
191             }
192             }
193 5         18 return $tmp;
194             }
195              
196             sub ___parse_data {
197 6   50 6   8 my $template = shift || {};
198 6   50     12 my $data = shift || {};
199 6         6 my $block_tag = shift;
200 6         8 my $text_level = $template->{___text___};
201 6         6 my $text_result = '';
202 6         6 my %data = ();
203 6         5 my %blocks = ();
204 6         4 my $key;
205 6         20 foreach $key (keys %$data) {
206 11 100       18 if(ref $data->{$key}) {
207 4         8 $blocks{$key} = $data->{$key};
208             } else {
209 7         14 $data{$key} = $data->{$key};
210             }
211             }
212 6         12 while($text_level) {
213 10 100       48 if($text_level =~ m//s) {
214 4         6 my $block_name = $2;
215 4         4 my $block;
216 4         5 $text_result .= $PREMATCH;
217 4         7 $text_level = $POSTMATCH;
218 4         4 foreach $block (@{$blocks{$block_name}}) {
  4         6  
219 5 50       10 $text_result .= "" if $debug;
220 5         26 $text_result .= ___parse_data($template->{___blocks___}->{$block_name},$block,$block_tag);
221 5 50       18 $text_result .= "" if $debug;
222             }
223             } else {
224 6         6 $text_result .= $text_level;
225 6         13 $text_level = undef;
226             }
227             }
228              
229             # put local macro value or leave for global value changes
230 6 50       28 $text_result =~ s/<%\s*(.+?)\s*%>/(exists $data{$1}) ? $data{$1} : "<% $1 %>"/ge;
  7         26  
231 6         17 return $text_result;
232             }
233              
234             1;
235             __END__