File Coverage

blib/lib/Rex/Template.pm
Criterion Covered Total %
statement 94 115 81.7
branch 17 30 56.6
condition 15 27 55.5
subroutine 11 12 91.6
pod 2 4 50.0
total 139 188 73.9


line stmt bran cond sub pod time code
1             #
2             # (c) Jan Gehring
3             #
4              
5             =head1 NAME
6              
7             Rex::Template - simple template engine
8              
9             =head1 SYNOPSIS
10              
11             use Rex::Template;
12              
13             my $template = Rex::Template->new;
14              
15             print $template->parse($content, \%template_vars);
16             print $template->parse($content, @template_vars);
17              
18             =head1 DESCRIPTION
19              
20             This is a simple template engine for configuration files. It is included mostly for backwards compatibility, and it is recommended to use L instead (for better control of chomping new lines, and better diagnostics if things go wrong).
21              
22             =head2 SYNTAX
23              
24             The following syntax is recognized:
25              
26             =over 4
27              
28             =item * anything between C%> and C<%E> markers are considered as a template directive, which is treated as Perl code
29              
30             =item * if the opening marker is followed by an equal sign (C%=>) or a plus sign (C%+>), then the directive is replaced with the value it evaluates to
31              
32             =item * if the closing marker is prefixed with a minus sign (C<-%E>), then any trailing newlines are chomped for that directive
33              
34             =back
35              
36             The built-in template support is intentionally kept basic and simple. For anything more sophisticated, please use your favorite template engine.
37              
38             =head2 EXAMPLES
39              
40             Plain text is unchanged:
41              
42             my $result = $template->parse( 'one two three', {} );
43              
44             # $result is 'one two three'
45              
46             Variable interpolation:
47              
48             my $result = template->parse( 'Hello, this is <%= $::name %>', { name => 'foo' } ); # original format
49             my $result = template->parse( 'Hello, this is <%+ $::name %>', { name => 'foo' } ); # alternative format with + sign
50             my $result = template->parse( 'Hello, this is <%= $name %>', { name => 'foo' } ); # local variables
51             my $result = template->parse( 'Hello, this is <%= $name %>', name => 'foo' ); # array of variables, instead of hashref
52              
53             # $result is 'Hello, this is foo' for all cases above
54              
55             Simple evaluation:
56              
57             my $result = $template->parse( '<%= join("/", @{$elements} ) %>', elements => [qw(one two three)] );
58             # $result is 'one/two/three'
59              
60             Embedded code blocks:
61              
62             my $content = '<% if ($logged_in) { %>
63             Logged in!
64             <% } else { %>
65             Logged out!
66             <% } %>';
67              
68             my $result = $template->parse( $content, logged_in => 1 );
69              
70             # $result is "\nLogged in!\n"
71              
72             =head1 DIAGNOSTICS
73              
74             Not much, mainly due to the internal approach of the module.
75              
76             If there was a problem, it prints an C level I<"syntax error at ...">, followed by a C about I<"It seems that there was an error processing the template because the result is empty.">, and finally I<"Error processing template at ...">.
77              
78             The beginning of the reported syntax error might give some clue where the error happened in the template, but that's it.
79              
80             Use L instead for better diagnostics.
81              
82             =head1 CONFIGURATION AND ENVIRONMENT
83              
84             If C<$Rex::Template::BE_LOCAL> is set to a true value, then local template variables are supported instead of only global ones (C<$foo> vs C<$::foo>). The default value is C<1> since Rex-0.41. It can be disabled with the L feature flag.
85              
86             If C<$Rex::Template::DO_CHOMP> is set to a true value, then any trailing new line character resulting from template directives are chomped. Defaults to C<0>.
87              
88             This module does not support any environment variables.
89              
90             =head1 EXPORTED FUNCTIONS
91              
92             =cut
93              
94             package Rex::Template;
95              
96 105     105   1464 use v5.12.5;
  105         414  
97 105     105   696 use warnings;
  105         291  
  105         4453  
98 105     105   642 use Symbol;
  105         245  
  105         8296  
99              
100             our $VERSION = '1.14.2.2'; # TRIAL VERSION
101              
102 105     105   780 use Rex::Config;
  105         1397  
  105         1453  
103 105     105   3176 use Rex::Logger;
  105         236  
  105         789  
104             require Rex::Args;
105              
106             our $DO_CHOMP = 0;
107             our $BE_LOCAL = 1;
108              
109             sub function {
110 1     1 0 679 my ( $class, $name, $code ) = @_;
111              
112 1         5 my $ref_to_name = qualify_to_ref( $name, $class );
113 1         38 *{$ref_to_name} = $code;
  1         9  
114             }
115              
116             sub new {
117 120     120 0 355 my $that = shift;
118 120   33     780 my $proto = ref($that) || $that;
119 120         338 my $self = {@_};
120              
121 120         319 bless( $self, $proto );
122              
123 120         324 return $self;
124             }
125              
126             =head2 parse($content, $variables)
127              
128             Parse C<$content> as a template, using C<$variables> hash reference to pass name-value pairs of variables to make them available for the template function.
129              
130             Alternatively, the variables may be passed as an array instead of a hash reference.
131              
132             =cut
133              
134             sub parse {
135 130     130 1 292 my $self = shift;
136 130         251 my $data = shift;
137              
138 130         249 my $vars = {};
139              
140 130 100       639 if ( ref( $_[0] ) eq "HASH" ) {
141 128         357 $vars = shift;
142             }
143             else {
144 2         9 $vars = {@_};
145             }
146              
147 130         190 my $new_data;
148 130         417 my $___r = "";
149              
150 130         291 my $do_chomp = 0;
151             $new_data = join(
152             "\n",
153             map {
154 130         1218 my ( $code, $type, $text ) = ( $_ =~ m/(\<%)*([+=])*(.+)%\>/s );
  407         1576  
155              
156 407 100       898 if ($code) {
157 153         332 my $pcmd = substr( $text, -1 );
158 153 50       344 if ( $pcmd eq "-" ) {
159 0         0 $text = substr( $text, 0, -1 );
160 0         0 $do_chomp = 1;
161             }
162              
163 153         423 my ( $var_type, $var_name ) = ( $text =~ m/([\$])::([a-zA-Z0-9_]+)/ );
164              
165 153 50 100     891 if ( $var_name && !ref( $vars->{$var_name} ) && !$BE_LOCAL ) {
    100 66        
      100        
      66        
166 0         0 $text =~ s/([\$])::([a-zA-Z0-9_]+)/$1\{\$$2\}/g;
167             }
168             elsif ( $var_name && !ref( $vars->{$var_name} ) && $BE_LOCAL ) {
169 44         319 $text =~ s/([\$])::([a-zA-Z0-9_]+)/$1$2/g;
170             }
171             else {
172 109         202 $text =~ s/([\$])::([a-zA-Z0-9_]+)/\$$2/g;
173             }
174              
175 153 100 66     780 if ( $type && $type =~ m/^[+=]$/ ) {
176 95         404 "\$___r .= $text;";
177             }
178             else {
179 58         138 $text;
180             }
181              
182             }
183              
184             else {
185 254         486 my $chomped = $_;
186 254 50 33     1217 if ( $DO_CHOMP || $do_chomp ) {
187 0         0 chomp $chomped;
188 0         0 $do_chomp = 0;
189             }
190 254         799 '$___r .= "' . _quote($chomped) . '";';
191              
192             }
193              
194             } split( /(\<%.*?%\>)/s, $data )
195             );
196              
197 130         388 eval {
198 105     105   71174 no strict 'vars';
  105         237  
  105         71491  
199              
200 130         288 for my $var ( keys %{$vars} ) {
  130         688  
201 700         2404 Rex::Logger::debug("Registering: $var");
202              
203 700         1858 my $ref_to_var = qualify_to_ref($var);
204              
205 700 100       15198 unless ( ref( $vars->{$var} ) ) {
206 488         1027 $ref_to_var = \$vars->{$var};
207             }
208             else {
209 212         606 $ref_to_var = $vars->{$var};
210             }
211             }
212              
213 130 50       419 if ( $BE_LOCAL == 1 ) {
214 130         425 my $var_data = '
215            
216             return sub {
217             my $___r = "";
218             my (
219            
220             ';
221              
222 130         330 my @code_values;
223 130         235 for my $var ( keys %{$vars} ) {
  130         422  
224 700         1137 my $new_var = _normalize_var_name($var);
225 700         2183 Rex::Logger::debug("Registering local: $new_var");
226 700         1341 $var_data .= '$' . $new_var . ", \n";
227 700         1496 push( @code_values, $vars->{$var} );
228             }
229              
230 130         293 $var_data .= '$this_is_really_nothing) = @_;';
231 130         221 $var_data .= "\n";
232              
233 130         240 $var_data .= $new_data;
234              
235 130         199 $var_data .= "\n";
236 130         178 $var_data .= ' return $___r;';
237 130         194 $var_data .= "\n};";
238              
239 130         344 Rex::Logger::debug("BE_LOCAL==1");
240              
241 130         1309 my %args = Rex::Args->getopts;
242 130 50 33     548 if ( defined $args{'d'} && $args{'d'} > 1 ) {
243 0         0 Rex::Logger::debug($var_data);
244             }
245              
246 130         18012 my $tpl_code = eval($var_data);
247              
248 130 50       608 if ($@) {
249 0         0 Rex::Logger::info($@);
250             }
251              
252 130         2837 $___r = $tpl_code->(@code_values);
253              
254             }
255             else {
256 0         0 Rex::Logger::debug("BE_LOCAL==0");
257 0         0 my %args = Rex::Args->getopts;
258 0 0 0     0 if ( defined $args{'d'} && $args{'d'} > 1 ) {
259 0         0 Rex::Logger::debug($new_data);
260             }
261              
262 0         0 $___r = eval($new_data);
263              
264 0 0       0 if ($@) {
265 0         0 Rex::Logger::info($@);
266             }
267             }
268              
269             # undef the vars
270 130         392 for my $var ( keys %{$vars} ) {
  130         556  
271 128         1414 $$var = undef;
272             }
273              
274             };
275              
276 130 50       499 if ( !$___r ) {
277 0         0 Rex::Logger::info(
278             "It seems that there was an error processing the template", "warn" );
279 0         0 Rex::Logger::info( "because the result is empty.", "warn" );
280 0         0 die("Error processing template");
281             }
282              
283 130         1146 return $___r;
284             }
285              
286             sub _quote {
287 254     254   507 my ($str) = @_;
288              
289 254         497 $str =~ s/\\/\\\\/g;
290 254         480 $str =~ s/"/\\"/g;
291 254         384 $str =~ s/\@/\\@/g;
292 254         367 $str =~ s/\%/\\%/g;
293 254         401 $str =~ s/\$/\\\$/g;
294              
295 254         1339 return $str;
296             }
297              
298             sub _normalize_var_name {
299 710     710   6379 my ($input) = @_;
300 710         1461 $input =~ s/[^A-Za-z0-9_]/_/g;
301 710         1209 return $input;
302             }
303              
304             =head2 is_defined($variable, $default_value)
305              
306             This function will check if C<$variable> is defined. If yes, it will return the value of C<$variable>, otherwise it will return C<$default_value>.
307              
308             You can use this function inside your templates, for example:
309              
310             ServerTokens <%= is_defined( $::server_tokens, 'Prod' ) %>
311              
312             =cut
313              
314             sub is_defined {
315 0     0 1   my ( $check_var, $default ) = @_;
316 0 0         if ( defined $check_var ) { return $check_var; }
  0            
317              
318 0           return $default;
319             }
320              
321             1;
322              
323             __END__