Commits

Toby Inkster committed 7257356

initial commit

  • Participants

Comments (0)

Files changed (10)

+use inc::Module::Package 'RDF 0.008';
+

lib/App/perlrdf/Command/Query.pm

+package App::perlrdf::Command::Query;
+
+use 5.010;
+use strict;
+use warnings;
+use utf8;
+
+BEGIN {
+	$App::perlrdf::Command::Query::AUTHORITY = 'cpan:TOBYINK';
+	$App::perlrdf::Command::Query::VERSION   = '0.001';
+}
+
+use App::perlrdf -command;
+use namespace::clean;
+
+use constant abstract      => q (query stores, files or remote endpoints with SPARQL);
+use constant command_names => qw( query sparql q );
+use constant description   => <<'DESCRIPTION';
+Use SPARQL to query:
+
+	* an RDF::Trine::Store;
+	* a remote SPARQL Protocol (1.0/1.1) endpoint; or
+	* one or more input files;
+
+But not a combination of the above.
+DESCRIPTION
+
+use constant opt_spec => (
+	__PACKAGE__->store_opt_spec,
+	[]=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>,
+	[ 'input|i=s@',        'Input filename or URL' ],
+	[ 'input-spec|I=s@',   'Input file specification' ],
+	[ 'input-format|p=s',  'Input format (mnemonic: parse)' ], 
+	[ 'input-base|b=s',    'Input base URI' ],
+	[ 'graph|g=s',         'Graph URI for input' ],
+	[ 'autograph|G',       'Generate graph URI based on input URI' ],
+	[]=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>,
+	[ 'endpoint=s',        'Remote SPARQL Protocol endpoint' ],
+	[]=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>,
+	[ 'execute|e=s',       'Query to execute' ],
+	[ 'sparql-file|f=s',   'File containing query to execute' ],
+	[]=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>
+	[ 'output|o=s@',       'Output filename or URL' ],
+	[ 'output-spec|O=s@',  'Output file specification' ],
+	[ 'output-format|s=s', 'Output format (mnemonic: serialise)' ],
+);
+
+sub validate_args
+{
+	my ($self, $opt, $arg) = @_;
+	
+	my %exclusions = (
+		execute  => ['sparql_file'],
+		endpoint => [
+			qw[ store dbi sqlite username password host port dbname database ],
+			qw[ input input_spec input_format input_base ],
+		],
+		map { ; $_ => [
+			qw[ store dbi sqlite username password host port dbname database ],
+			qw[ endpoint ],
+		] } qw( input input_spec input_format input_base )
+	);
+	
+	foreach my $k (keys %exclusions)
+	{
+		next unless exists $opt->{$k};
+		foreach my $e (@{ $exclusions{$k} })
+		{
+			next unless exists $opt->{$e};
+			$self->usage_error("Must not provide both '$k' and '$e' options.");
+		}
+	}
+}
+
+sub _sparql
+{
+	require App::perlrdf::FileSpec::InputFile;
+	my ($self, $opt, $arg) = @_;
+	
+	# SPARQL provided on command line
+	#
+	return $opt->{execute}
+		if $opt->{execute};
+	
+	# SPARQL from input file
+	#
+	App::perlrdf::FileSpec::InputFile::
+		-> new_from_filespec(
+			($opt->{sparql_file} // shift @$arg),
+			'SPARQL',
+		)
+		-> content;
+}
+
+sub _model
+{
+	require App::perlrdf::FileSpec::InputRDF;
+	my ($self, $opt, $arg) = @_;
+	
+	if (grep { exists $opt->{$_} } qw[ store dbi sqlite dbname database ])
+	{
+		return RDF::Trine::Model->new( $self->get_store($opt) );
+	}
+
+	my $model = RDF::Trine::Model->new;
+		
+	my @inputs = $self->get_filespecs(
+		'App::perlrdf::FileSpec::InputRDF',
+		input => $opt,
+	);
+	
+	push @inputs, map {
+		App::perlrdf::FileSpec::InputRDF::->new_from_filespec(
+			$_,
+			$opt->{input_format},
+			$opt->{input_base},
+		)
+	} @$arg;
+
+	push @inputs,
+		App::perlrdf::FileSpec::InputRDF::->new_from_filespec(
+			'-',
+			$opt->{input_format},
+			$opt->{input_base},
+		)
+		unless @inputs;
+
+	for (@inputs)
+	{
+		printf STDERR "Loading %s\n", $_->uri;
+		
+		my %params = ();
+		if ($opt->{autograph})
+			{ $params{graph} = $_->uri }
+		elsif ($opt->{graph})
+			{ $params{graph} = $opt->{graph} }
+			
+		eval {
+			local $@ = undef;
+			$_->parse_into_model($model, %params);
+			1;
+		} or warn "$@\n";
+	}
+
+	return $model;
+}
+
+sub _outputs
+{
+	require App::perlrdf::FileSpec::OutputRDF;
+	require App::perlrdf::FileSpec::OutputFile;
+	
+	my ($self, $opt, $arg, $class) = @_;
+	
+	my @outputs = $self->get_filespecs(
+		$class,
+		output => $opt,
+	);
+	push @outputs,
+		$class->new_from_filespec(
+			'-',
+			$opt->{output_format}||'text',
+			undef,
+		)
+		unless @outputs;
+	
+	return @outputs;
+}
+
+sub _process_sparql
+{
+	require RDF::Query;
+	require RDF::Query::Client;	
+	my ($self, $opt, $arg, $sparql, $model) = @_;
+	
+	my $qclass = ref $model ? 'RDF::Query' : 'RDF::Query::Client';
+	my $query  = $qclass->new($sparql);
+	my $result = $query->execute($model);
+	
+	if ($result->is_graph)
+	{
+		my $m = RDF::Trine::Model->new;
+		$m->add_iterator($m);
+		
+		my (@outputs) = $self->_outputs(
+			$opt,
+			$arg,
+			'App::perlrdf::FileSpec::OutputRDF',
+		);
+		
+		foreach my $out (@outputs)
+		{
+			$out->serialize_model($m);
+			$out->handle->close;
+		}
+	}
+	
+	if ($result->is_bindings)
+	{
+		my $mat = $result->materialize;
+		
+		my (@outputs) = $self->_outputs(
+			$opt,
+			$arg,
+			'App::perlrdf::FileSpec::OutputRDF',
+		);
+		
+		foreach my $out (@outputs)
+		{
+			if ($out->format =~ /json/i)
+			{
+				$out->handle->print($mat->as_json);
+			}
+			elsif ($out->format =~ /xml/i)
+			{
+				$mat->print_xml($out->handle);
+			}
+			else
+			{
+				$out->handle->print($mat->as_string);
+			}
+			
+			$mat->reset;
+			$out->handle->close;
+		}
+	}
+}
+
+sub execute
+{
+	require RDF::Trine;
+	my ($self, $opt, $arg) = @_;
+	
+	my $sparql = $self->_sparql($opt, $arg);
+
+	if (exists $opt->{endpoint})
+	{
+		return $self->_process_sparql($opt, $arg, $sparql, $opt->{endpoint});
+	}
+
+	my $model = $self->_model($opt, $arg);
+	$self->_process_sparql($opt, $arg, $sparql, $model);
+}
+
+1;
+
+
+__END__
+
+=head1 NAME
+
+App::perlrdf::Command::Query - SPARQL extension for App-perlrdf
+
+=head1 SYNOPSIS
+
+ $ perlrdf query -e 'SELECT * WHERE { ?s ?p ?o }' -i data.rdf
+
+ $ perlrdf query -e 'SELECT * WHERE { ?s ?p ?o }' -Q store.sqlite
+
+=head1 DESCRIPTION
+
+This module adds query abilities to the C<perlrdf> command-line client.
+
+=head1 BUGS
+
+Please report any bugs to
+L<http://rt.cpan.org/Dist/Display.html?Queue=App-perlrdf-Command-Query>.
+
+=head1 SEE ALSO
+
+L<App::perlrdf>.
+
+=head1 AUTHOR
+
+Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
+
+=head1 COPYRIGHT AND LICENCE
+
+This software is copyright (c) 2012 by Toby Inkster.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=head1 DISCLAIMER OF WARRANTIES
+
+THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+

meta/changes.pret

+# This file acts as the project's changelog.
+
+`App-perlrdf-Command-Query 0.001 cpan:TOBYINK`
+	issued  2012-07-19;
+	label   "Initial release".
+
+# This file contains general metadata about the project.
+
+@prefix : <http://usefulinc.com/ns/doap#>.
+
+`App-perlrdf-Command-Query`
+	:programming-language "Perl" ;
+	:shortdesc            "SPARQL extension for App-perlrdf";
+	:homepage             <https://metacpan.org/release/App-perlrdf-Command-Query>;
+	:download-page        <https://metacpan.org/release/App-perlrdf-Command-Query>;
+	:bug-database         <http://rt.cpan.org/Dist/Display.html?Queue=App-perlrdf-Command-Query>;
+	:created              2012-07-19;
+	:license              <http://dev.perl.org/licenses/>;
+	:maintainer           cpan:TOBYINK;
+	:developer            cpan:TOBYINK.
+
+<http://dev.perl.org/licenses/>
+	dc:title  "the same terms as the perl 5 programming language system itself".
+
+cpan:TOBYINK
+	foaf:name  "Toby Inkster";
+	foaf:mbox  <mailto:tobyink@cpan.org>.
+

meta/makefile.pret

+# This file provides instructions for packaging.
+
+`App-perlrdf-Command-Query`
+	perl_version_from m`App::perlrdf::Command::Query`;
+	version_from      m`App::perlrdf::Command::Query`;
+	readme_from       m`App::perlrdf::Command::Query`;
+	test_requires     p`Test::More 0.61`  .
+
+use Test::More tests => 1;
+BEGIN { use_ok('App::perlrdf::Command::Query') };
+
+use Test::More;
+eval "use Test::Pod 1.00";
+plan skip_all => "Test::Pod 1.00 required for testing POD" if $@;
+all_pod_files_ok();
+

xt/02pod_coverage.t

+use Test::More;
+use Test::Pod::Coverage;
+
+my @modules = qw(App::perlrdf::Command::Query);
+pod_coverage_ok($_, "$_ is covered") for @modules;
+done_testing(scalar @modules);
+

xt/03meta_uptodate.t

+use Test::More tests => 1;
+use Test::RDF::DOAP::Version;
+doap_version_ok('App-perlrdf-Command-Query', 'App::perlrdf::Command::Query');
+
+use Test::EOL;
+all_perl_files_ok();