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   1484 use v5.12.5;
  105         375  
97 105     105   610 use warnings;
  105         340  
  105         3433  
98 105     105   570 use Symbol;
  105         210  
  105         8080  
99              
100             our $VERSION = '1.14.2.3'; # TRIAL VERSION
101              
102 105     105   795 use Rex::Config;
  105         1481  
  105         1344  
103 105     105   3399 use Rex::Logger;
  105         234  
  105         719  
104             require Rex::Args;
105              
106             our $DO_CHOMP = 0;
107             our $BE_LOCAL = 1;
108              
109             sub function {
110 1     1 0 641 my ( $class, $name, $code ) = @_;
111              
112 1         4 my $ref_to_name = qualify_to_ref( $name, $class );
113 1         30 *{$ref_to_name} = $code;
  1         9  
114             }
115              
116             sub new {
117 120     120 0 380 my $that = shift;
118 120   33     746 my $proto = ref($that) || $that;
119 120         350 my $self = {@_};
120              
121 120         363 bless( $self, $proto );
122              
123 120         472 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 334 my $self = shift;
136 130         253 my $data = shift;
137              
138 130         244 my $vars = {};
139              
140 130 100       632 if ( ref( $_[0] ) eq "HASH" ) {
141 128         361 $vars = shift;
142             }
143             else {
144 2         7 $vars = {@_};
145             }
146              
147 130         216 my $new_data;
148 130         423 my $___r = "";
149              
150 130         275 my $do_chomp = 0;
151             $new_data = join(
152             "\n",
153             map {
154 130         1225 my ( $code, $type, $text ) = ( $_ =~ m/(\<%)*([+=])*(.+)%\>/s );
  407         1785  
155              
156 407 100       847 if ($code) {
157 153         315 my $pcmd = substr( $text, -1 );
158 153 50       334 if ( $pcmd eq "-" ) {
159 0         0 $text = substr( $text, 0, -1 );
160 0         0 $do_chomp = 1;
161             }
162              
163 153         455 my ( $var_type, $var_name ) = ( $text =~ m/([\$])::([a-zA-Z0-9_]+)/ );
164              
165 153 50 100     897 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         314 $text =~ s/([\$])::([a-zA-Z0-9_]+)/$1$2/g;
170             }
171             else {
172 109         201 $text =~ s/([\$])::([a-zA-Z0-9_]+)/\$$2/g;
173             }
174              
175 153 100 66     773 if ( $type && $type =~ m/^[+=]$/ ) {
176 95         433 "\$___r .= $text;";
177             }
178             else {
179 58         144 $text;
180             }
181              
182             }
183              
184             else {
185 254         454 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         862 '$___r .= "' . _quote($chomped) . '";';
191              
192             }
193              
194             } split( /(\<%.*?%\>)/s, $data )
195             );
196              
197 130         417 eval {
198 105     105   71035 no strict 'vars';
  105         260  
  105         70624  
199              
200 130         245 for my $var ( keys %{$vars} ) {
  130         703  
201 700         2448 Rex::Logger::debug("Registering: $var");
202              
203 700         1897 my $ref_to_var = qualify_to_ref($var);
204              
205 700 100       15213 unless ( ref( $vars->{$var} ) ) {
206 488         967 $ref_to_var = \$vars->{$var};
207             }
208             else {
209 212         530 $ref_to_var = $vars->{$var};
210             }
211             }
212              
213 130 50       426 if ( $BE_LOCAL == 1 ) {
214 130         463 my $var_data = '
215            
216             return sub {
217             my $___r = "";
218             my (
219            
220             ';
221              
222 130         267 my @code_values;
223 130         219 for my $var ( keys %{$vars} ) {
  130         432  
224 700         1291 my $new_var = _normalize_var_name($var);
225 700         2030 Rex::Logger::debug("Registering local: $new_var");
226 700         1355 $var_data .= '$' . $new_var . ", \n";
227 700         1516 push( @code_values, $vars->{$var} );
228             }
229              
230 130         326 $var_data .= '$this_is_really_nothing) = @_;';
231 130         277 $var_data .= "\n";
232              
233 130         239 $var_data .= $new_data;
234              
235 130         224 $var_data .= "\n";
236 130         209 $var_data .= ' return $___r;';
237 130         204 $var_data .= "\n};";
238              
239 130         342 Rex::Logger::debug("BE_LOCAL==1");
240              
241 130         1266 my %args = Rex::Args->getopts;
242 130 50 33     516 if ( defined $args{'d'} && $args{'d'} > 1 ) {
243 0         0 Rex::Logger::debug($var_data);
244             }
245              
246 130         18048 my $tpl_code = eval($var_data);
247              
248 130 50       683 if ($@) {
249 0         0 Rex::Logger::info($@);
250             }
251              
252 130         2825 $___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         385 for my $var ( keys %{$vars} ) {
  130         653  
271 128         1450 $$var = undef;
272             }
273              
274             };
275              
276 130 50       556 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         1241 return $___r;
284             }
285              
286             sub _quote {
287 254     254   526 my ($str) = @_;
288              
289 254         469 $str =~ s/\\/\\\\/g;
290 254         499 $str =~ s/"/\\"/g;
291 254         398 $str =~ s/\@/\\@/g;
292 254         384 $str =~ s/\%/\\%/g;
293 254         399 $str =~ s/\$/\\\$/g;
294              
295 254         1450 return $str;
296             }
297              
298             sub _normalize_var_name {
299 710     710   5832 my ($input) = @_;
300 710         1526 $input =~ s/[^A-Za-z0-9_]/_/g;
301 710         1168 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__