I've just released SVN::Web 0.42. This release is a mix of bug fixes and architectural changes to make it easier for people to build on SVN::Web.
One of the big changes is that now, instead of calling die() at various points, the majority of the code is now exception based, using SVN::Web::X (which derives from Exception::Class).
I'm not convinced that I'm quite using exceptions in the 'proper' fashion. Instead of having different classes of exception (::PathIsMissing,
Since SVN::Web only has two notional layers (SVN::Web, which dispatches to SVN::Web:: actions, and the actions themselves) I don't think that I'm missing much here.
There's a handful of new functionality -- you can now easily view diffs between arbitrary revisions of a file, for instance, which is quite handy.
And a few things have changed under the hood to make it easier for third-party SVN::Web:: to hook in and behave like fully integrated parts of the original codebase. This will make it much easier for my next trick, SVN::Web::Search, which will put a search interface on top of all the log messages, revisions, dates, and authors in your repository...
As described in an earlier entry, new versions of SVN::Web escaped over the weekend. First there was 0.40, which contained the bulk of the new code. Then 0.41 followed when I realised I'd made a few packaging cock ups.
One of the things you can do with the new version is pass SVN commit messages through multiple TT filters. Each filter can decorate the commit message in some way -- recognise URLs, e-mail addresses, RT ticket numbers, subversion revision numbers, etc, and convert them in to hyperlinks.
Say you've written "Fixes rt#1234" in your commit message. You'd like "rt#1234" to link straight to your bug database.
I thought that would be easy. Obviously, in straight Perl it's just the equivalent of:
$msg =~ s/rt#(\d+)/<a href="/rt.cgi?t=$1">$1</a>/;
I was quite surprised that TT doesn't offer this functionality. The built in replace method/filter doesn't cut it, as it doesn't handle backreferences. The TT:
[% msg.replace('rt#(\d+)', '<a href="/rt.cgi?t=$1">$1</a>') %]
results in the literal $1 being inserted into the output instead of the expected back reference.
The two popular suggestions in the TT list archives are to either use s/// directly in a [% PERL %] block, or write a custom filter to do the specific search and replace.
But [% PERL %] blocks might not be enabled in every TT install. And writing multiple filters (one to recognise RT tickets, one for subversion revision numbers, one for Sourceforge tickets,
Hence, Template::Plugin::Subst. With it, you can write:
[% msg.subst('rt#(\d+)', '<a href="/rt.cgi?t=$1">$1</a>') %]
and it will do the right thing. And as well as working as vmethod, it also works as a filter, so you can use that syntax instead.
The other thing that TT can't easily and cleanly do is stack multiple filters together, ignoring the ones that aren't installed.
SVN::Web ships with a config file that specifies a number of TT plugins to filter log messages through (to convert them to HTML, and make various things in the log clickable). These include plugins that might not be installed. In TT you can't say "Run all these filters, ignoring the ones that aren't installed". Trying to use a missing plugin (sensibly) generates a run-time error.
So, SVN::Web's log_msg_filter() implements this functionality. And the default SVN::Web configuration file can safely specify several different plugins, without the user having to worry about whether or not they're installed. Without them, everything works, and if they are installed then the user gets a slightly nicer output.
At the moment this is hardcoded in to SVN::Web, but I plan on releasing Template::Plugin::MultiFilter in the near future so that others can easily use this functionality.
I've been spending much of the last few weeks polishing up SVN::Web. It's a simple way to browse SVN repositories online, view diffs, compare revisions, check out log entries, and so forth.
Originally written by Chia-liang Kao, of SVK fame, he's handed the maintainer reins over to me.
Changes in the new version include:
An overhauled set of templates, so the out-of-the-box experience is much nicer.
A 'view' action that shows the contents of a file, along with the most recent log message.
More customisation options, making it easier to add or remove actions from the list of things that SVN::Web is allowed to do.
Added huge swathes of information to the documentation. It should now be much easier for anyone else to contribute code and/or templates to SVN::Web.
A fix for rt#12431.
A mechanism to 'stack' Template Toolkit filters. You can run easily run log messages through an arbitrary number of filters, without causing problems if one or more of the filters is not installed.
Inline display of some content from the repository where appropriate (e.g., viewing an image works properly).
Fixed a bug that meant the results from the automated tests weren't valid. Fixed the bugs that this uncovered.
And much more. It'll be hitting CPAN shortly. In the meantime, you can see the code (and the new, shiny, trac inspired interface) here.
I sent this to the london.pm mailing list, and was surprised to get few responses. That probably means it's a hard problem. Still...
I'm having a stab at reducing the memory footprint of a Perl application I'm working with. When it's running it's ~80MB resident. I know it plays fast and loose with memory, allocating hashes and the like at the drop of a hat. What I'd like to know are the long hanging fruit that I can try and recode to be a little more memory friendly.
Devel::Symdump + Devel::Size gets me some of the way there. I can instrument the application by waiting until it's gone through its initialisation phase (after which the memory usage is reasonably constant), and then walk the symbol table, dumping the size of everything that's found.
But, natch, that won't work for lexical variables, many of which have been created in subs and had references returned.
Poking through CPAN hasn't turned up anything useful (although some of the B:: namespace looks like it could be coerced in to this). Anyone got any pointers to approaches that are likely to work?
Any takers?
I've been busy over the past week producing four new Test::* modules.
These are a bit different from most other Test::* modules, which are normally written to help you test your code. These, however, are designed to help you test the environment that your code is going to be running in.
Test::Symlink lets you easily verify that a symlink exists, and that it links to the correct location.
symlink_ok($src => $dst);
Test::Unix::Group and Test:::Unix::User let you verify that a user, group, and home directory exist.
user_ok({ name => 'nik', uid => 1000}, 'nik has uid 1000');
homedir_ok({ name => 'nik', group => 'staff' },
"nik's homedir is owned by the staff group");
group_ok({ name => 'wheel', members => [qw(root nik)]},
"'wheel' has the correct members");
Finally, there's Test::Net::Connect, which lets you make sure that you can make a network connection to a particular host.
connect_ok({ host => 'www.example.com', port => 80 },
"Web server is reachable from here");
libtap made it to version 1.0 the other day, and there don't seem to be too many embarassing mistakes, so...
libtap is an implementation of the Test::Harness protocol for reporting test successes/failures (ok, not ok, skip, TODO, etc) in C, making it ideal for when you need to write test cases in C (Perl not available, Inline::C not available, that sort of thing). Full documentation is included.
More information, including links to the tarball, MD5 checksums, read-only access to the Subversion repository, and the bug/feature ticket system is at my Trac site.
This has only been tested on FreeBSD 5.x and 6.x at the moment; if you're on those systems then you can use ports/devel/libtap for ease of installation. Tests on other systems (and tickets reporting issues / patches to fix them) would be very welcome.
Last week I wrote about some initial progress I was making with libtap, an implementation of the Test::More / Test::Harness protocol for communicating test results for tests written in C.
Since then I've managed to make a bit more progress.
There's a website for it now. This is also my opportunity to experiment with trac, a combination wiki, ticket system, and subversion repo browser.
This means I've got tickets that let me keep track of suggestions, and see how much more work there is to go.
Functionality improved, as can be seen at the timeline and the roadmap.
I turned on anonymous SVN access, described here. As a handy side effect, source code to development versions of my CPAN modules is available too.
All I need to do now is work on closing the tickets...
... SPF.
More specifically, I am hating load-balancers that function by acting as DNS servers, handing out different A records for the services they balance depending on host load.
Self-same load-balancers can't publish TXT records...
Bad thing: My coffee machine's just blown up. There's ground coffee all over the kitchen.
Good thing: Krups' customer service. "Yes sir, just fax us a copy of the receipt and we'll ship a replacement to you free of charge."
I was expecting to have to box the old one up and send it back to them -- as Murphy dictates, I'd dumped the packaging it originally came in 2 weeks ago, something I do only after a bulky purchase has been in the house and working flawlessly for at least three months...
A few weeks back I did the work to make most of the FreeBSD regression test suite produce output that's compatible with Test::Harness and prove(1).
Back then there wasn't a name for that format. Now, of course, there is.
This was very much a rough cut. A lot of the tests were written in C, consisting of a few hundred (or in some cases a few thousand) ASSERT() calls, with a printf("PASS\n"); at the bottom -- if your test didn't dump core you knew it worked.
Changing most of those to add printf("1..1\n"); and printf("ok 1\n"); was the work of an afternoon.
On the TODO list was to implement as much of TAP as possible in C -- having plans, ok(), and so on. By doing that, and removing the ASSERT()s, a failing test doesn't prevent all the other tests from running, and you get a truer picture about how many tests overall are being run. And we all know that seeing the test count go up is a strong incentive to keep writing them.
So, here's a very early draft of that; libtap. Right now, plan(), ok(), and diag() functionality is there, which is almost all the way to what I need for FreeBSD regression suite.