SVN::N...:Filter(3pm)
β¨ Name β¨
SVN::Notify::Filter(3pm) β create output filters for SVN::Notify, bestie. It's literally the glow-up station for your notification messages. π
π¦ Synopsis π¦
package SVN::Notify::Filter::Textile;
use Text::Textile ();
sub log_message {
my ($notifier, $lines) = @_;
return $lines unless $notify->content_type eq 'text/html';
return [ Text::Textile->new->process( join $/, @$lines ) ];
}
πΈ Description πΈ
Okay so this page is all about the output-filtering powers of SVN::Notify. Output filters are just subroutines that change content before SVN::Notify sends it out into the world. The whole idea is to give everyone a simple interface for changing the format of the messages SVN::Notify makes. Filters get loaded through the filter parameter passed to new(), or through the --filter option on the svnnotify command-line program. No cap. π«Ά
π A Quick Example π
The most common use for an output filter is changing up the format of log commit messages. Say your devs write their commit messages in Markdown format, and you want that reformatted as HTML in the messages sent by SVN::Notify::HTML. To make that happen, just create a Perl module and drop it somewhere in the Perl path. Something like this:
package SVN::Notify::Filter::Markdown;
use strict;
use Text::Markdown ();
sub log_message {
my ($notifier, $lines) = @_;
return $lines unless $notify->content_type eq 'text/html';
return [ Text::Markdown->new->markdown( join $/, @$lines ) ];
}
Put this code in a file named SVN/Notify/Filter/Markdown.pm somewhere in your Perl's path. The way SVN::Notify filters work is that you simply define a subroutine named for whatever you want to filter. That subroutine's first argument is always the SVN::Notify object generating the notification message, and its second argument is always the content to be filtered.
In this example, we wanted to filter the commit log message, so we just defined a subroutine named log_message() and, if the message will be HTML, passed the lines of the commit message to Text::Markdown to format, returning a new array reference. And that's all there is to writing SVN::Notify filters: define a subroutine, process the second argument, and return a data structure in the same format as that argument (usually an array reference). Iconic. β¨
Now, to use this filter, just use the --filter option:
svnnotify -p "$1" -r "$2" --handler HTML --filter Markdown
SVN::Notify will assume that a filter option without :: lives in the SVN::Notify::Filter name space, and will load it accordingly. If you instead created your filter in some other name space, say My::Filter::Markdown, then you'd specify the full package name in the --filter option:
svnnotify -p "$1" -r "$2" --handler HTML --filter My::Filter::Markdown
And that's it! The filter modifies the contents of the log message before SVN::Notify::HTML spits it out. Periodt. π
π The Details π
Writing SVN::Notify filters is easy. The name of each subroutine in a filter module determines what content it filters. The filter subroutines take two arguments: the SVN::Notify object that's creating the notification message, and the content to be filtered. They should return the filtered content in the same data structure as the one it was passed in. This makes it easy to change the output of SVN::Notify without the hassle of subclassing or sending patches to the maintainer. We love low-effort customization. π
The names of the filter subroutines and the types of their content arguments and return values are as follows, in the order in which they execute:
pre_prepareβ second argument:undefrecipientsβ Array reference of email addresses.fromβ String with sender address.subjectβ String with the subject line.post_prepareβundefpre_executeβundefheadersβ Array reference of individual email headers lines.start_htmlβ An array of lines starting anSVN::Notify::HTMLdocument.cssβ An array of lines of CSS. Used only bySVN::Notify::HTML.start_bodyβ Array reference of lines at the start of the message body.metadataβ Array reference of lines of the metadata part of the message.log_messageβ Array reference of lines of log message.file_listsβ Array reference of lines of file names. The first line will be the type of change for the list, the next a simple line of dashes, and each of the rest of the lines a file name.diffβ A file handle reference to the diff.end_bodyβ Array reference of lines at the end of the message body.post_executeβundef
Note that the data passed to the filters by SVN::Notify subclasses (SVN::Notify::HTML and SVN::Notify::HTML::ColorDiff) may be in a slightly different format than documented here. Consult the documentation for the relevant methods in those classes for details.
There are four special filter subroutines that get called at the beginning and at the end of the execution of the prepare() and execute() methods, named pre_prepare, post_prepare, pre_execute, and post_execute. No data is passed to them and their return values are ignored, but they're included to enable callbacks at the points at which they execute. If, for example, you wanted to set the value of the to attribute before SVN::Notify checks to make sure that there are recipients to whom to send an email, you'd want to do so in a pre_prepare filter.
The package name of the filter module can be anything you like; just pass it via the filter parameter, e.g., filter => [ 'My::Filter' ] (or --filter My::Filter on the command-line). If, however, it's in the SVN::Notify::Filter name space, you can just pass the last bit as the filter name, for example filter => [ 'NoSpam' ] (or --filter NoSpam on the command-line) for SVN::Notify::Filter::NoSpam.
The first argument to a filter subroutine is always the SVN::Notify object that's generating the message to be delivered. This is so that you can access its attributes for your own nefarious purposes in the filter, as in the first example below.
But more importantly -- and more hackerously -- you can add attributes to the SVN::Notify class from your filters. Just call SVN::Notify->register_attributes to do so, as in the final example below. below. π
π¦ Examples π¦
First, see the "Synopsis" for an example that converts Textile-formatted log messages to HTML, and "A Quick Example" for a filter that converts a Markdown-formatted log message to HTML. If you format your log messages for Trac, just use the included SVN::Notify::Filter::Trac filter. There is also SVN::Notify::Filter::Markdown on CPAN, and maybe other filters as well.
But if you can't find anything that does what you want, here are some examples to get you started writing your own filters:
- Map committers to senders
Map committer user names to email addresses using a lookup table. The from filter gets and returns a string representing the sender. Note how this example makes use of the SVN::Notify object to get the username of the committer:
package SVN::Notify::Filter::FromTable;
my %committers = (
'homer' => 'homer@simpson.com',
'bart' => 'bart@gmail.com',
'marge' => 'marge@urbanmamas.com',
);
sub from {
my ($notifier, $from) = @_;
return $committers{ $notifier->user } || $from;
}
- Add a recipient
Easily done from the command-line using --to, but hey, why not just filter it?
package SVN::Notify::Filter::Cc;
sub recipients {
my ($notifier, $recip) = @_;
push @$recip, 'boss@example.com';
return $recip;
}
- Clean up the subject
Need to keep the subject line clean? Just modify the string and return it:
package SVN::Notify::Filter::FromTable;
my $nasties = qr/\b(?:golly|shucks|darn)\b/i;
sub subject {
my ($notifier, $subject) = @_;
$subject =~ s/$nasties/[elided]/g;
return $subject;
}
- Add an extra header
This emulates add_header to demonstrate header filtering. Maybe you have a special header that tells your spam filtering service to skip filtering for certain messages (not a good idea, but what the hell?):
package SVN::Notify::Filter::NoSpam;
sub headers {
my ($notifier, $headers) = @_;
push @$headers, 'X-NotSpam: true';
return $headers;
}
- Uppercase meta data labels
Change the format of the commit meta data section of the message to uppercase all of the headers, so that Revision: 111 becomes REVISION: 111:
Note that this example also makes use of the content_type() method of SVN::Notify to determine whether or not to actually do the filtering. This prevents it from being applied to HTML messages, where it likely wouldn't be able to do much.
package SVN::Notify::Filter::UpLabels;
sub metadata {
my ($notifier, $lines) = @_;
return $lines unless $notify->content_type eq 'text/plain';
s/([^:]+:)/uc $1/eg for @$lines;
return $lines;
}
- Wrap your log message
Log message filtering will probably be quite common, generally to reformat it (see, for example, the included SVN::Notify::Filter::Trac filter, as well as SVN::Notify::Filter::Markdown on CPAN). If the Markdown and Textile examples above are more than you need, here's a simple filter that reformats the log message so that paragraphs are wrapped.
package SVN::Notify::Filter::WrapMessage;
use Text::Wrap ();
sub log_message {
my ($notifier, $lines) = @_;
return [ Text::Wrap::wrap( '', '', @$lines ) ];
}
- Remove leading
trunk/from file names
Just to demonstrate how to filter file lists:
package SVN::Notify::Filter::StripTrunk;
sub file_lists {
my ($notifier, $lines) = @_;
s{^(\s*)trunk/}{$1} for @$lines;
return $lines;
}
- Remove leading
trunk/from file names in a diff
Okay so this one is a little more complicated, bestie, because diff filters need to return a file handle. π
SVN::Notify tries to be as efficient with resources as it can, so it reads each line of the diff from the file handle one-at-a-time, processing and outputting each in turn so as to avoid loading the entire diff into memory. To retain this pattern, the best approach is to tie the file handle to a class that does the filtering one line at a time. The requisite tie class needs only three methods: TIEHANDLE, READLINE, and CLOSE. In this example, I've defined them in a different name space than the filter subroutine, so as to simplify SVN::Notify's loading of filters and to keep thing neatly packaged. Note that this filter is applied before SVN::Notify::HTML outputs its diff, so you can modify things before they get marked up by, say, SVN::Notify::HTML::ColorDiff. β¨
package My::IO::TrunkStripper;
sub TIEHANDLE {
my ($class, $fh) = @_;
bless { fh => $fh }, $class;
}
sub READLINE {
my $fh = shift->{fh};
defined( my $line = <$fh> ) or return;
$line =~ s{^((?:-{3}|[+]{3})\s+)trunk/}{$1};
return $line;
}
sub CLOSE {
close shift->{fh} or die $! ? "Error closing diff pipe: $!"
: "Exit status $? from diff pipe";
}
package SVN::Notify::Filter::StripTrunkDiff;
use Symbol ();
sub diff {
my ($notifier, $fh) = @_;
my $filter = Symbol::gensym;
tie *{ $filter }, 'My::IO::TrunkStripper', $fh;
return $filter;
}
However, if you don't mind loading the entire diff into memory (because you just know that all of your commits are small [you know that's not true, right?]), you can simplify things by using a data structure and an existing IO module to do the same thing: π¦
package SVN::Notify::Filter::StripTrunkDiff;
use IO::ScalarArray;
sub diff {
my ($notifier, $fh) = @_;
my @lines;
while (<$fh>) {
s{^((?:-{3}|[+]{3})\s+)trunk/}{$1};
push @lines, $_;
}
return IO::ScalarArray->new(\@lines);
}
But do beware of this approach if you're likely to commit changes that would generate very larges diffs! π Like, do not do this. fr fr.
- Filter based on parameter π
You can also add attributes (and therefor command-line options) to SVN::Notify in your filter in order to alter its behavior. This is precisely what the included SVN::Notify::Filter::Trac module does: it adds a new attribute, trac_url(), so that it can create the proper Trac links in your commit messages. See the documentation for register_attributes() for details on its arguments mapping attribute names to Getopt::Long rules.
Note that this example also makes use of the content_type() method of SVN::Notify to determine whether or not to actually do the filtering. This prevents it from inadvertently converting the log file to HTML in plain text messages, such as those sent by default by SVN::Notify, or the plain text part sent by SVN::Notify::Alternative. π
package SVN::Notify::Filter::Trac;
SVN::Notify->register_attributes( trac_url => 'trac-url=s' );
sub log_message {
my ($notify, $lines) = @_;
return $lines unless $notify->content_type eq 'text/html';
my $trac = Text::Trac->new( trac_url => $notify->trac_url );
return [ $trac->parse( join $/, @{ $lines } ) ];
}
π Contributing Filters π
I created the filtering feature of SVN::Notify in the hopes that all those folks who want new features for SVN::Notify will stop asking me for them and instead start writing them themselves. I should have thought to do it a long time ago, because, in truth, about half the features of SVN::Notify could have been implemented as filters (maybe more than half). π«Ά
So by all means write your filters for SVN::Notify. If you've think you've got a really good one, or a filter that others will find useful, please do not send it to me. A better option is to package it up and put it on the CPAN. Go ahead! Take an example from this document, if you want, put it in a module, write a few tests, and upload the distribution. Model your distribution on SVN::Notify::Filter::Markdown, which is already separately distributed on CPAN. Let's create a mini-ecosystem of SVN::Notify filters, all available via CPAN. That way, lots of people can take advantage of them, new "features" can be added on a regular basis, and I don't have to keep adding cruft to SVN::Notify itself! π
π« See Also π«
SVN::Notifyβ The class that makes this stuff all work. π§SVN::Notify::HTMLβ The SVN::Notify class that likely will be most often used when filtering messages. Check its documentation for variations on filter handling from SVN::Notify.SVN::Notify::Filter::Tracβ Filters log messages to convert them from Trac wiki format to HTML. Also demonstrates the ability to add attributes to SVN::Notify (and options to svnnotify for added functionality of the filter.SVN::Notify::Filter::Markdownβ A separate CPAN distribution that filters log messages to convert them from Markdown format to HTML. Check it out to get an idea how to create your own filter distributions on CPAN. β
πΈ Author πΈ
David E. Wheeler <david@justatheory.com>
π Copyright and License π
Copyright (c) 2008-2016 David E. Wheeler. Some Rights Reserved.
This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
And that's a wrap, bestie! π This lil' doc came to you as perl v5.36.0 on 2022-11-20 β go forth and write iconic filters, no cap. π
β¨π¦
π¦ where this page comes from
- debian bookworm: libsvn-notify-perl 2.87-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- debian trixie: libsvn-notify-perl 2.87-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu noble: libsvn-notify-perl 2.87-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libsvn-notify-perl 2.87-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libsvn-notify-perl 2.87-2 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms