1. Toby Inkster
  2. p5-rdf-query-functions-buzzword-gb

Commits

Toby Inkster  committed c5aa56b

Already on CPAN, adding to svn.

  • Participants
  • Branches RDF-Query-Functions-Buzzword-GB

Comments (0)

Files changed (13)

File Changes

View file
+############################################################################
+## Changes #################################################################
+############################################################################
+
+RDF-Query-Functions-Buzzword-GB
+===============================
+
+Created:      2011-07-13
+Home page:    <http://search.cpan.org/dist/RDF-Query-Functions-Buzzword-GB/>
+Bug tracker:  <http://rt.cpan.org/Dist/Display.html?Queue=RDF-Query-Functions-Buzzword-GB>
+Maintainer:   Toby Inkster <mailto:tobyink@cpan.org>
+
+0.001 [2011-07-13]
+

File Changes.ttl

View file
+#############################################################
+
+@prefix :        <http://usefulinc.com/ns/doap#> .
+@prefix dcs:     <http://ontologi.es/doap-changeset#> .
+@prefix dc:      <http://purl.org/dc/terms/> .
+@prefix foaf:    <http://xmlns.com/foaf/0.1/> .
+@prefix my:      <http://purl.org/NET/cpan-uri/dist/RDF-Query-Functions-Buzzword-GB/> .
+@prefix rdfs:    <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix toby:    <http://tobyinkster.co.uk/#> .
+@prefix xsd:     <http://www.w3.org/2001/XMLSchema#> .
+
+#############################################################
+
+<>
+
+	dc:title         "Changes" ;
+	dc:description   "Revision history for Perl extension RDF::Query::Functions::Buzzword::GB."@en ;
+	dc:subject       my:project ;
+	dc:creator       toby:i .
+
+#############################################################
+
+my:v_0-001
+
+	a               :Version ;
+	dc:issued       "2011-07-13"^^xsd:date ;
+	:revision       "0.001"^^xsd:string ;
+	:file-release   <http://backpan.cpan.org/authors/id/T/TO/TOBYINK/RDF-Query-Functions-Buzzword-GB-0.001.tar.gz> ;
+	rdfs:comment    "Original version"@en .
+
+#############################################################
+
+my:project
+
+	a               :Project ;
+	:name           "RDF-Query-Functions-Buzzword-GB" ;
+	:shortdesc      "plugin for buzzword.org.uk UK-specific functions"@en ;
+	:programming-language  "Perl" ;
+	:homepage       <http://search.cpan.org/dist/RDF-Query-Functions-Buzzword-GB/> ;
+	:download-page  <http://search.cpan.org/dist/RDF-Query-Functions-Buzzword-GB/> ;
+	:bug-database   <http://rt.cpan.org/Dist/Display.html?Queue=RDF-Query-Functions-Buzzword-GB> ;
+	:repository     [ a :SVNRepository ; :browse <http://goddamn.co.uk/viewvc/perlmods/RDF-Query-Functions-Buzzword-GB/> ] ;
+	:maintainer     toby:i ;
+	:developer      toby:i ;
+	:documenter     toby:i ;
+	:tester         toby:i ;
+	:created        "2011-07-13"^^xsd:date ;
+	:license        <http://dev.perl.org/licenses/> ;
+	:release        my:v_0-001 ;
+	rdfs:seeAlso    <http://buzzword.org.uk/2011/functions/gb> .
+
+#############################################################
+
+toby:i
+
+	a               foaf:Person ;
+	foaf:name       "Toby Inkster" ;
+	foaf:homepage   <http://tobyinkster.co.uk/> ;
+	foaf:page       <http://search.cpan.org/~tobyink/> ;
+	foaf:mbox       <mailto:tobyink@cpan.org> ;
+	<http://www.w3.org/2002/07/owl#sameAs> <http://purl.org/NET/cpan-uri/person/tobyink> .
+
+#############################################################

File Changes.xml

View file
+<?xml version="1.0" encoding="utf-8"?>
+<rdf:RDF xmlns:dbug="http://ontologi.es/doap-bugs#" xmlns:dc="http://purl.org/dc/terms/" xmlns:dcs="http://ontologi.es/doap-changeset#" xmlns:doap="http://usefulinc.com/ns/doap#" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
+<rdf:Description rdf:nodeID="rD08A784CAD9611E0BDACA531A2B30CCDr0">
+	<doap:browse rdf:resource="http://goddamn.co.uk/viewvc/perlmods/RDF-Query-Functions-Buzzword-GB/"/>
+	<rdf:type rdf:resource="http://usefulinc.com/ns/doap#SVNRepository"/>
+</rdf:Description>
+<rdf:Description rdf:about="file:///home/tai/src/perlmods/RDF-Query-Functions-Buzzword-GB/Changes.ttl">
+	<dc:creator rdf:resource="http://tobyinkster.co.uk/#i"/>
+	<dc:description xml:lang="en">Revision history for Perl extension RDF::Query::Functions::Buzzword::GB.</dc:description>
+	<dc:subject rdf:resource="http://purl.org/NET/cpan-uri/dist/RDF-Query-Functions-Buzzword-GB/project"/>
+	<dc:title>Changes</dc:title>
+</rdf:Description>
+<rdf:Description rdf:about="http://purl.org/NET/cpan-uri/dist/RDF-Query-Functions-Buzzword-GB/project">
+	<doap:bug-database rdf:resource="http://rt.cpan.org/Dist/Display.html?Queue=RDF-Query-Functions-Buzzword-GB"/>
+	<doap:created rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2011-07-13</doap:created>
+	<doap:developer rdf:resource="http://tobyinkster.co.uk/#i"/>
+	<doap:documenter rdf:resource="http://tobyinkster.co.uk/#i"/>
+	<doap:download-page rdf:resource="http://search.cpan.org/dist/RDF-Query-Functions-Buzzword-GB/"/>
+	<doap:homepage rdf:resource="http://search.cpan.org/dist/RDF-Query-Functions-Buzzword-GB/"/>
+	<doap:license rdf:resource="http://dev.perl.org/licenses/"/>
+	<doap:maintainer rdf:resource="http://tobyinkster.co.uk/#i"/>
+	<doap:name>RDF-Query-Functions-Buzzword-GB</doap:name>
+	<doap:programming-language>Perl</doap:programming-language>
+	<doap:release rdf:resource="http://purl.org/NET/cpan-uri/dist/RDF-Query-Functions-Buzzword-GB/v_0-001"/>
+	<doap:repository rdf:nodeID="rD08A784CAD9611E0BDACA531A2B30CCDr0"/>
+	<doap:shortdesc xml:lang="en">plugin for buzzword.org.uk UK-specific functions</doap:shortdesc>
+	<doap:tester rdf:resource="http://tobyinkster.co.uk/#i"/>
+	<rdf:type rdf:resource="http://usefulinc.com/ns/doap#Project"/>
+	<rdfs:seeAlso rdf:resource="http://buzzword.org.uk/2011/functions/gb"/>
+</rdf:Description>
+<rdf:Description rdf:about="http://purl.org/NET/cpan-uri/dist/RDF-Query-Functions-Buzzword-GB/v_0-001">
+	<dc:issued rdf:datatype="http://www.w3.org/2001/XMLSchema#date">2011-07-13</dc:issued>
+	<doap:file-release rdf:resource="http://backpan.cpan.org/authors/id/T/TO/TOBYINK/RDF-Query-Functions-Buzzword-GB-0.001.tar.gz"/>
+	<doap:revision rdf:datatype="http://www.w3.org/2001/XMLSchema#string">0.001</doap:revision>
+	<rdf:type rdf:resource="http://usefulinc.com/ns/doap#Version"/>
+	<rdfs:comment xml:lang="en">Original version</rdfs:comment>
+</rdf:Description>
+<rdf:Description xmlns:ns1="http://www.w3.org/2002/07/owl#" rdf:about="http://tobyinkster.co.uk/#i">
+	<rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
+	<ns1:sameAs rdf:resource="http://purl.org/NET/cpan-uri/person/tobyink"/>
+	<foaf:homepage rdf:resource="http://tobyinkster.co.uk/"/>
+	<foaf:mbox rdf:resource="mailto:tobyink@cpan.org"/>
+	<foaf:name>Toby Inkster</foaf:name>
+	<foaf:page rdf:resource="http://search.cpan.org/~tobyink/"/>
+</rdf:Description>
+</rdf:RDF>

File MANIFEST

View file
+TODO
+Changes
+Changes.ttl
+Changes.xml
+Makefile.PL
+MANIFEST
+MANIFEST.SKIP
+README
+META.yml
+SIGNATURE
+
+t/01basic.t
+
+inc/Module/AutoInstall.pm
+inc/Module/Install/AutoInstall.pm
+inc/Module/Install/Base.pm
+inc/Module/Install/Can.pm
+inc/Module/Install/DOAPChangeSets.pm
+inc/Module/Install/Fetch.pm
+inc/Module/Install/Include.pm
+inc/Module/Install/Makefile.pm
+inc/Module/Install/Metadata.pm
+inc/Module/Install.pm
+inc/Module/Install/ReadmeFromPod.pm
+inc/Module/Install/Win32.pm
+inc/Module/Install/WriteAll.pm
+
+lib/RDF/Query/Functions/Buzzword/GB.pm

File MANIFEST.SKIP

View file
+^Makefile$
+^blib/
+^pm_to_blib
+^blibdirs
+\.svn
+^example.*\.pl$
+^[^/]+\.(tar\.gz|tar\.bz2|tgz|tbz2|tbz|zip|tar)$
+^MYMETA.yml

File META.yml

View file
+---
+abstract: ~
+author:
+  - 'Toby Inkster <tobyink@cpan.org>'
+build_requires:
+  ExtUtils::MakeMaker: 6.42
+  Test::More: 0.61
+configure_requires:
+  ExtUtils::MakeMaker: 6.42
+distribution_type: module
+generated_by: 'Module::Install version 1.00'
+keywords:
+  - SPARQL
+  - query
+  - extension
+  - functions
+  - plugin
+  - RDF
+  - Semantic
+  - Web
+  - SemWeb
+  - United
+  - Kingdom
+  - Great
+  - Britain
+  - GB
+  - UK
+  - England
+  - Wales
+  - Scotland
+  - Ireland
+license: perl
+meta-spec:
+  url: http://module-build.sourceforge.net/META-spec-v1.4.html
+  version: 1.4
+name: RDF-Query-Functions-Buzzword-GB
+no_index:
+  directory:
+    - inc
+    - t
+requires:
+  RDF::Query: 2.903
+  Scalar::Util: 0
+  common::sense: 0
+resources:
+  bugtracker: http://rt.cpan.org/Dist/Display.html?Queue=RDF-Query-Functions-Buzzword-GB
+  homepage: http://search.cpan.org/dist/RDF-Query-Functions-Buzzword-GB/
+  license: http://dev.perl.org/licenses/
+  repository: http://goddamn.co.uk/viewvc/perlmods/RDF-Query-Functions-Buzzword-GB/
+version: 0.001

File Makefile.PL

View file
+use strict;
+use warnings;
+
+use inc::Module::Install;
+
+my $dist = 'RDF-Query-Functions-Buzzword-GB';
+my $fn   = "lib/$dist.pm"; $fn =~ s#-#/#g;
+
+name                $dist;
+perl_version_from   $fn;
+version_from        $fn;
+abstract_from       $fn;
+readme_from         $fn;
+author              'Toby Inkster <tobyink@cpan.org>';
+license             'perl';
+
+requires            'common::sense'      => 0;
+requires            'RDF::Query'         => '2.903';
+requires            'Scalar::Util'       => 0;
+test_requires       'Test::More'         => '0.61';
+
+resources(
+	'homepage'   => "http://search.cpan.org/dist/$dist/",
+	'repository' => "http://goddamn.co.uk/viewvc/perlmods/$dist/",
+	'bugtracker' => "http://rt.cpan.org/Dist/Display.html?Queue=$dist",
+	);
+
+keywords(qw[SPARQL query extension functions plugin RDF Semantic Web SemWeb United Kingdom Great Britain GB UK England Wales Scotland Ireland]);
+
+write_doap_changes;
+write_doap_changes_xml;
+
+auto_install;
+WriteAll(
+	'meta' => 1,
+	'sign' => 1,
+	);

File RDF-Query-Functions-Buzzword-GB-0.001.tar.gz

Binary file added.

File README

View file
+NAME
+    RDF::Query::Functions::Buzzword::Util - plugin for buzzword.org.uk
+    utility functions
+
+SYNOPSIS
+      use RDF::TrineShortcuts qw[:all];
+      use Data::Dumper;
+  
+      my $data = rdf_parse(<<'TURTLE', type=>'turtle');
+      @prefix foaf: <http://xmlns.com/foaf/0.1/> .
+      @prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+  
+      <http://tobyinkster.co.uk/#i>
+        foaf:name  "Toby Inkster" ;
+        foaf:phone "0123456789 ext 1234" .
+      TURTLE
+  
+      $r = rdf_query(<<'SPARQL', $data);
+      PREFIX foaf: <http://xmlns.com/foaf/0.1/>
+      PREFIX gb: <http://buzzword.org.uk/2011/functions/gb#>
+      SELECT
+        ?name
+        (gb:telephone_uri(?phone) AS ?phoneuri)
+      WHERE
+      {
+        ?person foaf:name ?name ; foaf:phone ?phone .
+      }
+      SPARQL
+  
+      print Dumper(flatten_iterator($r, literal_as=>'ntriples'));
+
+DESCRIPTION
+    This is a plugin for RDF::Query providing a number of extension
+    functions.
+
+    *   http://buzzword.org.uk/2011/functions/gb#postcode_valid
+
+        Given a literal, returns a boolean indicating whether it seems to be
+        a syntactically valid UK postcode.
+
+    *   http://buzzword.org.uk/2011/functions/gb#postcode_format
+
+        Given a literal, if it seems to be a valid UK postcode,
+        canonicalises the formatting; otherwise returns the literal
+        unscathed.
+
+    *   http://buzzword.org.uk/2011/functions/gb#telephone_valid
+
+        Given a literal, returns a boolean indicating whether it seems to be
+        a number that could be dialed from a UK phone.
+
+        Only the digits '0' to '9', letter 'x' (extension) and '+'
+        (international dialing code) are expected. Other characters are
+        stripped out before any checks.
+
+    *   http://buzzword.org.uk/2011/functions/gb#telephone_format
+
+        Canonicalises the formatting of a phone number that is valid. Should
+        return invalid phone numbers unscathed.
+
+    *   http://buzzword.org.uk/2011/functions/gb#telephone_std
+
+        Returns the STD code for a phone number, if it could be extracted;
+        the empty string otherwise.
+
+    *   http://buzzword.org.uk/2011/functions/gb#telephone_local
+
+        Returns the local part of a phone number, if it could be extracted;
+        the empty string otherwise.
+
+    *   http://buzzword.org.uk/2011/functions/gb#telephone_extension
+
+        Returns the extension from a phone number, if it could be extracted;
+        the empty string otherwise.
+
+    *   http://buzzword.org.uk/2011/functions/gb#telephone_uri
+
+        Returns a phone number as a <tel:> URI.
+
+        An existing <tel:> URI should pass through unscathed.
+
+SEE ALSO
+    RDF::Query.
+
+    <http://perlrdf.org/>.
+
+AUTHOR
+    Toby Inkster <tobyink@cpan.org>.
+
+COPYRIGHT
+    Copyright 2004-2011 Toby Inkster
+
+    This library is free software; you can redistribute it and/or modify it
+    under the same terms as Perl itself.
+

File TODO

View file
+TODO:
+* The phone number stuff could do with some more work.
+* Add postcode-to-geo mapping??
+* What other GB-specific stuff might be useful?

File example1.pl

View file
+use lib "lib";
+use RDF::TrineShortcuts qw[:all];
+use Data::Dumper;
+
+my $data = rdf_parse(<<'TURTLE', type=>'turtle');
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+
+<http://tobyinkster.co.uk/#i>
+	foaf:name "Toby Inkster" ;
+	foaf:phone "01234567890x1234" , "118.247", <tel:123>;
+	foaf:postcode "bn71rs" .
+TURTLE
+
+print Dumper(flatten_iterator(rdf_query(<<'SPARQL', $data), literal_as=>'ntriples'));
+PREFIX foaf: <http://xmlns.com/foaf/0.1/>
+PREFIX sparql: <sparql:>
+PREFIX gb: <http://buzzword.org.uk/2011/functions/gb#>
+PREFIX util: <http://buzzword.org.uk/2011/functions/util#>
+SELECT
+	?name
+	?phone
+	?postcode
+	(gb:postcode_format(?postcode) AS ?pcfmt)
+#	(gb:telephone_std(?phone) AS ?phonestd)
+#	(gb:telephone_local(?phone) AS ?phonelocal)
+#	(gb:telephone_extension(?phone) AS ?phoneext)
+	(gb:telephone_uri(?phone) AS ?phoneuri)
+WHERE
+{
+	?person foaf:name ?name ; foaf:phone ?phone ; foaf:postcode ?postcode .
+}
+SPARQL
+
+

File lib/RDF/Query/Functions/Buzzword/GB.pm

View file
+package RDF::Query::Functions::Buzzword::GB;
+
+our $VERSION = '0.001';
+
+use common::sense;
+use RDF::Query::Error qw(:try);
+use RDF::Trine::Namespace qw[XSD];
+use Scalar::Util qw[blessed];
+
+sub install
+{
+	# Various types of UK postcodes, for later use.
+	my %PostcodeRE;
+	$PostcodeRE{'area'}     = '([A-PR-UWYZ][A-HK-Y]?)';
+	$PostcodeRE{'district'} = '([0-9]{1,2}|[1-9][A-HJKMNPR-Y])';
+	$PostcodeRE{'space'}    = '\s+';
+	$PostcodeRE{'space?'}   = '\s*';
+	$PostcodeRE{'sector'}   = '([0-9])';
+	$PostcodeRE{'unit'}     = '([ABDEFGHJLNP-UW-Z]{2})';
+	$PostcodeRE{'std'}      = join '', @PostcodeRE{qw'area district space? sector unit'}; 
+	$PostcodeRE{'bfpo'}     = '(BFPO)\s*([0-9]{1,4})';
+	$PostcodeRE{'giro'}     = '(GIR)\s*(0AA)';
+	$PostcodeRE{'santa'}    = '(SAN)\s*(TA1)';
+	$PostcodeRE{'os_area'}  = '(ASCN|BBND|BIQQ|FIQQ|PCRN|SIQQ|STHL|TDCU|TKCA)';
+	$PostcodeRE{'os_pc'}    = $PostcodeRE{'os_area'} .'\s*(1ZZ)';
+	$PostcodeRE{'*'}        = join '|', map {"(?:$_)"} @PostcodeRE{qw'std bfpo giro santa os_pc'};
+	
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/gb#postcode_valid"} ||= sub {
+		my ($query, $node) = @_;
+		
+		unless (blessed($node) and $node->isa('RDF::Trine::Node::Literal'))
+		{
+			throw RDF::Query::Error::TypeError(-text => "postcode_valid function requires literal argument");
+		}
+
+		if ($node->literal_value =~ /$PostcodeRE{'*'}/i)
+		{
+			return RDF::Query::Node::Literal->new('true', undef, $XSD->boolean->uri);
+		}
+		
+		return RDF::Query::Node::Literal->new('false', undef, $XSD->boolean->uri);
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/gb#postcode_format"} ||= sub {
+		my ($query, $node) = @_;
+		
+		unless (blessed($node) and $node->isa('RDF::Trine::Node::Literal'))
+		{
+			throw RDF::Query::Error::TypeError(-text => "postcode_format function requires literal argument");
+		}
+
+		if ($node->literal_value =~ /$PostcodeRE{'std'}/i)
+		{
+			return RDF::Query::Node::Literal->new(
+				uc sprintf('%s%s %s%s', $1, $2, $3, $4),
+				$node->literal_value_language,
+				$node->literal_datatype,
+				);
+		}
+		
+		if (my @parts = ($node->literal_value =~ /$PostcodeRE{'*'}/i))
+		{
+			return RDF::Query::Node::Literal->new(
+				(uc join ' ', grep { defined $_ } @parts[1 .. $#parts]),
+				$node->literal_value_language,
+				$node->literal_datatype,
+				);
+		}
+		
+		return $node;
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/gb#telephone_valid"} ||= sub {
+		my ($query, $node) = @_;
+		
+		unless (blessed($node) and $node->isa('RDF::Trine::Node::Literal'))
+		{
+			throw RDF::Query::Error::TypeError(-text => "telephone_valid function requires literal argument");
+		}
+
+		my ($intl, $std, $num, $ext) = _parse_telephone_number($node->literal_value);
+		
+		if (ref $intl)
+		{
+			return RDF::Query::Node::Literal->new('false', undef, $XSD->boolean->uri);
+		}
+		
+		return RDF::Query::Node::Literal->new('true', undef, $XSD->boolean->uri);
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/gb#telephone_std"} ||= sub {
+		my ($query, $node) = @_;
+		
+		unless (blessed($node) and $node->isa('RDF::Trine::Node::Literal'))
+		{
+			throw RDF::Query::Error::TypeError(-text => "telephone_std function requires literal argument");
+		}
+
+		my ($intl, $std, $num, $ext) = _parse_telephone_number($node->literal_value);
+		
+		if (defined $std && !ref $std)
+		{
+			return RDF::Query::Node::Literal->new($std);
+		}
+		
+		return RDF::Query::Node::Literal->new('');
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/gb#telephone_local"} ||= sub {
+		my ($query, $node) = @_;
+		
+		unless (blessed($node) and $node->isa('RDF::Trine::Node::Literal'))
+		{
+			throw RDF::Query::Error::TypeError(-text => "telephone_number function requires literal argument");
+		}
+
+		my ($intl, $std, $num, $ext) = _parse_telephone_number($node->literal_value);
+		
+		if (defined $num && !ref $num)
+		{
+			return RDF::Query::Node::Literal->new($num);
+		}
+		
+		return RDF::Query::Node::Literal->new('');
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/gb#telephone_extension"} ||= sub {
+		my ($query, $node) = @_;
+		
+		unless (blessed($node) and $node->isa('RDF::Trine::Node::Literal'))
+		{
+			throw RDF::Query::Error::TypeError(-text => "telephone_extension function requires literal argument");
+		}
+
+		my ($intl, $std, $num, $ext) = _parse_telephone_number($node->literal_value);
+		
+		if (defined $ext && !ref $ext)
+		{
+			return RDF::Query::Node::Literal->new($ext);
+		}
+		
+		return RDF::Query::Node::Literal->new('');
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/gb#telephone_uri"} ||= sub {
+		my ($query, $node) = @_;
+		
+		if (blessed($node) and $node->isa('RDF::Trine::Node::Resource'))
+		{
+			return $node if $node->uri =~ /^tel:/i;
+		}
+		
+		unless (blessed($node) and $node->isa('RDF::Trine::Node::Literal'))
+		{
+			throw RDF::Query::Error::TypeError(-text => "telephone_uri function requires literal argument, or <tel:> URI.");
+		}
+
+		my ($intl, $std, $num, $ext) = _parse_telephone_number($node->literal_value);
+		
+		my $uri;
+		if (defined $intl && !ref $intl)
+		{
+			$uri = sprintf('tel:%s', $intl);
+		}
+		elsif (defined $std)
+		{
+			my $stdx = $std;
+			$stdx =~ s/^0//;
+			$uri = sprintf('tel:+44-%s-%s', $stdx, $num);
+		}
+		elsif (defined $num)
+		{
+			$uri = sprintf('tel:%s;phone-context=+44', $num);
+		}
+		
+		if (defined $uri && defined $ext)
+		{
+			$uri .= sprintf(';extension=%s', $ext);
+		}
+		
+		if (defined $uri)
+		{
+			return RDF::Query::Node::Resource->new($uri);
+		}
+		
+		return $node;
+	};
+
+	$RDF::Query::functions{"http://buzzword.org.uk/2011/functions/gb#telephone_format"} ||= sub {
+		my ($query, $node) = @_;
+		
+		unless (blessed($node) and $node->isa('RDF::Trine::Node::Literal'))
+		{
+			throw RDF::Query::Error::TypeError(-text => "telephone_format function requires literal argument.");
+		}
+
+		my ($intl, $std, $num, $ext) = _parse_telephone_number($node->literal_value);
+		
+		my $uri;
+		if (defined $intl && !ref $intl)
+		{
+			$uri = sprintf('%s', $intl);
+		}
+		elsif (defined $std)
+		{
+			$uri = sprintf('%s %s', $std, $num);
+		}
+		elsif (defined $num)
+		{
+			$uri = sprintf('%s', $num);
+		}
+		
+		if (defined $uri && defined $ext)
+		{
+			$uri .= sprintf(' x%s', $ext);
+		}
+		
+		if (defined $uri)
+		{
+			return RDF::Query::Node::Literal->new($uri);
+		}
+		
+		return $node;
+	};
+
+} #/sub install
+
+sub _parse_telephone_number
+{
+	no strict;
+	
+	my $std;
+	my $n;
+	my $x;
+	
+	local $_ = shift;
+	s/[^0-9x\+]//ig;
+	($_, $x) = split /x/i;
+	
+	s/^00/+/;
+	
+	if (/^\+/)
+	{
+		return ($_, undef, undef, $x);
+	}
+	
+	if (/^118\d{3}$/ || /^1\d{2}$/ || /^999$/)
+	{
+		return (undef, undef, $_, undef);
+	}
+
+	$_ = "0$_" unless /^0/;
+	return { error => "Phone number $_ seems to be wrong length." }
+		unless (length($_) == 11 || length($_) == 10);
+	
+	if (/^02/)
+	{
+		$std = substr $_, 0, 3;
+		$n   = substr $_, 3;
+	}	
+	elsif (/^011/ || /^0[358]/)
+	{
+		$std = substr $_, 0, 4;
+		$n   = substr $_, 4;
+	}
+	else
+	{
+		$std = substr $_, 0, 5;
+		$n   = substr $_, 5;
+	}
+	
+	return (undef, $std, $n, $x);
+}
+
+
+1;
+
+__END__
+
+=head1 NAME
+
+RDF::Query::Functions::Buzzword::Util - plugin for buzzword.org.uk utility functions
+
+=head1 SYNOPSIS
+
+  use RDF::TrineShortcuts qw[:all];
+  use Data::Dumper;
+  
+  my $data = rdf_parse(<<'TURTLE', type=>'turtle');
+  @prefix foaf: <http://xmlns.com/foaf/0.1/> .
+  @prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+  
+  <http://tobyinkster.co.uk/#i>
+    foaf:name  "Toby Inkster" ;
+    foaf:phone "0123456789 ext 1234" .
+  TURTLE
+  
+  $r = rdf_query(<<'SPARQL', $data);
+  PREFIX foaf: <http://xmlns.com/foaf/0.1/>
+  PREFIX gb: <http://buzzword.org.uk/2011/functions/gb#>
+  SELECT
+    ?name
+    (gb:telephone_uri(?phone) AS ?phoneuri)
+  WHERE
+  {
+    ?person foaf:name ?name ; foaf:phone ?phone .
+  }
+  SPARQL
+  
+  print Dumper(flatten_iterator($r, literal_as=>'ntriples'));
+
+=head1 DESCRIPTION
+
+This is a plugin for RDF::Query providing a number of extension functions.
+
+=over
+
+=item * http://buzzword.org.uk/2011/functions/gb#postcode_valid
+
+Given a literal, returns a boolean indicating whether it seems to be
+a syntactically valid UK postcode.
+
+=item * http://buzzword.org.uk/2011/functions/gb#postcode_format
+
+Given a literal, if it seems to be a valid UK postcode, canonicalises
+the formatting; otherwise returns the literal unscathed.
+
+=item * http://buzzword.org.uk/2011/functions/gb#telephone_valid
+
+Given a literal, returns a boolean indicating whether it seems to be
+a number that could be dialed from a UK phone.
+
+Only the digits '0' to '9', letter 'x' (extension) and '+' (international
+dialing code) are expected. Other characters are stripped out before any
+checks.
+
+=item * http://buzzword.org.uk/2011/functions/gb#telephone_format
+
+Canonicalises the formatting of a phone number that is valid. Should return
+invalid phone numbers unscathed.
+
+=item * http://buzzword.org.uk/2011/functions/gb#telephone_std
+
+Returns the STD code for a phone number, if it could be extracted; the
+empty string otherwise.
+
+=item * http://buzzword.org.uk/2011/functions/gb#telephone_local
+
+Returns the local part of a phone number, if it could be extracted; the
+empty string otherwise.
+
+=item * http://buzzword.org.uk/2011/functions/gb#telephone_extension
+
+Returns the extension from a phone number, if it could be extracted; the
+empty string otherwise.
+
+=item * http://buzzword.org.uk/2011/functions/gb#telephone_uri
+
+Returns a phone number as a E<lt>tel:E<gt> URI.
+
+An existing E<lt>tel:E<gt> URI should pass through unscathed.
+
+=back
+
+=head1 SEE ALSO
+
+L<RDF::Query>.
+
+L<http://perlrdf.org/>.
+
+=head1 AUTHOR
+
+Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
+
+=head1 COPYRIGHT
+
+Copyright 2004-2011 Toby Inkster
+
+This library is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.

File t/01basic.t

View file
+use Test::More tests => 1;
+use RDF::Query;
+
+ok(
+	defined $RDF::Query::functions{'http://buzzword.org.uk/2011/functions/gb#telephone_valid'},
+	"Module::Pluggable found me!",
+	);