File Coverage

blib/lib/String/Interpolate/RE.pm
Criterion Covered Total %
statement 49 49 100.0
branch 32 32 100.0
condition 17 18 94.4
subroutine 8 8 100.0
pod n/a
total 106 107 99.0


line stmt bran cond sub pod time code
1             package String::Interpolate::RE;
2              
3             # ABSTRACT: interpolate variables into strings using regular expressions
4              
5 6     6   1343678 use strict;
  6         45  
  6         176  
6 6     6   31 use warnings;
  6         12  
  6         137  
7 6     6   29 use Carp;
  6         10  
  6         323  
8              
9 6     6   2571 use Exporter::Shiny qw[ strinterp ];
  6         24884  
  6         38  
10              
11             our $VERSION = '0.10';
12              
13             ## no critic (ProhibitAccessOfPrivateData)
14              
15             my %Opt = (
16             variable_re => qr/\w+/,
17             raiseundef => 0,
18             emptyundef => 0,
19             useenv => 1,
20             format => 0,
21             recurse => 0,
22             recurse_limit => 0,
23             recurse_fail_limit => 100,
24             );
25              
26             *strinterp = _mk_strinterp( \%Opt );
27              
28             sub _generate_strinterp {
29              
30 6     6   707 my ( $me, $name, $args ) = @_;
31              
32             return \&strinterp
33 6 100 100     59 if ! defined $args || ! defined $args->{opts};
34              
35 1         7 my %opt = %Opt;
36 1         3 $opt{lc $_} = $args->{opts}{$_} foreach keys %{$args->{opts}};
  1         7  
37 1         3 return _mk_strinterp( \%opt );
38             }
39              
40             sub _mk_strinterp {
41              
42 7     7   17 my $default_opt = shift;
43              
44             return sub {
45              
46 49     49   27890 my ( $text, $var, $opts ) = @_;
47              
48 49 100       141 $var = {} unless defined $var;
49              
50 49         262 my %opt = %$default_opt;
51              
52 49 100       136 if ( defined $opts ) {
53 26         127 $opt{ lc $_ } = $opts->{$_} foreach keys %$opts;
54             }
55             ## use critic
56              
57 49 100       137 my $fmt = $opt{format} ? ':([^}]+)' : '()';
58              
59 49         96 $opt{track} = {};
60 49         94 $opt{loop} = 0;
61 49         83 $opt{fmt} = $fmt;
62              
63 49         147 _strinterp( $text, $var, \%opt );
64              
65 39         259 return $text;
66             }
67 7         44 }
68              
69             sub _strinterp {
70              
71 94     94   137 my $var = $_[1];
72 94         128 my $opt = $_[2];
73 94         160 my $fmt = $opt->{fmt};
74 94         137 my $re = $opt->{variable_re};
75              
76 94         962 $_[0] =~ s{
77             \$ # find a literal dollar sign
78             ( # followed by either
79             \{ ($re)(?:$fmt)? \} # a variable name in curly brackets ($2)
80             # and an optional sprintf format
81             | # or
82             (\w+) # a bareword ($3)
83             )
84             }{
85 100 100       356 my $t = defined $4 ? $4 : $2;
86              
87 100 100       274 my $user_value = 'CODE' eq ref $var ? $var->($t) : $var->{$t};
88              
89             my $v =
90             # user provided?
91             defined $user_value ? $user_value
92              
93             # maybe in the environment
94             : $opt->{useenv} && exists $ENV{$t} ? $ENV{$t}
95              
96             # undefined: throw an error?
97             : $opt->{raiseundef} ? croak( "undefined variable: $t\n" )
98              
99             # undefined: replace with ''?
100 100 100 100     978 : $opt->{emptyundef} ? ''
    100          
    100          
    100          
101              
102             # undefined
103             : undef
104              
105             ;
106              
107 97 100 66     358 if ( $opt->{recurse} && defined $v ) {
108              
109              
110             RECURSE:
111             {
112              
113 55         79 croak(
114             "circular interpolation loop detected with repeated interpolation of <\$$t>\n"
115 55 100       728 ) if $opt->{track}{$t}++;
116              
117 51         73 ++$opt->{loop};
118              
119 51 100 100     111 last RECURSE if $opt->{recurse_limit} && $opt->{loop} > $opt->{recurse_limit};
120              
121             croak(
122             "recursion fail-safe limit ($opt->{recurse_fail_limit}) reached at interpolation of <\$$t>\n"
123 48 100 100     660 ) if $opt->{recurse_fail_limit} && $opt->{loop} > $opt->{recurse_fail_limit};
124              
125 45         99 _strinterp( $v, $_[1], $_[2] );
126              
127             }
128              
129 32         65 delete $opt->{track}{$t};
130 32         47 --$opt->{loop};
131             }
132              
133             # if not defined, just put it back into the string
134 74 100 100     405 ! defined $v ? '$' . $1
    100          
135              
136             # no format? return as is
137             : ! defined $3 || $3 eq '' ? $v
138              
139             # format it
140             : sprintf( $3, $v)
141              
142             ;
143              
144             }egx;
145             }
146              
147             1;
148              
149             #
150             # This file is part of String-Interpolate-RE
151             #
152             # This software is Copyright (c) 2017 by Smithsonian Astrophysical Observatory.
153             #
154             # This is free software, licensed under:
155             #
156             # The GNU General Public License, Version 3, June 2007
157             #
158              
159             =pod
160              
161             =head1 NAME
162              
163             String::Interpolate::RE - interpolate variables into strings using regular expressions
164              
165             =head1 VERSION
166              
167             version 0.10
168              
169             =head1 SYNOPSIS
170              
171             # default formulation
172             use String::Interpolate::RE qw( strinterp );
173              
174             $str = strinterp( "${Var1} $Var2", $vars, \%opts );
175              
176             # import with different default options.
177             use String::Interpolate::RE strinterp => { opts => { useENV => 0 } };
178              
179             =head1 DESCRIPTION
180              
181             This module interpolates variables into strings using regular
182             expression matching rather than Perl's built-in interpolation
183             mechanism and thus hopefully does not suffer from the security
184             problems inherent in using B to interpolate into strings of
185             suspect ancestry.
186              
187             =head2 Changing the default option values
188              
189             The default values for L's options were not all well
190             thought out. B uses L,
191             allowing a version of L with saner defaults to be
192             exported. Simply specify them when importing:
193              
194             use String::Interpolate::RE strinterp => { opts => { useENV => 0 } };
195              
196             The subroutine may be renamed using the C<-as> option:
197              
198             use String::Interpolate::RE strinterp => { -as => strinterp_noenv,
199             opts => { useENV => 0 } };
200              
201             strinterp_noenv( ... );
202              
203             =head1 INTERFACE
204              
205             =over
206              
207             =item strinterp
208              
209             $str = strinterp( $template );
210             $str = strinterp( $template, $vars );
211             $str = strinterp( $template, $vars, \%opts );
212              
213             Interpolate variables into a template string, returning the
214             resultant string. The template string is scanned for tokens of the
215             form
216              
217             $VAR
218             ${VAR}
219              
220             where C is composed of one or more word characters (as defined by
221             the C<\w> Perl regular expression pattern). C is resolved using
222             the optional C<$vars> argument, which may either by a hashref (in
223             which case C must be a key), or a function reference (which is
224             passed C as its only argument and must return the value).
225              
226             If the value returned for C is defined, it will be interpolated
227             into the string at that point. By default, variables which are not
228             defined are by default left as is in the string.
229              
230             The C<%opts> parameter may be used to modify the behavior of this
231             function. The following (case insensitive) keys are recognized:
232              
233             =over
234              
235             =item format I
236              
237             If this flag is true, the template string may provide a C
238             compatible format which will be used to generate the interpolated
239             value. The format should be appended to the variable name with
240             an intervening C<:> character, e.g.
241              
242             ${VAR:fmt}
243              
244             For example,
245              
246             %var = ( foo => 3 );
247             print strinterp( '${foo:%03d}', \%var, { format => 1 } );
248              
249             would result in
250              
251             003
252              
253             =item raiseundef I
254              
255             If true, a variable which has not been defined will result in an
256             exception being raised. This defaults to false.
257              
258             =item emptyundef I
259              
260             If true, a variable which has not been defined will be replaced with
261             the empty string. This defaults to false.
262              
263             =item useENV I
264              
265             If true, the C<%ENV> hash will be searched for variables which are not
266             defined in the passed C<%var> hash. This defaults to true.
267              
268             =item recurse I
269              
270             If true, derived values are themselves scanned for variables to
271             interpolate. To specify a limit to the number of levels of recursions
272             to attempt, set the C option. Circular dependencies
273             are caught, but just to be safe there's a limit of recursion levels
274             specified by C, beyond which an exception is
275             thrown.
276              
277             For example,
278              
279             my %var = ( a => '$b', b => '$c', c => 'd' );
280             strinterp( '$a', \%var ) => '$b'
281             strinterp( '$a', \%var, { recurse => 1 } ) => 'd'
282             strinterp( '$a', \%var, { recurse => 1, recurse_limit => 1 } ) => '$c'
283              
284             strinterp( '$a', { a => '$b', b => '$a' } , { recurse => 1 }
285             recursive interpolation loop detected with repeated
286             interpolation of $a
287              
288             =item recurse_limit I
289              
290             The number of recursion levels to descend when recursing into a
291             variable's value before stopping. The default is C<0>, which means no
292             limit.
293              
294             =item recurse_fail_limit I
295              
296             The number of recursion levels to descend when recursing into a
297             variable's value before giving up and croaking. The default is C<100>.
298             Setting this to C<0> means no limit.
299              
300             =item variable_re I
301              
302             This specifies the regular expression (created with the C
303             operator) which will match a variable name. It defaults to
304             C. Don't use C<:>, C<{>, or C<}> in the regex, or things may
305             break.
306              
307             =back
308              
309             =back
310              
311             =head1 DIAGNOSTICS
312              
313             =over
314              
315             =item C<< undefined variable: %s >>
316              
317             This string is thrown if the C option is set and the
318             variable C<%s> is not defined.
319              
320             =item C<< recursive interpolation loop detected with repeated interpolation of <%s> >>
321              
322             When resolving nested interpolated values (with the C option
323             true ) a circular loop was found.
324              
325             =item C<< recursion fail-safe limit (%d) reached at interpolation of <%s> >>
326              
327             The recursion fail safe limit (C) was reached while
328             interpolating nested variable values (with the C option true ).
329              
330             =back
331              
332             =head1 BUGS AND LIMITATIONS
333              
334             You can make new bug reports, and view existing ones, through the
335             web interface at L.
336              
337             =head1 AUTHOR
338              
339             Diab Jerius
340              
341             =head1 COPYRIGHT AND LICENSE
342              
343             This software is Copyright (c) 2017 by Smithsonian Astrophysical Observatory.
344              
345             This is free software, licensed under:
346              
347             The GNU General Public License, Version 3, June 2007
348              
349             =cut
350              
351             __END__