1package Template::Plugin::Subst; 2 3# Copyright (c) 2005 Nik Clayton 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# 2. Redistributions in binary form must reproduce the above copyright 12# notice, this list of conditions and the following disclaimer in the 13# documentation and/or other materials provided with the distribution. 14# 15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25# SUCH DAMAGE. 26 27use warnings; 28use strict; 29 30use Template::Plugin::Filter; 31use base qw(Template::Plugin::Filter); 32 33use Template::Stash; 34 35$Template::Stash::SCALAR_OPS->{subst} = \&subst; 36 37=head1 NAME 38 39Template::Plugin::Subst - s/// functionality for Template Toolkit templates 40 41=head1 VERSION 42 43Version 0.02 44 45=cut 46 47our $VERSION = '0.02'; 48 49sub init { 50 my $self = shift; 51 52 $self->{_DYNAMIC} = 1; 53 54 return $self; 55} 56 57sub filter { 58 my($self, $text, $args, $config) = @_; 59 60 $config = $self->merge_config($config); 61 62 my $pattern = $config->{pattern}; 63 my $replacement = $config->{replacement}; 64 my $global = defined $config->{global} ? $config->{global} : 1; 65 66# warn "pattern: $pattern, replacement: $replacement\n"; 67 $text = subst($text, $pattern, $replacement, $global); 68 69 return $text; 70} 71 72sub subst { 73 my($text, $pattern, $replacement, $global) = @_; 74 75 $global = defined $global ? $global : 1; 76 77# warn "-> subst() ('$pattern', '$replacement')\n"; 78 if($text !~ m/$pattern/) { 79# warn "text does not match '$pattern', returning"; 80 return $text; 81 } 82 83 # If there are no subgroups then it's a simple search/replace 84 if($#- == 0) { 85# warn "No subgroups found, doing simple search/replace\n"; 86 if($global) { 87 $text =~ s/$pattern/$replacement/g; 88 } else { 89 $text =~ s/$pattern/$replacement/; 90 } 91 return $text; 92 } 93 94 # First, save the original text, and what was matched 95 my $saved_text = $text; 96 my $PREMATCH = substr($saved_text, 0, $-[0]); 97 my $MATCHED = substr($saved_text, $-[0], $+[0] - $-[0]); 98 my $POSTMATCH = substr($saved_text, $+[0]); 99 100 # Save the positions where we matched 101 my @saved_match_start = @-; 102 my @saved_match_end = @+; 103 104# warn "PREMATCH : <<$PREMATCH>>"; 105# warn "MATCHED : <<$MATCHED>>"; 106# warn "POSTMATCH: <<$POSTMATCH>>"; 107 108 # Now do the s///. This will leave placeholders (literally, '$1', '$2', 109 # etc, in the replaced text. 110# warn "Doing s///"; 111 $MATCHED =~ s/$pattern/$replacement/; 112# warn "MATCHED: <<$MATCHED>>"; 113 114 foreach my $i (1..$#saved_match_start) { 115 my $backref = substr($saved_text, 116 $saved_match_start[$i], 117 $saved_match_end[$i] - $saved_match_start[$i]); 118 $MATCHED =~ s/\$$i/$backref/g; 119 } 120 121# warn "Fixed up backrefs"; 122# warn "MATCHED: <<$MATCHED>>"; 123 124 if($global) { 125 return $PREMATCH . $MATCHED . subst($POSTMATCH, $pattern, $replacement); 126 } else { 127 return $PREMATCH . $MATCHED . $POSTMATCH; 128 } 129} 130 131=head1 SYNOPSIS 132 133=head2 As a vemthod 134 135 [% USE Subst %] 136 137 [% str = 'foobar' %] 138 139 [% str.subst('(foo)(bar)', '$2$1', 1) %] 140 141=head2 As a filter 142 143 [% USE filt = Subst 144 pattern = '(foo)(bar)' 145 replacement = '$2$1' 146 global = 1 %] 147 148Then 149 150 [% text | $filt %] 151 152or 153 154 [% FILTER $filt %] 155 foobar 156 [% END %] 157 158=head1 DESCRIPTION 159 160Template::Plugin::Subst acts as a filter and a virtual method to carry 161out regular expression substitutions with back references on text and 162variables in the Template Toolkit. 163 164That's the advantage of this approach over the built-in C<replace> 165method. C<replace> doesn't deal with backrefs, so code like this: 166 167 [% str = 'foobar' %] 168 [% str.replace('(foo)(bar)', '$2$1') %] 169 170inserts a literal C<$2$1> in to your document. 171 172But with Template::Plugin::Subst; 173 174 [% USE Subst %] 175 [% str = 'foobar' %] 176 [% str.subst('(foo)(bar)', '$2$1') %] 177 178you get the expected C<barfoo>. 179 180It can also be used as a filter, in which case it's very useful for finding 181information in text and augmenting it in a useful fashion. 182 183For example, suppose you want all strings of the form C<rt#\d+>, which 184reference RT ticket numbers, to be converted to links to your local 185RT installation. 186 187First, instatiate the filter: 188 189 [% USE rt = Subst 190 pattern = 'rt#(\d+)' 191 replacement = '<a href="/rt.cgi?t=$1">rt#$1</a>' %] 192 193and then use it to filter arbitrary text: 194 195 [% text_variable | $rt %] 196 197=head1 OPTIONS 198 199=head2 vmethod 200 201 .subst($pattern, $replacement[, $global]) 202 203As a vmethod the first two arguments are the pattern to search for and 204the string to replace it with. These arguments are mandatory. 205 206The third argument is a boolean that specifies whether or the 207search/replace should be global, and behaves in the same way as the C<g> 208modifier on a C<s///> operation. The default value is '1'. Note that 209this differs from the default setting on the C<s///> operator. 210 211=head2 Filter 212 213 [% USE filt = Subst 214 pattern = '...' 215 replacement = '...' 216 global = 1 %] 217 218These three named arguments have the same semantics as the arguments to 219the vmethod. C<global> is optional, and defaults to 1. 220 221=head1 AUTHOR 222 223Nik Clayton, C<< <nik@FreeBSD.org> >> 224 225=head1 BUGS 226 227Please report any bugs or feature requests to 228C<bug-template-plugin-subst@rt.cpan.org>, or through the web interface at 229L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Template-Plugin-Subst>. 230I will be notified, and then you'll automatically be notified of progress on 231your bug as I make changes. 232 233=head1 COPYRIGHT & LICENSE 234 235Copyright (c) 2005 Nik Clayton 236All rights reserved. 237 238Redistribution and use in source and binary forms, with or without 239modification, are permitted provided that the following conditions 240are met: 241 242 1. Redistributions of source code must retain the above copyright 243 notice, this list of conditions and the following disclaimer. 244 2. Redistributions in binary form must reproduce the above copyright 245 notice, this list of conditions and the following disclaimer in the 246 documentation and/or other materials provided with the distribution. 247 248THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 249ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 250IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 251ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 252FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 253DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 254OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 255HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 256LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 257OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 258SUCH DAMAGE. 259 260=cut 261 2621; # End of Template::Plugin::Subst 263