File Coverage

blib/lib/RDF/TrineX/Functions.pm
Criterion Covered Total %
statement 24 26 92.3
branch n/a
condition n/a
subroutine 9 9 100.0
pod n/a
total 33 35 94.2


line stmt bran cond sub pod time code
1             package RDF::TrineX::Functions;
2              
3 2     2   84751 use 5.010;
  2         7  
  2         111  
4 2     2   12 use strict qw< vars subs >;
  2         6  
  2         76  
5 2     2   11 no warnings;
  2         9  
  2         91  
6 2     2   4878 use utf8;
  2         7  
  2         13  
7              
8             BEGIN {
9 2     2   5 $RDF::TrineX::Functions::AUTHORITY = 'cpan:TOBYINK';
10 2         43 $RDF::TrineX::Functions::VERSION = '0.004';
11             }
12              
13 2     2   13 use Carp qw< croak >;
  2         4  
  2         543  
14 2     2   16670 use IO::Detect 0.003 qw< is_filehandle is_filename is_fileuri >;
  2         328758  
  2         21  
15 2     2   26390 use PerlX::Maybe qw< maybe >;
  2         14141  
  2         156  
16 2     2   2912 use RDF::NS::Trine;
  0            
  0            
17             use RDF::Trine qw< store >;
18             use RDF::Trine::Namespace qw< rdf rdfs owl xsd >;
19             use Scalar::Util qw< blessed >;
20             use URI::file;
21              
22             use Sub::Exporter -setup => {
23             exports => [
24             qw< curie iri blank variable literal statement store model parse >,
25             serialize => \&_build_serializer,
26             ],
27             groups => {
28             nodes => [qw< curie iri blank literal variable >],
29             shortcuts => [
30             parse => { -as => 'rdf_parse' },
31             serialize => { -as => 'rdf_string' },
32             ],
33             shortcuts_nodes => [
34             parse => { -as => 'rdf_parse' },
35             serialize => { -as => 'rdf_string' },
36             iri => { -as => 'rdf_resource' },
37             blank => { -as => 'rdf_blank' },
38             literal => { -as => 'rdf_literal' },
39             variable => { -as => 'rdf_variable' },
40             statement => { -as => 'rdf_statement' },
41             ],
42             },
43             };
44              
45             foreach my $nodetype (qw< iri blank variable literal >)
46             {
47             my $orig = $nodetype eq 'iri'
48             ? sub { RDF::Trine::Node::Resource->new(@_) }
49             : RDF::Trine->can($nodetype);
50            
51             my $sub;
52             $sub = sub
53             {
54             shift if blessed $_[0] && $_[0]->isa(__PACKAGE__);
55             return $sub->(@{$_[0]}) if ref $_[0] eq 'ARRAY';
56            
57             my $node = shift;
58            
59             if (blessed($node) and $node->isa('RDF::Trine::Node'))
60             {
61             return $node;
62             }
63            
64             if ($nodetype eq 'iri' and blessed($node) and $node->isa('URI'))
65             {
66             return $orig->("$node", @_);
67             }
68             elsif ($nodetype eq 'literal' and blessed($node) and $node->isa('URI'))
69             {
70             $_[1] //= $xsd->anyURI unless $_[0];
71             return $orig->("$node", @_);
72             }
73            
74             if ($nodetype =~ m[^(iri|blank)$] and $node =~ /^_:(.+)$/)
75             {
76             return RDF::Trine::blank($1, @_);
77             }
78            
79             if ($nodetype =~ m[^(variable|blank)$] and $node =~ /^\?(.+)$/)
80             {
81             return RDF::Trine::variable($1, @_);
82             }
83            
84             $orig->("$node", @_);
85             };
86            
87             *$nodetype = $sub;
88             }
89              
90             sub new
91             {
92             my ($class, @args) = @_;
93             bless \@args, $class;
94             }
95              
96             sub curie
97             {
98             shift if blessed $_[0] && $_[0]->isa(__PACKAGE__);
99             return curie(@{$_[0]}) if ref $_[0] eq 'ARRAY';
100            
101             my $node = shift;
102            
103             if (blessed($node) and $node->isa('RDF::Trine::Node'))
104             {
105             return $node;
106             }
107            
108             if (blessed($node) and $node->isa('URI'))
109             {
110             return RDF::Trine::iri("$node", @_);
111             }
112            
113             if ($node =~ /^_:(.+)$/)
114             {
115             return RDF::Trine::blank($1, @_);
116             }
117            
118             state $ns = RDF::NS::Trine->new('any');
119             $ns->URI($node);
120             }
121              
122             sub statement
123             {
124             shift if blessed $_[0] && $_[0]->isa(__PACKAGE__);
125             return statement(@{$_[0]}) if ref $_[0] eq 'ARRAY';
126            
127             my (@nodes) = map {
128             if (blessed($_) and $_->isa('RDF::Trine::Node')) { $_ }
129             elsif (blessed($_) and $_->isa('URI')) { iri($_) }
130             else { literal($_) }
131             } @_;
132            
133             (@nodes==4)
134             ? RDF::Trine::Statement::Quad->new(@nodes)
135             : RDF::Trine::Statement->new(@nodes)
136             }
137              
138             sub model
139             {
140             shift if blessed $_[0] && $_[0]->isa(__PACKAGE__);
141             return model(@{$_[0]}) if ref $_[0] eq 'ARRAY';
142            
143             my $store = shift;
144             return $store if blessed($store) && $store->isa('RDF::Trine::Model');
145            
146             $store
147             ? RDF::Trine::Model->new($store)
148             : RDF::Trine::Model->new()
149             }
150              
151             sub parse
152             {
153             shift if blessed $_[0] && $_[0]->isa(__PACKAGE__);
154             return parse(@{$_[0]}) if ref $_[0] eq 'ARRAY';
155            
156             my ($thing, %opts) = @_;
157            
158             my $model = delete($opts{into}) // delete($opts{model});
159             my $base = delete($opts{base});
160             my $parser = delete($opts{parser}) // delete($opts{type}) // delete($opts{as}) // delete($opts{using});
161             my $graph = delete($opts{graph}) // delete($opts{context});
162              
163             # Normalise $graph.
164             #
165             $graph = iri($graph) if defined $graph;
166              
167             if (blessed($thing) && $thing->isa('RDF::Trine::Store')
168             or blessed($thing) && $thing->isa('RDF::Trine::Model'))
169             {
170             return model($thing) unless $model;
171            
172             $thing->as_stream->each(sub {
173             my ($s, $p, $o) = shift->nodes;
174             $model->add_statement(
175             $graph
176             ? statement($s, $p, $o, $graph)
177             : statement($s, $p, $o)
178             );
179             });
180             return $model;
181             }
182            
183             $model //= model();
184             return $model unless defined $thing;
185            
186             # Normalise $parser. It should be a class name or blessed object.
187             # If undef, then 'RDF::Trine::Parser' class.
188             # If media type then, figure out correct parser.
189             # If format name then, figure out correct parser.
190             #
191             if (not $parser)
192             { $parser = 'RDF::Trine::Parser' }
193             elsif (not blessed $parser and $parser =~ m{/} and $parser !~ m{^RDF/}i)
194             { $parser = RDF::Trine::Parser->parser_by_media_type($parser)->new }
195             elsif (not blessed $parser)
196             { $parser = RDF::Trine::Parser->new($parser) }
197              
198             # Normalise $base. Accept RDF::Trine::Nodes.
199             #
200             if (blessed $base and $base->isa('RDF::Trine::Node::Resource'))
201             {
202             $base = $base->uri;
203             }
204              
205             # Deal with $thing being a URI.
206             # "file://" is explicitly not handled here.
207             #
208             if (blessed($thing) && $thing->isa('URI')
209             or blessed($thing) && $thing->isa('RDF::Trine::Node::Resource') && ($thing = $thing->uri)
210             or !blessed($thing) && $thing =~ m{^(https?|ftp|file|data):\S+$})
211             {
212             if (is_fileuri $thing)
213             {
214             # Convert to a local path, and allow to fall through...
215             URI->new("$thing")->file;
216             }
217             elsif (not ref $parser and $parser eq 'RDF::Trine::Parser')
218             {
219             RDF::Trine::Parser->parse_url_into_model(
220             "$thing",
221             $model,
222             maybe context => $graph,
223             );
224             return $model;
225             }
226             else
227             {
228             # UA string consistent with RDF::Trine::Parser
229             my $ua = LWP::UserAgent->new(agent => "RDF::Trine/$RDF::Trine::VERSION");
230             my $resp = $ua->get("$thing");
231             $parser->parse_into_model(
232             ("$base"//"$thing"),
233             $resp->decoded_content,
234             $model,
235             maybe context => $graph,
236             );
237             return $model;
238             }
239             }
240            
241             # Deal with $thing being a filename.
242             #
243             if (is_filename $thing)
244             {
245             $base //= URI::file->new_abs("$thing");
246             $parser->parse_file_into_model(
247             "$base",
248             "$thing",
249             $model,
250             maybe context => $graph,
251             );
252             return $model;
253             }
254            
255             croak "No base URI provided" unless $base;
256            
257             # Deal with $thing being a filehandle (or something similar).
258             #
259             if (is_filehandle $thing)
260             {
261             $parser->parse_file_into_model(
262             "$base",
263             $thing,
264             $model,
265             maybe context => $graph,
266             );
267             return $model;
268             }
269            
270             croak "No parser provided for parsing" unless blessed $parser;
271             $parser->parse_into_model(
272             "$base",
273             $thing,
274             $model,
275             maybe context => $graph,
276             );
277            
278             return $model;
279             }
280              
281             sub _build_serializer
282             {
283             my ($class, $name, $arg) = @_;
284            
285             my $sub;
286             $sub = sub
287             {
288             shift if blessed $_[0] && $_[0]->isa(__PACKAGE__);
289             return $sub->(@{$_[0]}) if ref $_[0] eq 'ARRAY';
290            
291             my ($data, %opts) = do {
292             (@_==2)
293             ? ($_[0], as => $_[1])
294             : @_
295             };
296            
297             my $ser = delete($opts{serializer})
298             // delete($opts{type})
299             // delete($opts{as})
300             // delete($opts{using})
301             // $arg->{type}
302             // $arg->{-type}
303             // 'Turtle';
304            
305             my $file = delete($opts{to})
306             // delete($opts{file})
307             // delete($opts{output});
308            
309             if (not blessed $ser)
310             {
311             $ser = RDF::Trine::Serializer->new($ser, %opts);
312             }
313            
314             if (blessed $data and $data->isa('RDF::Trine::Iterator'))
315             {
316             return defined($file)
317             ? $ser->serialize_iterator_to_file($file, $data)
318             : $ser->serialize_iterator_to_string($data);
319             }
320            
321             return defined($file)
322             ? $ser->serialize_model_to_file($file, $data)
323             : $ser->serialize_model_to_string($data);
324             }
325             }
326              
327             *serialize = __PACKAGE__->_build_serializer(serialize => {});
328              
329             __PACKAGE__
330             __END__
331              
332             =head1 NAME
333              
334             RDF::TrineX::Functions - some shortcut functions for RDF::Trine's object-oriented interface
335              
336             =head1 SYNOPSIS
337              
338             use RDF::TrineX::Functions -all;
339            
340             my $model = model();
341             parse('/tmp/mydata.rdf', into => $model);
342            
343             $model->add_statement(statement(
344             iri('http://example.com/'),
345             iri('http://purl.org/dc/terms/title'),
346             "An Example",
347             ));
348            
349             print RDF::Trine::Serializer
350             -> new('Turtle')
351             -> serialize_model_to_string($model);
352              
353             =head1 DESCRIPTION
354              
355             This is a replacement for the venerable RDF::TrineShortcuts. Not a
356             drop-in replacement. It has fewer features, fewer dependencies,
357             less hackishness, less magic and fewer places it can go wrong.
358              
359             It uses Sub::Exporter, which allows exported functions to be renamed
360             easily:
361              
362             use RDF::TrineX::Functions
363             parse => { -as => 'parse_rdf' };
364              
365             =head2 Functions
366              
367             =over
368              
369             =item C<iri>, C<literal>, C<blank>, C<variable>
370              
371             As per the similarly named functions exported by L<RDF::Trine> itself.
372              
373             These are wrapped with a very tiny bit of DWIMmery. A blessed L<URI>
374             object passed to C<iri> will be handled properly; a blessed URI
375             object passed to C<literal> will default the datatype to xsd:anyURI.
376             A string starting with "_:" passed to either C<iri> or C<blank> will
377             correctly create a blank node. A string starting with "?" passed to
378             either C<blank> or C<variable> will correctly create a variable. If
379             any of them are passed an existing RDF::Trine::Node, it will be
380             passed through untouched.
381              
382             Other than that, no magic.
383              
384             =item C<< curie >>
385              
386             Like C<iri> but passes strings through L<RDF::NS::Trine>.
387              
388             =item C<< statement(@nodes) >>
389              
390             As per the similarly named function exported by L<RDF::Trine> itself.
391              
392             Again, a tiny bit of DWIMmery: blessed URI objects are passed through
393             C<iri> and unblessed scalars (i.e. strings) are assumed to be literals.
394              
395             =item C<store>
396              
397             As per the similarly named function exported by L<RDF::Trine> itself.
398              
399             =item C<model>
400              
401             Returns a new RDF::Trine::Model. May be passed a store as a parameter.
402              
403             =item C<< parse($source, %options) >>
404              
405             Parses the source and returns an RDF::Trine::Model. The source may be:
406              
407             =over
408              
409             =item * a URI
410              
411             A string URI, blessed URI object or RDF::Trine::Node::Resource, which
412             will be retrieved and parsed.
413              
414             =item * a file
415              
416             A filehandle, L<Path::Class::File>, L<IO::All>, L<IO::Handle> object,
417             or the name of an existing file (i.e. a scalar string). The file will
418             be read and parsed.
419              
420             Except in the case of L<Path::Class::File>, L<IO::All> and strings,
421             you need to tell the C<parse> function what parser to use, and what
422             base URI to use.
423              
424             =item * a string
425              
426             You need to tell the C<parse> function what parser to use, and what
427             base URI to use.
428              
429             =item * a model or store
430              
431             An existing model or store, which will just be returned as-is.
432              
433             =item * undef
434              
435             Returns an empty model.
436              
437             =back
438              
439             The C<parser> option can be used to provide a blessed L<RDF::Trine::Parser>
440             object to use; the C<type> option can be used instead to provide a media
441             type hint. The C<base> option provides the base URI. The C<model> option
442             can be used to tell this function to parse into an existing model rather
443             than returning a new one. The C<graph> option may be used to provide a graph
444             URI.
445              
446             C<into> is an alias for C<model>; C<type>, C<using> and C<as> are
447             aliases for C<parser>; C<context> is an alias for C<graph>.
448              
449             Examples:
450              
451             my $model = parse('/tmp/data.ttl', as => 'Turtle');
452              
453             my $data = iri('http://example.com/data.nt');
454             my $parser = RDF::Trine::Parser::NTriples->new;
455             my $model = model();
456            
457             parse($data, using => $parser, into => $model);
458              
459             =item C<< serialize($data, %options) >>
460              
461             Serializes the data (which can be an RDF::Trine::Model or an
462             RDF::Trine::Iterator) and returns it as a string.
463              
464             The C<serializer> option can be used to provide a blessed
465             L<RDF::Trine::Serializer> object to use; the C<type> option can be used
466             instead to provide a type hint. The C<output> option can be used to
467             provide a filehandle, IO::All, Path::Class::File or file name to
468             write to instead of returning the results as a string.
469              
470             C<to> and C<file> are aliases for C<output>; C<type>, C<using> and C<as>
471             are aliases for C<serializer>.
472              
473             Examples:
474              
475             print serialize($model, as => 'Turtle');
476              
477             my $file = Path::Class::File->new('/tmp/data.nt');
478             serialize($iterator, to => $file, as => 'NTriples');
479              
480             =back
481              
482             =head2 Array References
483              
484             In addition to the above interface, each function supports being called with a
485             single arrayref argument. In those cases, the arrayref is dereferenced into an
486             array, and treated as a list of arguments. That is, the following are
487             equivalent:
488              
489             foo($bar, $baz);
490             foo([$bar, $baz]);
491              
492             This is handy if you're writing a module of your own and wish to accept some
493             RDF data:
494              
495             sub my_method {
496             my ($self, $rdf, $foo) = @_;
497             $rdf = parse($rdf);
498            
499             ....
500             }
501              
502             Your method can now be called like this:
503              
504             $object->my_method($model, 'foo');
505            
506             $object->my_method($url, 'foo');
507            
508             $object->my_method(
509             [ $filehandle, as => 'Turtle', base => $uri ],
510             'foo',
511             );
512              
513             =head2 Export
514              
515             By default, nothing is exported. You need to request things:
516              
517             use RDF::TrineX::Functions qw< iri literal blank statement model >;
518              
519             Thanks to L<Sub::Exporter>, you can rename functions:
520              
521             use RDF::TrineX::Functions
522             qw< literal statement model >,
523             blank => { -as => 'bnode' },
524             iri => { -as => 'resource' };
525              
526             If you want to export everything, you can do:
527              
528             use RDF::TrineX::Functions -all;
529              
530             To export just the functions which generate RDF::Trine::Node objects:
531              
532             use RDF::TrineX::Functions -nodes;
533              
534             Or maybe even:
535              
536             use RDF::TrineX::Functions -nodes => { -suffix => '_node' };
537              
538             If you want to export something roughly compatible with the old
539             RDF::TrineShortcuts, then there's:
540              
541             use RDF::TrineX::Functions -shortcuts;
542              
543             When exporting the C<serialize> function you may set a default format:
544              
545             use RDF::TrineX::Functions
546             serialize => { -type => 'NTriples' };
547              
548             This will be used when C<serialize> is called with no explicit type given.
549              
550             =head2 Pseudo-OO interface
551              
552             =over
553              
554             =item C<new>
555              
556             This acts as a constructor, returning a new RDF::TrineX::Functions object.
557              
558             =back
559              
560             All the normal functions can be called as methods:
561              
562             my $R = RDF::TrineX::Functions->new;
563             my $model = $R->model;
564              
565             There's no real advantage to using this module as an object, but it can
566             help you avoid namespace pollution.
567              
568             =head1 BUGS
569              
570             Please report any bugs to
571             L<http://rt.cpan.org/Dist/Display.html?Queue=RDF-TrineX-Functions>.
572              
573             =head1 SEE ALSO
574              
575             L<RDF::Trine>, L<RDF::QueryX::Lazy>, L<RDF::NS>.
576              
577             =head1 AUTHOR
578              
579             Toby Inkster E<lt>tobyink@cpan.orgE<gt>.
580              
581             =head1 COPYRIGHT AND LICENCE
582              
583             This software is copyright (c) 2012 by Toby Inkster.
584              
585             This is free software; you can redistribute it and/or modify it under
586             the same terms as the Perl 5 programming language system itself.
587              
588             =head1 DISCLAIMER OF WARRANTIES
589              
590             THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
591             WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
592             MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
593