A piggy bank of commands, fixes, succinct reviews, some mini articles and technical opinions from a (mostly) Perl developer.

XSLT math

Using math in XSLT: http://www.xml.com/pub/a/2001/05/07/xsltmath.html

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes"/>

<xsl:template match="numbers">
A. 4 + 3.2 = <xsl:value-of select="x + y"/>
B. 3.2 - 4 = <xsl:value-of select="y - x"/>
C. 4 * 3.2 = <xsl:value-of select="x * y"/>
D. 11/3.2 = <xsl:value-of select="z div y"/>
E. 4 + 3.2 * 11 = <xsl:value-of select="x+y*z"/>
F. (4 + 3.2) * 11 = <xsl:value-of select="(x+y)*z"/>
G. 11 mod 4 = <xsl:value-of select="z mod x"/>
H. 4 + 3.2 + 11 = <xsl:value-of select="sum(*)"/>
I. floor(3.2) = <xsl:value-of select="floor(y)"/>
J. ceiling(3.2) = <xsl:value-of select="ceiling(y)"/>
K. round(3.2) = <xsl:value-of select="round(y)"/>
L. 11 + count(*) = <xsl:value-of select="11+count(*)"/>
M. 3.2 + string-length("3.2") =
<xsl:value-of select="y + string-length(y)"/>
N. 11 + "hello" = <xsl:value-of select="z + 'hello'"/>
</xsl:template>

</xsl:stylesheet>


Warning With some XSLT processors, the use of decimal numbers may introduce a tiny error. For example, the "3.2 - 4" in this example comes out as "-.7999999999999998" on some processors. While being off by .0000000000000002 isn't much, being off at all shows that math is not XSLT's strong point.

Display output of XSL transformation using PHP

Use XAMPP or similar to serve (reportedly works on PHP 5):

<?
$query = $_GET['q'];
$filePath = 'http://source-server:8889/page/'.$query;

$xslDoc = new DOMDocument();
$xslDoc->load("http://localhost/xslt/page_render.xsl");
$xmlDoc = new DOMDocument();
$xmlDoc->load( $filePath );

$proc = new XSLTProcessor();

$proc->importStylesheet($xslDoc);
echo $proc->transformToXML($xmlDoc);
?>

Screen capture for long pages in firefox

The Firefox add-on Aviary is a very powerful screenshot program that can easily capture a really long web page as an image.

No XSLT2 in PHP

There is only one known xslt2 processor called SAXON (http://saxon.sourceforge.net/), built by Michael Kay, which was released for the Java and .NET platforms.

Other XSLT processors, including Sablotron, Xalan and MSXML, have not yet adopted xslt2.

Kay writes: "implementation [for xslt2 in php] does not exist because no-one has written one". He continues, "The usual workaround I recommend is to implement XSLT 2.0 transformation as a (Java-based or .NET-based) REST web service, and for the php application to submit transformation requests to that web service by means of HTTP requests. An alternative is to use a php-Java bridge"

Michael Kay manages an email-based discussion group called MulberryTech, a good resource for learning XSLT techniques (both 1 and 2). (http://www.mulberrytech.com/xsl/xsl-list/)


Don't display dynamic elements until page has loaded

<head>
<script type="text/javascript">

function getRefToDiv(divID,oDoc) {
if( document.getElementById ) {
return document.getElementById(divID); }
if( document.all ) {
return document.all[divID]; }
if( !oDoc ) { oDoc = document; }
if( document.layers ) {
if( oDoc.layers[divID] ) { return oDoc.layers[divID]; } else {
//repeatedly run through all child layers
for( var x = 0, y; !y && x < oDoc.layers.length; x++ ) {
//on success, return that layer, else return nothing
y = getRefToDiv(divID,oDoc.layers[x].document); }
return y; } }
return false;
}

function toggleDiv(divID_as_a_string,action) {
//get a reference as above ...
myReference = getRefToDiv(divID_as_a_string);
if( !myReference ) {
//window.alert('Nothing works in this browser');
return; //don't go any further
}
//now we have a reference to it
if( myReference.style ) {
//DOM & proprietary DOM
if (action == 'on') {
myReference.style.visibility = 'visible';
} else {
myReference.style.visibility = 'hidden';
}
} else {
//layers syntax
if (action == 'on') {
myReference.visibility = 'show';
} else {
myReference.visibility = 'hide';
}
}
}

function start() {
toggleDiv('slider','on');
}

window.onload = start;
&lt/script>
</head>

<body>
<div id="slider" style="visibility: hidden">
</body>

(If, Then, Else) LOGIC in XSL

Syntax:
<xsl:if test="XXX">
<!-- do something -->
</xsl:if>

<xsl:choose>
<xsl:when test="XXX">
<!-- do one thing -->
</xsl:when>
<xsl:when test="YYY">
<!-- do another thing -->
</xsl:when>
<xsl:otherwise>
<!-- do default thing -->
</xsl:otherwise>
</xsl:choose>


Logic examples:
<xsl:when test="matches(sql:feedurl_id, '^(\d+)$')">
<xsl:if test="$canonical-urls/agg:canonical-url[text()=$canonical-link-href][1]/@module = $module-type">
<xsl:when test="$module-type = 'knowledge'">
<xsl:when test="$module-type='iplayer' or $module-type='schedule'">
<xsl:if test="$debug">
<xsl:when test="document('')//this:modules/this:module[@type=$module-type]/this:merge-feeds">
<xsl:if test="position()=1">
<xsl:if test="position()=3 and count(agg:module)=0">
<xsl:when test="starts-with($name,'profile') or starts-with($name,'weather')">
<xsl:when test="starts-with(@type,'profile')">
<xsl:when test="@type='weather'">
<xsl:when test="count($modules/agg:modules/agg:module[starts-with(@type,'profile')])>0">

Histogram with perl

perl -lane'($i) = $F[4] =~ m{(\d+)\.}; $m=100; $s=10; for ($c=0; $c<=$m; $c+=$s) { if (($i >= $c) && ($i < $c+$s)) { $h{$c}++; } };END{ for ($c=0; $c<=$m; $c+=$s) { print "$c : ".("#" x $h{$c}); }}'

$i = the value with which you want to make a histogram

$h = hashref holding the histogram data
$s = histogram step size
$m = maximum histogram value

$c = loop counter

Cocoon XSLT errors

java.lang.NullPointerException

This could mean you've used the wrong attribute name in a tag, e.g.
<xsl:attribute type="type">
instead of
<xsl:attribute name="type">

There seems to be no syntax checking to warn in cases like this.

Basic Javascript

if( document.getElementById ){
var myReference = document.getElementById('divID');
}

XML::Validator::Schema

#!/usr/bin/perl

use XML::SAX::ParserFactory;
use XML::Validator::Schema;
# create a new validator object, using foo.xsd
$validator = XML::Validator::Schema->new(file => 'foo.xsd');
# create a SAX parser and assign the validator as a Handler
$parser = XML::SAX::ParserFactory->parser(Handler => $validator);
# validate foo.xml against foo.xsd
eval { $parser->parse_uri('foo.xml') };
die "File failed validation: $@" if $@;

The problem is:

Validating with XML::XPath

use XML::XPath; # update: use XML::LibXML and XML::LibXML::XPathContext instead
use Test::More qw(no_plan);

my $id = 298;
my $xml = &get_url("/$id")->{xml};
my $xp = XML::XPath->new( xml => $xml );
my $r = '/feed'; # root
ok( $xp->exists($r), "there should be a feed" );
ok( $xp->exists("$r/id"), "there should be a feed ID" );
ok( $xp->find("$r/id") =~ m{/$id}, "feed ID should be correct" );
ok( $xp->exists("$r/link[\@rel=\"self\"]"), "rel=self attr is correct" );
ok( ($xp->find("$r/link")->get_nodelist)[0]->getAttribute('href'), "href link exists" ); # this is ugly
check( $xp->find("$r/updated") => like => qr/^\d{4}\-\d\d\-\d\dT\d\d:\d\d:\d\d\.\d+Z$/ );

my $entries = $xp->find("/feed/entry"); # find all entry sections
ok( $entries, "there are entries" );
if ($entries->size) {
my $e = "/entry";
foreach my $node ($entries->get_nodelist) {
# it's bloody stupid transforming the XML::XPath object to a string and then parsing back to an object
# instead maybe loop through each node in the list using xpath [0] [1], etc?
# But why should I have to hack around this....
my $entry = XML::XPath->new( xml => $node->toString );
ok( $entry->exists("$e/title"), "title exists" );
}
}

# Conclusion: XML::XPath is bad for writing tests.

Crontab format (V3)

MAILTO=me@there.co.uk

# minute (0-59),
# | hour (0-23),
# | | day of the month (1-31),
# | | | month of the year (1-12),
# | | | | day of the week (0-6 with 0=Sunday).
# | | | | | commands
# 3 2,3,4 * * 0,6 /command to run on more than one day/hour
# 3 2,4-7 * * 1-5 /another/command to run on a range of days/hours

04 10 * * * /command/here 2>> /path/to/log/prog.$(date +\%Y\%m\%d).log

NOTES:
  • You must escape percent signs with a backslash
  • Putting them in double quotes doesn't work
  • Putting in double quotes and backslashes works, but the backslashes are written as part of the filename. So don't so that.

Perl test template

Comments: Logging may be mostly useful for debugging -- the log level can be raised for release.

#!/usr/bin/perl

#################################################################################
# Description of tests
#################################################################################

use strict;
use warnings;

use Test::More qw(no_plan);
use Test::Differences; # eq_or_diff()
use Test::Lazy qw/try check/; # check() displays the expected & actual upon failure, even for scalars
use Log::Log4perl;
use XML::Simple qw(:strict);

Log::Log4perl::init('conf/log4perl-test.conf');
my $LOG = Log::Log4perl->get_logger('log4perl.appender.LOGFILE');

$| = 1; select STDERR;
$| = 1; select STDOUT;

$LOG->info("Started tests");

use_ok('MyNamespace::MyModule');

dies_ok { MyNamespace::MyModule->new } 'fail to instantiate without parameters';

my $m = MyNamespace::MyModule->new( url => "foo", name => 'bar');
isa_ok($m, 'MyNamespace::MyModule');