Permalink
Browse files

also detect format via extension

  • Loading branch information...
1 parent acd2eb3 commit 876148a219d6044ae8039ae4145bebc711b34951 @nichtich committed Jun 14, 2011
Showing with 291 additions and 122 deletions.
  1. +3 −0 .gitignore
  2. +1 −0 dist.ini
  3. +4 −4 example/app.psgi
  4. +109 −73 lib/RDF/Light.pm
  5. +5 −6 lib/RDF/Light/Graph.pm
  6. +48 −34 lib/RDF/Light/Source.pm
  7. +20 −0 t/30_sources.t
  8. +82 −0 t/40_formats.t
  9. 0 t/{40_graph.t → 50_graph.t}
  10. +16 −3 t/TestPlackApp.pm
  11. +3 −2 t/template.t
View
@@ -1 +1,4 @@
*~
+.build
+*.tar.gz
+RDF-Light-*
View
@@ -27,3 +27,4 @@ bugtracker.web = https://github.com/nichtich/RDF-Light/issues
[PodSyntaxTests]
[AutoPrereqs]
+skip = ^TestPlackApp|RDF::Light::Node$
View
@@ -97,7 +97,7 @@ my $index_app = sub {
$tt->process("index.html", $vars, \$content);
utf8::downgrade($content);
- $env->{'psgix.logger'}->({ level => "info", message => "Index done:$content" });
+ #$env->{'psgix.logger'}->({ level => "info", message => "Index done:$content" });
return [ 200, ['Content-Type' => 'text/html'], [$content]];
};
@@ -106,9 +106,9 @@ my $index_app = sub {
builder {
enable 'SimpleLogger';
enable 'Debug';
- enable 'Plack::Middleware::Static', root => $dir, path => qr/\.css$/;
- enable 'JSONP'; # for RDF/JSON in AJAX
- enable "+RDF::Light", source => $model, base => $base;
+ enable 'JSONP'; # to support RDF/JSON in AJAX
+ enable 'Static', root => $dir, path => qr/\.css$/;
+ enable '+RDF::Light', source => $model, base => $base;
enable $index_app;
$app;
};
View
@@ -1,7 +1,6 @@
-package RDF::Light;
-
use strict;
use warnings;
+package RDF::Light;
=head1 NAME
@@ -28,38 +27,6 @@ RDF::Light - Simplified Linked Data handling
$app;
}
-=head1 INTRODUCTION
-
-This package provides a PSGI application to serve RDF as Linked Data. In
-contrast to other Linked Data applications, URIs must not have query parts and
-the distinction between information-resources and non-information resources is
-disregarded (some Semantic Web evangelists may be angry about this). By now
-this package is experimental. For a more complete package see
-L<RDF::LinkedData>.
-
-The package implements a PSGI application that can be used as
-L<Plack::Middleware> to provide RDF data. The implementation is based on
-L<RDF::Trine> which is a full implementation of RDF standards in Perl.
-
-=head1 OVERVIEW
-
-An RDF::Light application processes PSGI/HTTP requests in three steps:
-
-=over 4
-
-=item 1
-
-Determine query URI and serialization format (mime type) and set the request
-variables C<rdflight.uri>, C<rdflight.type>, and C<rdflight.serializer>.
-
-=item 2
-
-Retrieve data about the resource which is identified by the request URI.
-
-=item 3
-
-Create a serialization.
-
=cut
use Try::Tiny;
@@ -71,7 +38,7 @@ use Carp;
use RDF::Light::Source;
use parent 'Plack::Middleware';
-use Plack::Util::Accessor qw(source formats base);
+use Plack::Util::Accessor qw(source base formats via_param via_extension);
use parent 'Exporter';
our @EXPORT_OK = qw(guess_serialization);
@@ -91,14 +58,13 @@ our %rdf_formats = (
sub prepare_app {
my $self = shift;
+ $self->formats( \%rdf_formats ) unless $self->formats;
+
# TODO: support array ref and custom serialization formats
- if ( $self->formats ) {
- ref $self->formats eq 'HASH' or carp 'formats must be a hash reference';
- } else {
- $self->formats( \%rdf_formats );
- }
+ ref $self->formats eq 'HASH'
+ or carp 'formats must be a hash reference';
- # TODO: support file extensions and disabling formats
+ $self->via_param(1) unless defined $self->via_param;
}
sub call {
@@ -114,7 +80,6 @@ sub call {
unless defined $env->{'rdflight.uri'};
if ( $type ) {
- # TODO: document this variables
$env->{'rdflight.type'} = $type;
$env->{'rdflight.serializer'} = $serializer;
@@ -124,9 +89,13 @@ sub call {
return [ 200, [ 'Content-Type' => $type ], [ $rdf_data ] ];
}
}
-
- # pass through if no/unknown serializer or empty source (URI not found) or error
- return $app->( $env );
+
+ # pass through if no/unknown serializer or empty source (URI not found) or error
+ if ( $app ) {
+ return $app->( $env );
+ } else {
+ return [ 404, [ 'Content-Type' => 'text/plain' ], [ 'Not found' ] ];
+ }
}
sub retrieve_and_serialize {
@@ -146,20 +115,22 @@ sub retrieve_and_serialize {
if ( UNIVERSAL::isa( $src, 'CODE' ) ) {
$rdf = $src->($env);
} elsif ( UNIVERSAL::isa( $src, 'RDF::Trine::Model' ) ) {
- $rdf = $src->bounded_description( iri($env->{'rdflight.uri'}) );
+ $rdf = $src->bounded_description( iri($env->{'rdflight.uri'}) );
+ } elsif ( UNIVERSAL::can( $src, 'retrieve' ) ) {
+ $rdf = $src->retrieve( $env );
}
if ( UNIVERSAL::isa( $rdf, 'RDF::Trine::Model' ) ) {
- if ( $rdf->size > 0 ) {
- return $serializer->serialize_model_to_string( $rdf );
- }
+ if ( $rdf->size > 0 ) {
+ return $serializer->serialize_model_to_string( $rdf );
+ }
} elsif ( UNIVERSAL::isa( $rdf, 'RDF::Trine::Iterator' ) ) {
- if ( $rdf->peek ) {
- return $serializer->serialize_iterator_to_string( $rdf );
- }
+ if ( $rdf->peek ) {
+ return $serializer->serialize_iterator_to_string( $rdf );
+ }
} else {
- # TODO: how to indicate an error? (500?)
- # $env->{'rdflight.error'} = ... ?
+ # TODO: how to indicate an error? (500?)
+ # $env->{'rdflight.error'} = ... ?
}
}
@@ -181,15 +152,26 @@ sub guess_serialization {
my $accept = $env->{HTTP_ACCEPT} || '';
my $req = Plack::Request->new( $env );
- my $format = $req->param('format') || ''; # TODO: also support extensions
+ my $format;
+
+ if ($self->via_param and $req->param('format')) {
+ $format = $req->param('format');
+ } elsif ($self->via_extension) {
+ my $path = $env->{PATH_INFO} || '';
+ if ( $path =~ /^(.*)\.([^.]+)$/ and $possible_formats->{$2} ) {
+ $env->{PATH_INFO} = $1;
+ $format = $2;
+ }
+ }
my ($type, $serializer);
- if ($format ne '') {
- try {
- $serializer = RDF::Trine::Serializer->new( $possible_formats->{$format} );
+ if ($format) {
+ my $name = $possible_formats->{$format};
+ if ($name) { try {
+ $serializer = RDF::Trine::Serializer->new( $name );
($type) = $serializer->media_types;
- } # TODO: catch if unknown format or format not available
+ } } # TODO: catch if unknown format or format not available
} else {
($type, $serializer) = try {
RDF::Trine::Serializer->negotiate( request_headers => $req->headers );
@@ -209,7 +191,7 @@ sub uri {
return $env->{'rdflight.uri'} if defined $env->{'rdflight.uri'};
- my ($base,$self); # TODO: support as second argument
+ my ($base, $self); # TODO: support as second argument
if (UNIVERSAL::isa($env,'RDF::Light')) {
($self, $env) = ($env, shift);
@@ -227,6 +209,40 @@ sub uri {
return $base.$path;
}
+=head1 INTRODUCTION
+
+This package provides a PSGI application to serve RDF as Linked Data. In
+contrast to other Linked Data applications, URIs must not have query parts and
+the distinction between information-resources and non-information resources is
+disregarded (some Semantic Web evangelists may be angry about this). By now
+this package is experimental. For a more complete package see
+L<RDF::LinkedData>.
+
+The package implements a PSGI application that can be used as
+L<Plack::Middleware> to provide RDF data. The implementation is based on
+L<RDF::Trine> which is a full implementation of RDF standards in Perl.
+
+=head1 OVERVIEW
+
+An RDF::Light application processes PSGI/HTTP requests in three steps:
+
+=over 4
+
+=item 1
+
+Determine query URI and serialization format (mime type) and set the request
+variables C<rdflight.uri>, C<rdflight.type>, and C<rdflight.serializer>.
+
+=item 2
+
+Retrieve data about the resource which is identified by the request URI.
+
+=item 3
+
+Create a serialization.
+
+=back
+
=head1 METHODS
=head2 new ( [ %configuration ] )
@@ -239,15 +255,27 @@ Creates a new object.
=item source
-Sets a code reference as RDF source (see L<RDF::Light::Source>) or a
-L<RDF::Trine::Model> to query from. You can also set an array reference
-with a list of multiple sources, which are cascaded.
+Sets a L<RDF::Trine::Model> or a code reference as RDF source that returns a
+Model or Iterator (see L<RDF::Light::Source>) to query from. You can also set
+an array reference with a list of multiple sources, which are cascaded.
+
+For testing you can use the function dummy_source that always returns a single
+triple and is exported by RDF::Light::Source.
+
+=item base
+
+Maps request URIs to a given URI prefix, similar to L<Plack::App::URLMap>.
+
+For instance if you deploy you application at C<http://your.domain/> and set
+base to C<http://other.domain/> then a request for C<http://your.domain/foo>
+is be mapped to the URI C<http://other.domain/foo>.
=item formats
-Defines supported serialization formats. You can either specify an array reference
-with serializer names or a hash reference with mappings of format names to serializer
-names. Serializer names must exist in L<RDF::Trine::Serializer>::serializer_names.
+Defines supported serialization formats. You can either specify an array
+reference with serializer names or a hash reference with mappings of format
+names to serializer names. Serializer names must exist in
+RDF::Trine's L<RDF::Trine::Serializer>::serializer_names.
RDF::Light->new ( formats => [qw(ntriples rdfxml turtle)] )
@@ -258,13 +286,22 @@ names. Serializer names must exist in L<RDF::Trine::Serializer>::serializer_name
ttl => 'turtle'
} );
-=item base
+By default the formats rdf, xml, and rdfxml (for L<RDF::Trine::Serializer>),
+ttl (for L<RDF::Trine::Serializer::Turtle>), json
+(for L<RDF::Trine::Serializer::RDFJSON>), and nt
+(for L<RDF::Trine::Serializer::NTriples>) are used.
-Maps request URIs to a given URI prefix, similar to L<Plack::App::URLMap>.
+=item via_param
-For instance if you deploy you application at C<http://your.domain/> and set
-base to C<http://other.domain/> then a request for C<http://your.domain/foo>
-is be mapped to the URI C<http://other.domain/foo>.
+Detect serialization format via 'format' parameter. For instance
+C<foobar?format=ttl> will serialize URI foobar in RDF/Turtle.
+This is enabled by default.
+
+=item via_extension
+
+Detect serialization format via "file extension". For instance
+C<foobar.rdf> will serialize URI foobar in RDF/XML.
+This is disabled by default.
=item extensions
@@ -296,8 +333,7 @@ and path. Query parameters are ignored.
=head2 SEE ALSO
-See also L<RDF::Light::Graph>.
-RDF::Light may be renamed to RDF::Light::LOD for "Linked Open Data".
+See also L<RDF::Light::Graph>, which is bundled with this module.
=head2 ACKNOWLEDGEMENTS
View
@@ -1,7 +1,6 @@
-package RDF::Light::Graph;
-
use strict;
use warnings;
+package RDF::Light::Graph;
=head1 NAME
@@ -35,7 +34,7 @@ sub new {
}, $class;
}
-sub model { shift->{model} }
+sub model { $_[0]->{model} }
sub objects {
my $self = shift;
@@ -46,7 +45,7 @@ sub objects {
$subject = $self->node($subject)
unless UNIVERSAL::isa( $subject, 'RDF::Light::Node' );
- my $all = 1 if ($property =~ s/^(.+[^_])_$/$1/);
+ my $all = ($property =~ s/^(.+[^_])_$/$1/) ? 1 : 0;
my $predicate = $self->node($property);
if (defined $predicate) {
@@ -287,11 +286,11 @@ sub new {
}
sub uri {
- shift->trine->value
+ shift->trine->uri_value
}
sub href { # TODO: check whether non-XML characters are possible
- escapeHTML(shift->trine->value);
+ escapeHTML(shift->trine->uri_value);
}
sub objects { # TODO: rename to 'attr' or 'prop' ?
Oops, something went wrong.

0 comments on commit 876148a

Please sign in to comment.