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   1404 use v5.12.5;
  105         374  
97 105     105   602 use warnings;
  105         343  
  105         3300  
98 105     105   566 use Symbol;
  105         229  
  105         8163  
99              
100             our $VERSION = '1.14.3'; # VERSION
101              
102 105     105   733 use Rex::Config;
  105         1301  
  105         1296  
103 105     105   3146 use Rex::Logger;
  105         238  
  105         713  
104             require Rex::Args;
105              
106             our $DO_CHOMP = 0;
107             our $BE_LOCAL = 1;
108              
109             sub function {
110 1     1 0 651 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         8  
114             }
115              
116             sub new {
117 120     120 0 402 my $that = shift;
118 120   33     679 my $proto = ref($that) || $that;
119 120         302 my $self = {@_};
120              
121 120         354 bless( $self, $proto );
122              
123 120         460 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 293 my $self = shift;
136 130         229 my $data = shift;
137              
138 130         268 my $vars = {};
139              
140 130 100       676 if ( ref( $_[0] ) eq "HASH" ) {
141 128         377 $vars = shift;
142             }
143             else {
144 2         7 $vars = {@_};
145             }
146              
147 130         301 my $new_data;
148 130         371 my $___r = "";
149              
150 130         271 my $do_chomp = 0;
151             $new_data = join(
152             "\n",
153             map {
154 130         1223 my ( $code, $type, $text ) = ( $_ =~ m/(\<%)*([+=])*(.+)%\>/s );
  407         1645  
155              
156 407 100       838 if ($code) {
157 153         321 my $pcmd = substr( $text, -1 );
158 153 50       691 if ( $pcmd eq "-" ) {
159 0         0 $text = substr( $text, 0, -1 );
160 0         0 $do_chomp = 1;
161             }
162              
163 153         436 my ( $var_type, $var_name ) = ( $text =~ m/([\$])::([a-zA-Z0-9_]+)/ );
164              
165 153 50 100     958 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         299 $text =~ s/([\$])::([a-zA-Z0-9_]+)/$1$2/g;
170             }
171             else {
172 109         235 $text =~ s/([\$])::([a-zA-Z0-9_]+)/\$$2/g;
173             }
174              
175 153 100 66     752 if ( $type && $type =~ m/^[+=]$/ ) {
176 95         436 "\$___r .= $text;";
177             }
178             else {
179 58         118 $text;
180             }
181              
182             }
183              
184             else {
185 254         465 my $chomped = $_;
186 254 50 33     1138 if ( $DO_CHOMP || $do_chomp ) {
187 0         0 chomp $chomped;
188 0         0 $do_chomp = 0;
189             }
190 254         812 '$___r .= "' . _quote($chomped) . '";';
191              
192             }
193              
194             } split( /(\<%.*?%\>)/s, $data )
195             );
196              
197 130         394 eval {
198 105     105   66921 no strict 'vars';
  105         254  
  105         68425  
199              
200 130         231 for my $var ( keys %{$vars} ) {
  130         676  
201 700         2490 Rex::Logger::debug("Registering: $var");
202              
203 700         1962 my $ref_to_var = qualify_to_ref($var);
204              
205 700 100       14548 unless ( ref( $vars->{$var} ) ) {
206 488         1114 $ref_to_var = \$vars->{$var};
207             }
208             else {
209 212         501 $ref_to_var = $vars->{$var};
210             }
211             }
212              
213 130 50       399 if ( $BE_LOCAL == 1 ) {
214 130         439 my $var_data = '
215            
216             return sub {
217             my $___r = "";
218             my (
219            
220             ';
221              
222 130         244 my @code_values;
223 130         235 for my $var ( keys %{$vars} ) {
  130         395  
224 700         1244 my $new_var = _normalize_var_name($var);
225 700         1981 Rex::Logger::debug("Registering local: $new_var");
226 700         1300 $var_data .= '$' . $new_var . ", \n";
227 700         1453 push( @code_values, $vars->{$var} );
228             }
229              
230 130         358 $var_data .= '$this_is_really_nothing) = @_;';
231 130         315 $var_data .= "\n";
232              
233 130         261 $var_data .= $new_data;
234              
235 130         208 $var_data .= "\n";
236 130         270 $var_data .= ' return $___r;';
237 130         202 $var_data .= "\n};";
238              
239 130         351 Rex::Logger::debug("BE_LOCAL==1");
240              
241 130         1142 my %args = Rex::Args->getopts;
242 130 50 33     555 if ( defined $args{'d'} && $args{'d'} > 1 ) {
243 0         0 Rex::Logger::debug($var_data);
244             }
245              
246 130         17682 my $tpl_code = eval($var_data);
247              
248 130 50       601 if ($@) {
249 0         0 Rex::Logger::info($@);
250             }
251              
252 130         2685 $___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         390 for my $var ( keys %{$vars} ) {
  130         523  
271 128         1413 $$var = undef;
272             }
273              
274             };
275              
276 130 50       525 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         1077 return $___r;
284             }
285              
286             sub _quote {
287 254     254   527 my ($str) = @_;
288              
289 254         447 $str =~ s/\\/\\\\/g;
290 254         485 $str =~ s/"/\\"/g;
291 254         403 $str =~ s/\@/\\@/g;
292 254         359 $str =~ s/\%/\\%/g;
293 254         398 $str =~ s/\$/\\\$/g;
294              
295 254         1333 return $str;
296             }
297              
298             sub _normalize_var_name {
299 710     710   6229 my ($input) = @_;
300 710         1426 $input =~ s/[^A-Za-z0-9_]/_/g;
301 710         1222 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__