1#============================================================= -*-Perl-*- 2# 3# Template::Plugin::Filter 4# 5# DESCRIPTION 6# Template Toolkit module implementing a base class plugin 7# object which acts like a filter and can be used with the 8# FILTER directive. 9# 10# AUTHOR 11# Andy Wardley <abw@wardley.org> 12# 13# COPYRIGHT 14# Copyright (C) 2001-2009 Andy Wardley. All Rights Reserved. 15# 16# This module is free software; you can redistribute it and/or 17# modify it under the same terms as Perl itself. 18# 19#============================================================================ 20 21package Template::Plugin::Filter; 22 23use strict; 24use warnings; 25use base 'Template::Plugin'; 26use Scalar::Util 'weaken'; 27 28 29our $VERSION = 1.38; 30our $DYNAMIC = 0 unless defined $DYNAMIC; 31 32 33sub new { 34 my ($class, $context, @args) = @_; 35 my $config = @args && ref $args[-1] eq 'HASH' ? pop(@args) : { }; 36 37 # look for $DYNAMIC 38 my $dynamic; 39 { 40 no strict 'refs'; 41 $dynamic = ${"$class\::DYNAMIC"}; 42 } 43 $dynamic = $DYNAMIC unless defined $dynamic; 44 45 my $self = bless { 46 _CONTEXT => $context, 47 _DYNAMIC => $dynamic, 48 _ARGS => \@args, 49 _CONFIG => $config, 50 }, $class; 51 52 return $self->init($config) 53 || $class->error($self->error()); 54} 55 56 57sub init { 58 my ($self, $config) = @_; 59 return $self; 60} 61 62 63sub factory { 64 my $self = shift; 65 my $this = $self; 66 67 # This causes problems: https://rt.cpan.org/Ticket/Display.html?id=46691 68 # If the plugin is loaded twice in different templates (one INCLUDEd into 69 # another) then the filter gets garbage collected when the inner template 70 # ends (at least, I think that's what's happening). So I'm going to take 71 # the "suck it and see" approach, comment it out, and wait for someone to 72 # complain that this module is leaking memory. 73 74 # weaken($this); 75 76 if ($self->{ _DYNAMIC }) { 77 return $self->{ _DYNAMIC_FILTER } ||= [ sub { 78 my ($context, @args) = @_; 79 my $config = ref $args[-1] eq 'HASH' ? pop(@args) : { }; 80 81 return sub { 82 $this->filter(shift, \@args, $config); 83 }; 84 }, 1 ]; 85 } 86 else { 87 return $self->{ _STATIC_FILTER } ||= sub { 88 $this->filter(shift); 89 }; 90 } 91} 92 93sub filter { 94 my ($self, $text, $args, $config) = @_; 95 return $text; 96} 97 98 99sub merge_config { 100 my ($self, $newcfg) = @_; 101 my $owncfg = $self->{ _CONFIG }; 102 return $owncfg unless $newcfg; 103 return { %$owncfg, %$newcfg }; 104} 105 106 107sub merge_args { 108 my ($self, $newargs) = @_; 109 my $ownargs = $self->{ _ARGS }; 110 return $ownargs unless $newargs; 111 return [ @$ownargs, @$newargs ]; 112} 113 114 115sub install_filter { 116 my ($self, $name) = @_; 117 $self->{ _CONTEXT }->define_filter( $name => $self->factory ); 118 return $self; 119} 120 121 122 1231; 124 125__END__ 126 127=head1 NAME 128 129Template::Plugin::Filter - Base class for plugin filters 130 131=head1 SYNOPSIS 132 133 package MyOrg::Template::Plugin::MyFilter; 134 135 use Template::Plugin::Filter; 136 use base qw( Template::Plugin::Filter ); 137 138 sub filter { 139 my ($self, $text) = @_; 140 141 # ...mungify $text... 142 143 return $text; 144 } 145 146 # now load it... 147 [% USE MyFilter %] 148 149 # ...and use the returned object as a filter 150 [% FILTER $MyFilter %] 151 ... 152 [% END %] 153 154=head1 DESCRIPTION 155 156This module implements a base class for plugin filters. It hides 157the underlying complexity involved in creating and using filters 158that get defined and made available by loading a plugin. 159 160To use the module, simply create your own plugin module that is 161inherited from the C<Template::Plugin::Filter> class. 162 163 package MyOrg::Template::Plugin::MyFilter; 164 165 use Template::Plugin::Filter; 166 use base qw( Template::Plugin::Filter ); 167 168Then simply define your C<filter()> method. When called, you get 169passed a reference to your plugin object (C<$self>) and the text 170to be filtered. 171 172 sub filter { 173 my ($self, $text) = @_; 174 175 # ...mungify $text... 176 177 return $text; 178 } 179 180To use your custom plugin, you have to make sure that the Template 181Toolkit knows about your plugin namespace. 182 183 my $tt2 = Template->new({ 184 PLUGIN_BASE => 'MyOrg::Template::Plugin', 185 }); 186 187Or for individual plugins you can do it like this: 188 189 my $tt2 = Template->new({ 190 PLUGINS => { 191 MyFilter => 'MyOrg::Template::Plugin::MyFilter', 192 }, 193 }); 194 195Then you C<USE> your plugin in the normal way. 196 197 [% USE MyFilter %] 198 199The object returned is stored in the variable of the same name, 200'C<MyFilter>'. When you come to use it as a C<FILTER>, you should add 201a dollar prefix. This indicates that you want to use the filter 202stored in the variable 'C<MyFilter>' rather than the filter named 203'C<MyFilter>', which is an entirely different thing (see later for 204information on defining filters by name). 205 206 [% FILTER $MyFilter %] 207 ...text to be filtered... 208 [% END %] 209 210You can, of course, assign it to a different variable. 211 212 [% USE blat = MyFilter %] 213 214 [% FILTER $blat %] 215 ...text to be filtered... 216 [% END %] 217 218Any configuration parameters passed to the plugin constructor from the 219C<USE> directive are stored internally in the object for inspection by 220the C<filter()> method (or indeed any other method). Positional 221arguments are stored as a reference to a list in the C<_ARGS> item while 222named configuration parameters are stored as a reference to a hash 223array in the C<_CONFIG> item. 224 225For example, loading a plugin as shown here: 226 227 [% USE blat = MyFilter 'foo' 'bar' baz = 'blam' %] 228 229would allow the C<filter()> method to do something like this: 230 231 sub filter { 232 my ($self, $text) = @_; 233 234 my $args = $self->{ _ARGS }; # [ 'foo', 'bar' ] 235 my $conf = $self->{ _CONFIG }; # { baz => 'blam' } 236 237 # ...munge $text... 238 239 return $text; 240 } 241 242By default, plugins derived from this module will create static 243filters. A static filter is created once when the plugin gets 244loaded via the C<USE> directive and re-used for all subsequent 245C<FILTER> operations. That means that any argument specified with 246the C<FILTER> directive are ignored. 247 248Dynamic filters, on the other hand, are re-created each time 249they are used by a C<FILTER> directive. This allows them to act 250on any parameters passed from the C<FILTER> directive and modify 251their behaviour accordingly. 252 253There are two ways to create a dynamic filter. The first is to 254define a C<$DYNAMIC> class variable set to a true value. 255 256 package MyOrg::Template::Plugin::MyFilter; 257 use base 'Template::Plugin::Filter'; 258 our $DYNAMIC = 1; 259 260The other way is to set the internal C<_DYNAMIC> value within the C<init()> 261method which gets called by the C<new()> constructor. 262 263 sub init { 264 my $self = shift; 265 $self->{ _DYNAMIC } = 1; 266 return $self; 267 } 268 269When this is set to a true value, the plugin will automatically 270create a dynamic filter. The outcome is that the C<filter()> method 271will now also get passed a reference to an array of postional 272arguments and a reference to a hash array of named parameters. 273 274So, using a plugin filter like this: 275 276 [% FILTER $blat 'foo' 'bar' baz = 'blam' %] 277 278would allow the C<filter()> method to work like this: 279 280 sub filter { 281 my ($self, $text, $args, $conf) = @_; 282 283 # $args = [ 'foo', 'bar' ] 284 # $conf = { baz => 'blam' } 285 } 286 287In this case can pass parameters to both the USE and FILTER directives, 288so your filter() method should probably take that into account. 289 290 [% USE MyFilter 'foo' wiz => 'waz' %] 291 292 [% FILTER $MyFilter 'bar' biz => 'baz' %] 293 ... 294 [% END %] 295 296You can use the C<merge_args()> and C<merge_config()> methods to do a quick 297and easy job of merging the local (e.g. C<FILTER>) parameters with the 298internal (e.g. C<USE>) values and returning new sets of conglomerated 299data. 300 301 sub filter { 302 my ($self, $text, $args, $conf) = @_; 303 304 $args = $self->merge_args($args); 305 $conf = $self->merge_config($conf); 306 307 # $args = [ 'foo', 'bar' ] 308 # $conf = { wiz => 'waz', biz => 'baz' } 309 ... 310 } 311 312You can also have your plugin install itself as a named filter by 313calling the C<install_filter()> method from the C<init()> method. You 314should provide a name for the filter, something that you might 315like to make a configuration option. 316 317 sub init { 318 my $self = shift; 319 my $name = $self->{ _CONFIG }->{ name } || 'myfilter'; 320 $self->install_filter($name); 321 return $self; 322 } 323 324This allows the plugin filter to be used as follows: 325 326 [% USE MyFilter %] 327 328 [% FILTER myfilter %] 329 ... 330 [% END %] 331 332or 333 334 [% USE MyFilter name = 'swipe' %] 335 336 [% FILTER swipe %] 337 ... 338 [% END %] 339 340Alternately, you can allow a filter name to be specified as the 341first positional argument. 342 343 sub init { 344 my $self = shift; 345 my $name = $self->{ _ARGS }->[0] || 'myfilter'; 346 $self->install_filter($name); 347 return $self; 348 } 349 350 [% USE MyFilter 'swipe' %] 351 352 [% FILTER swipe %] 353 ... 354 [% END %] 355 356=head1 EXAMPLE 357 358Here's a complete example of a plugin filter module. 359 360 package My::Template::Plugin::Change; 361 use Template::Plugin::Filter; 362 use base qw( Template::Plugin::Filter ); 363 364 sub init { 365 my $self = shift; 366 367 $self->{ _DYNAMIC } = 1; 368 369 # first arg can specify filter name 370 $self->install_filter($self->{ _ARGS }->[0] || 'change'); 371 372 return $self; 373 } 374 375 sub filter { 376 my ($self, $text, $args, $config) = @_; 377 378 $config = $self->merge_config($config); 379 my $regex = join('|', keys %$config); 380 381 $text =~ s/($regex)/$config->{ $1 }/ge; 382 383 return $text; 384 } 385 386 1; 387 388=head1 AUTHOR 389 390Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/> 391 392=head1 COPYRIGHT 393 394Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved. 395 396This module is free software; you can redistribute it and/or 397modify it under the same terms as Perl itself. 398 399=head1 SEE ALSO 400 401L<Template::Plugin>, L<Template::Filters>, L<Template::Manual::Filters> 402 403=cut 404 405# Local Variables: 406# mode: perl 407# perl-indent-level: 4 408# indent-tabs-mode: nil 409# End: 410# 411# vim: expandtab shiftwidth=4: 412