RDF-aREF-0.25 000755 001750 001750 0 12475316027 11414 5 ustar 00voj voj 000000 000000 README 100644 001750 001750 6152 12475316027 12361 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25 NAME
RDF::aREF - Another RDF Encoding Form
SYNOPSIS
use RDF::aREF;
my $rdf = {
_id => 'http://example.com/people#alice',
foaf_name => 'Alice Smith',
foaf_age => '42^xsd_integer',
foaf_homepage => [
{
_id => 'http://personal.example.org/',
dct_modified => '2010-05-29^xsd_date',
},
'http://work.example.com/asmith/',
],
foaf_knows => {
dct_description => 'a nice guy@en',
},
};
decode_aref( $rdf,
callback => sub {
my ($subject, $predicate, $object, $language, $datatype) = @_;
...
}
);
my @lastmod = aref_query $rdf, 'foaf_homepage.dct_modified^';
my $model = RDF::Trine::Model->new;
decode_aref( $rdf, callback => $model );
print RDF::Trine::Serializer->new('Turtle')->serialize_model_to_string($model);
my $model = RDF::Trine::Model->new;
RDF::Trine::Parser->parse_url_into_model($url, $model);
my $aref = encode_aref $model;
DESCRIPTION
aREF (another RDF Encoding Form ) is an
encoding of RDF graphs in form of arrays, hashes, and Unicode strings.
This module provides methods for decoding from aREF data to RDF triples
(RDF::aREF::Decoder), for encoding RDF data in aREF
(RDF::aREF::Encoder), and for querying parts of an RDF graph
(RDF::aREF::Query).
EXPORTED FUNCTIONS
The following functions are exported by default.
decode_aref $aref [, %options ]
Decodes an aREF document given as hash reference with
RDF::aREF::Decoder. Equivalent to
"RDF::aREF::Decoder->new(%options)->decode($aref)".
encode_aref $graph [, %options ]
Construct an aREF subject mapfrom an RDF graph. The RDF::aREF::Encoder
for possible options. The $graph can be supplied as:
* instance of RDF::Trine::Model
* instance of RDF::Trine::Model::Iterator
* an URL or a filename (only if RDF::Trine is installed)
* instance of Attean::API::TripleIterator (experimental)
* instance of Attean::API::TripleStore (experimental)
* hash reference with RDF/JSON format
(as returned by method "as_hashref" in RDF::Trine::Model)
aref_query $graph, [ $origin ], @queries
Query parts of an aREF data structure by aREF query expressions
and return a list. See
RDF::aREF::Query for details.
aref_query_map( $graph, [ $origin ], $query_map )
Map parts of an aREF data structure to a flat key-value structure.
SEE ALSO
* aREF is specified at .
* See Catmandu::RDF for an application of this module.
* Usee RDF::Trine for more elaborated handling of RDF data in Perl.
* See RDF::YAML for a similar (outdated) RDF encoding in YAML.
COPYRIGHT AND LICENSE
Copyright Jakob Voss, 2014-
This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
Changes 100644 001750 001750 5161 12475316027 12773 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25 # Changelog for RDF-aREF
0.25 2015-03-03 12:33:40 CET
- support aref_query on property maps
- support aREF query expression with multiple items
- support encoding RDF from file
- support Unicode normalization
0.23 2014-12-11 16:40:30 CET
- new decoder option keep_bnode_map
0.22 2014-12-11 14:57:01 CET
- modified encoder, added subject and triple method
- remove or hide some exerimental methods
- avoid malformed bnode identifiers
0.21 2014-10-28 14:08:40 CET
- allow 'a' in aREF query expressions
- experimental support to encode from URL
- support multiple queries in aref_query
0.20 2014-10-16 12:09:05 CEST
- support empty queries
- fix bug for literal nodes without language tags in encoder
- new encoder method add_hashref
0.19 2014-10-15 14:56:18 CEST
- added encoding methods
- removed internal method aref_to_trine_statement (issue #6)
0.18 2014-10-14 10:35:56 CEST
- implement aref_query_map (issue #7)
- adjust subjects to aREF spec (more strict)
0.17 2014-10-07 13:30:47 CEST
- fix failure on Perl 5.10
- more cleanup and tests
0.16 2014-10-07 10:50:16 CEST
- cleanup
- only require Perl 5.10 (issue #5)
0.15 2014-10-05 15:16:09 CEST
- better support RDF::Trine in Encoder
- introduced RDF::aREF::Query (experimental)
0.14 2014-10-02 14:14:09 CEST
- adjust with aREF 0.19
- added aref_query (experimental)
0.13 2014-10-01 16:09:44 CEST
- improveded Encoder
0.12 2014-10-01 14:45:09 CEST
- migrated to Dist::Zilla
- added methods aref_get_literal and aref_get_resource
- disallow ':' in predicates
- added partial Encoder
0.11 2014-04-09T12:56:37
- don't die on malformed IRIs
0.10 2014-01-22T11:11:50
- fixed bnode collision bug
- new option: bnode_prefix
- show how to serialize with RDF::Trine
- more test cases
0.09 2014-01-21T13:11:32
- better error message when expecting an URI (close #3)
- Avoid numeric blank node identifiers.
Although it's legal, some parsers don't like them.
- Show how to decode into RDF::Trine::Model
0.08 2014-01-15T11:08:31
- facilitate use of this module together with RDF::Trine
0.07 2013-12-18T08:21:41
- increased error detection
- fixed test_requires
0.06 2013-12-14T20:15:38
- extended test suite
- test-cases with errors
0.05 2013-12-04T20:01:53
- improved handling of undefined/null
- use warnings; list provides
0.04 2013-12-03T08:51:00
- improved error handling, fixed bug in subject-maps
0.03 2013-12-02T13:47:26
- release
- avoid lexical $_
- added error test
- run coverage test with RDF::Trine
- fixed links
- improved unit tests
- rewrote aREF decoder
- split RDF::aREF from Catmandu-RDF
t 000755 001750 001750 0 12475316027 11600 5 ustar 00voj voj 000000 000000 RDF-aREF-0.25 nfc.t 100644 001750 001750 476 12475316027 12662 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t use strict;
use warnings;
use Test::More;
use RDF::aREF;
my $rdf = "t/nfc.ttl";
my $aref = encode_aref $rdf, NFC => 1;
is $aref->{foaf_given}, $aref->{foaf_surname}, "Unicode Normalization NFC";
$aref = encode_aref $rdf;
isnt $aref->{foaf_given}, $aref->{foaf_surname}, "Unicode Normalization NFC";
done_testing;
LICENSE 100644 001750 001750 43617 12475316027 12535 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25 This software is copyright (c) 2014- by -.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
Terms of the Perl programming language system itself
a) the GNU General Public License as published by the Free
Software Foundation; either version 1, or (at your option) any
later version, or
b) the "Artistic License"
--- The GNU General Public License, Version 1, February 1989 ---
This software is Copyright (c) 2014- by -.
This is free software, licensed under:
The GNU General Public License, Version 1, February 1989
GNU GENERAL PUBLIC LICENSE
Version 1, February 1989
Copyright (C) 1989 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The license agreements of most software companies try to keep users
at the mercy of those companies. By contrast, our General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. The
General Public License applies to the Free Software Foundation's
software and to any other program whose authors commit to using it.
You can use it for your programs, too.
When we speak of free software, we are referring to freedom, not
price. Specifically, the General Public License is designed to make
sure that you have the freedom to give away or sell copies of free
software, that you receive source code or can get it if you want it,
that you can change the software or use pieces of it in new free
programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of a such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must tell them their rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any program or other work which
contains a notice placed by the copyright holder saying it may be
distributed under the terms of this General Public License. The
"Program", below, refers to any such program or work, and a "work based
on the Program" means either the Program or any work containing the
Program or a portion of it, either verbatim or with modifications. Each
licensee is addressed as "you".
1. You may copy and distribute verbatim copies of the Program's source
code as you receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice and
disclaimer of warranty; keep intact all the notices that refer to this
General Public License and to the absence of any warranty; and give any
other recipients of the Program a copy of this General Public License
along with the Program. You may charge a fee for the physical act of
transferring a copy.
2. You may modify your copy or copies of the Program or any portion of
it, and copy and distribute such modifications under the terms of Paragraph
1 above, provided that you also do the following:
a) cause the modified files to carry prominent notices stating that
you changed the files and the date of any change; and
b) cause the whole of any work that you distribute or publish, that
in whole or in part contains the Program or any part thereof, either
with or without modifications, to be licensed at no charge to all
third parties under the terms of this General Public License (except
that you may choose to grant warranty protection to some or all
third parties, at your option).
c) If the modified program normally reads commands interactively when
run, you must cause it, when started running for such interactive use
in the simplest and most usual way, to print or display an
announcement including an appropriate copyright notice and a notice
that there is no warranty (or else, saying that you provide a
warranty) and that users may redistribute the program under these
conditions, and telling the user how to view a copy of this General
Public License.
d) You may charge a fee for the physical act of transferring a
copy, and you may at your option offer warranty protection in
exchange for a fee.
Mere aggregation of another independent work with the Program (or its
derivative) on a volume of a storage or distribution medium does not bring
the other work under the scope of these terms.
3. You may copy and distribute the Program (or a portion or derivative of
it, under Paragraph 2) in object code or executable form under the terms of
Paragraphs 1 and 2 above provided that you also do one of the following:
a) accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of
Paragraphs 1 and 2 above; or,
b) accompany it with a written offer, valid for at least three
years, to give any third party free (except for a nominal charge
for the cost of distribution) a complete machine-readable copy of the
corresponding source code, to be distributed under the terms of
Paragraphs 1 and 2 above; or,
c) accompany it with the information you received as to where the
corresponding source code may be obtained. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form alone.)
Source code for a work means the preferred form of the work for making
modifications to it. For an executable file, complete source code means
all the source code for all modules it contains; but, as a special
exception, it need not include source code for modules which are standard
libraries that accompany the operating system on which the executable
file runs, or for standard header files or definitions files that
accompany that operating system.
4. You may not copy, modify, sublicense, distribute or transfer the
Program except as expressly provided under this General Public License.
Any attempt otherwise to copy, modify, sublicense, distribute or transfer
the Program is void, and will automatically terminate your rights to use
the Program under this License. However, parties who have received
copies, or rights to use copies, from you under this General Public
License will not have their licenses terminated so long as such parties
remain in full compliance.
5. By copying, distributing or modifying the Program (or any work based
on the Program) you indicate your acceptance of this license to do so,
and all its terms and conditions.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the original
licensor to copy, distribute or modify the Program subject to these
terms and conditions. You may not impose any further restrictions on the
recipients' exercise of the rights granted herein.
7. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of the license which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
the license, you may choose any version ever published by the Free Software
Foundation.
8. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to humanity, the best way to achieve this is to make it
free software which everyone can redistribute and change under these
terms.
To do so, attach the following notices to the program. It is safest to
attach them to the start of each source file to most effectively convey
the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
Copyright (C) 19yy
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19xx name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the
appropriate parts of the General Public License. Of course, the
commands you use may be called something other than `show w' and `show
c'; they could even be mouse-clicks or menu items--whatever suits your
program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
program `Gnomovision' (a program to direct compilers to make passes
at assemblers) written by James Hacker.
, 1 April 1989
Ty Coon, President of Vice
That's all there is to it!
--- The Artistic License 1.0 ---
This software is Copyright (c) 2014- by -.
This is free software, licensed under:
The Artistic License 1.0
The Artistic License
Preamble
The intent of this document is to state the conditions under which a Package
may be copied, such that the Copyright Holder maintains some semblance of
artistic control over the development of the package, while giving the users of
the package the right to use and distribute the Package in a more-or-less
customary fashion, plus the right to make reasonable modifications.
Definitions:
- "Package" refers to the collection of files distributed by the Copyright
Holder, and derivatives of that collection of files created through
textual modification.
- "Standard Version" refers to such a Package if it has not been modified,
or has been modified in accordance with the wishes of the Copyright
Holder.
- "Copyright Holder" is whoever is named in the copyright or copyrights for
the package.
- "You" is you, if you're thinking about copying or distributing this Package.
- "Reasonable copying fee" is whatever you can justify on the basis of media
cost, duplication charges, time of people involved, and so on. (You will
not be required to justify it to the Copyright Holder, but only to the
computing community at large as a market that must bear the fee.)
- "Freely Available" means that no fee is charged for the item itself, though
there may be fees involved in handling the item. It also means that
recipients of the item may redistribute it under the same conditions they
received it.
1. You may make and give away verbatim copies of the source form of the
Standard Version of this Package without restriction, provided that you
duplicate all of the original copyright notices and associated disclaimers.
2. You may apply bug fixes, portability fixes and other modifications derived
from the Public Domain or from the Copyright Holder. A Package modified in such
a way shall still be considered the Standard Version.
3. You may otherwise modify your copy of this Package in any way, provided that
you insert a prominent notice in each changed file stating how and when you
changed that file, and provided that you do at least ONE of the following:
a) place your modifications in the Public Domain or otherwise make them
Freely Available, such as by posting said modifications to Usenet or an
equivalent medium, or placing the modifications on a major archive site
such as ftp.uu.net, or by allowing the Copyright Holder to include your
modifications in the Standard Version of the Package.
b) use the modified Package only within your corporation or organization.
c) rename any non-standard executables so the names do not conflict with
standard executables, which must also be provided, and provide a separate
manual page for each non-standard executable that clearly documents how it
differs from the Standard Version.
d) make other distribution arrangements with the Copyright Holder.
4. You may distribute the programs of this Package in object code or executable
form, provided that you do at least ONE of the following:
a) distribute a Standard Version of the executables and library files,
together with instructions (in the manual page or equivalent) on where to
get the Standard Version.
b) accompany the distribution with the machine-readable source of the Package
with your modifications.
c) accompany any non-standard executables with their corresponding Standard
Version executables, giving the non-standard executables non-standard
names, and clearly documenting the differences in manual pages (or
equivalent), together with instructions on where to get the Standard
Version.
d) make other distribution arrangements with the Copyright Holder.
5. You may charge a reasonable copying fee for any distribution of this
Package. You may charge any fee you choose for support of this Package. You
may not charge a fee for this Package itself. However, you may distribute this
Package in aggregate with other (possibly commercial) programs as part of a
larger (possibly commercial) software distribution provided that you do not
advertise this Package as a product of your own.
6. The scripts and library files supplied as input to or produced as output
from the programs of this Package do not automatically fall under the copyright
of this Package, but belong to whomever generated them, and may be sold
commercially, and may be aggregated with this Package.
7. C or perl subroutines supplied by you and linked into this Package shall not
be considered part of this Package.
8. The name of the Copyright Holder may not be used to endorse or promote
products derived from this software without specific prior written permission.
9. 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.
The End
cpanfile 100644 001750 001750 343 12475316027 13161 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25 requires 'perl', '5.10.0';
requires 'RDF::NS', '20140908';
suggests 'RDF::Trine', '1.010';
suggests 'Unicode::Normalize';
on test => sub {
requires 'JSON';
requires 'RDF::Trine';
requires 'Unicode::Normalize';
};
dist.ini 100644 001750 001750 33 12475316027 13075 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25 author=Jakob Voß
[@Milla]
null.t 100644 001750 001750 2052 12475316027 13076 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t use strict;
use warnings;
use Test::More;
use RDF::aREF;
sub decode_ok(@) {
my $aref = shift;
my $count = shift;
my $triples = 0;
RDF::aREF::Decoder
->new( callback => sub { $triples++ }, @_ )
->decode($aref);
local $Test::Builder::Level = $Test::Builder::Level + 1;
is $triples, $count;
}
# ignore undef
decode_ok { _id => undef, a => 'ex:ample' }, 0;
decode_ok { _id => 'an:id', a => 'ex:ample' }, 1;
decode_ok { _id => 'an:id', a => undef }, 0;
decode_ok { _id => 'an:id', a => [undef,'ex:ample'] }, 1;
# ignore undef and '0'
decode_ok { _id => '0', a => 'ex:ample' }, 0, null => 0;
decode_ok { _id => 'an:id', a => 'ex:ample' }, 1, null => 0;
decode_ok { _id => 'an:id', a => '0' }, 0, null => 0;
decode_ok { _id => 'an:id', a => ['0','ex:ample'] }, 1, null => 0;
decode_ok { _id => 'an:id', a => [undef,'ex:ample'] }, 1, null => undef;
decode_ok { 'an:id' => { a => 'ex:ample' }, '0' => { a => 'ex:ample' } }, 1, null => 0;
# ignore as subject
decode_ok { '' => { a => 'ex:ample' } }, 0, null => '';
done_testing;
META.yml 100644 001750 001750 1515 12475316027 12750 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25 ---
abstract: 'Another RDF Encoding Form'
author:
- 'Jakob Voß'
build_requires:
JSON: '0'
RDF::Trine: '0'
Unicode::Normalize: '0'
configure_requires:
Module::Build::Tiny: '0.039'
dynamic_config: 0
generated_by: 'Dist::Milla version v1.0.8, Dist::Zilla version 5.020, CPAN::Meta::Converter version 2.141520'
license: perl
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
version: '1.4'
name: RDF-aREF
no_index:
directory:
- t
- xt
- inc
- share
- eg
- examples
requires:
RDF::NS: '20140908'
perl: v5.10.0
resources:
bugtracker: https://github.com/nichtich/RDF-aREF/issues
homepage: https://github.com/nichtich/RDF-aREF
repository: https://github.com/nichtich/RDF-aREF.git
version: '0.25'
x_contributors:
- 'Jakob Voss '
- 'Jakob Voss '
MANIFEST 100644 001750 001750 4310 12475316027 12624 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25 # This file was automatically generated by Dist::Zilla::Plugin::Manifest v5.020.
Build.PL
Changes
LICENSE
MANIFEST
META.json
META.yml
README
cpanfile
dist.ini
lib/RDF/aREF.pm
lib/RDF/aREF/Decoder.pm
lib/RDF/aREF/Encoder.pm
lib/RDF/aREF/Query.pm
t/aref-query.t
t/decode-errors.t
t/decoder.t
t/doi-example.json
t/encode_aref.t
t/encoder.t
t/example.json
t/nfc.t
t/nfc.ttl
t/null.t
t/objects.t
t/release-pod-syntax.t
t/suite.t
t/suite/README.md
t/suite/blanks.json
t/suite/blanks.nt
t/suite/invalid-datatype-01.err
t/suite/invalid-datatype-01.json
t/suite/invalid-object-01.err
t/suite/invalid-object-01.json
t/suite/invalid-object-02.err
t/suite/invalid-object-02.json
t/suite/invalid-object-03.err
t/suite/invalid-object-03.json
t/suite/invalid-predicate-01.err
t/suite/invalid-predicate-01.json
t/suite/invalid-predicate-02.err
t/suite/invalid-predicate-02.json
t/suite/invalid-predicate-03.err
t/suite/invalid-predicate-03.json
t/suite/invalid-subject-01.err
t/suite/invalid-subject-01.json
t/suite/invalid-subject-02.err
t/suite/invalid-subject-02.json
t/suite/invalid-subject-03.err
t/suite/invalid-subject-03.json
t/suite/invalid-subject-04.err
t/suite/invalid-subject-04.json
t/suite/invalid-subject-05.err
t/suite/invalid-subject-05.json
t/suite/invalid-subject-06.err
t/suite/invalid-subject-06.json
t/suite/invalid-subject-07.err
t/suite/invalid-subject-07.json
t/suite/invalid-subject-08.err
t/suite/invalid-subject-08.json
t/suite/invalid-subject-09.err
t/suite/invalid-subject-09.json
t/suite/invalid-subject-10.err
t/suite/invalid-subject-10.json
t/suite/invalid-subject-10.nt
t/suite/json-ld-14.json
t/suite/json-ld-14.nt
t/suite/json-ld-23.json
t/suite/json-ld-23.nt
t/suite/rdf-trine-model.json
t/suite/rdf-trine-model.nt
t/suite/strict-invalid-subject-11.err
t/suite/strict-invalid-subject-11.json
t/suite/strict-invalid-subject-12.err
t/suite/strict-invalid-subject-12.json
t/suite/strict-invalid-subject-13.err
t/suite/strict-invalid-subject-13.json
t/suite/strict-null-object-1.err
t/suite/strict-null-object-1.json
t/suite/strict-null-object-2.err
t/suite/strict-null-object-2.json
t/suite/strict-null-object-2.nt
t/suite/uri-object-01.json
t/suite/uri-object-01.nt
t/suite/uri-object-02.json
t/suite/uri-object-02.nt
t/trine.t
Build.PL 100644 001750 001750 222 12475316027 12745 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25 # This Build.PL for RDF-aREF was generated by Dist::Zilla::Plugin::ModuleBuildTiny 0.006.
use v5.10.0;
use Module::Build::Tiny 0.039;
Build_PL();
nfc.ttl 100644 001750 001750 206 12475316027 13211 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t "\u0045\u0304\u0300\uAC00\u11A8" ;
"\u1E14\uAC01" .
suite.t 100644 001750 001750 3140 12475316027 13254 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t use strict;
use Test::More;
use File::Find;
BEGIN {
eval {
require RDF::Trine::Model;
require RDF::Trine::Serializer::NTriples::Canonical;
require JSON;
1;
} or do {
plan skip_all => "RDF::Trine or JSON required";
};
}
sub slurp {
local (@ARGV, $/) = @_;
my $data = -e $ARGV[0] ? <> : "";
$data =~ s/\n$//;
$data;
}
use RDF::aREF qw(decode_aref);
use RDF::aREF::Decoder;
sub trine_statement { # TODO: remove/modify this method
RDF::aREF::Decoder::trine_statement(@_);
}
foreach my $file (sort ) {
$file =~ s/\.json$//;
my $name = $file; $name =~ s{^t/suite/}{};
my $aref = JSON::from_json(slurp("$file.json"));
my $nt = slurp("$file.nt");
my $err = slurp("$file.err");
my @errors;
my $model = RDF::Trine::Model->new;
# TODO: use iterator instead
# a (->next returns a trine-statement)
$model->begin_bulk_ops;
my $decoder = RDF::aREF::Decoder->new(
callback => sub {
$model->add_statement( trine_statement( @_ ) )
},
complain => 2,
strict => ($file =~ /strict/ ? 1 : 0),
);
eval { $decoder->decode($aref) };
if ($err) {
my $got = $@;
$got =~ s{ at t/suite\.t line \d+[.]?\n}{};
is $got, $err, "$name.err";
} else {
$model->end_bulk_ops;
my $got = RDF::Trine::Serializer::NTriples::Canonical->new(
onfail => 'truncate',
)->serialize_model_to_string($model);
$got =~ s/\r|\n$//g;
$file =~ s{.*/}{};
is $got, $nt, $file;
}
}
done_testing;
trine.t 100644 001750 001750 4722 12475316027 13253 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t use strict;
use Test::More;
use RDF::aREF;
use RDF::aREF::Encoder;
BEGIN {
eval {
require RDF::Trine;
RDF::Trine->import(qw(statement iri literal));
1;
} or do {
plan skip_all => "RDF::Trine required";
};
}
my $model = RDF::Trine::Model->new;
decode_aref( {
_id => 'http://example.org/alice',
a => 'foaf_Person',
foaf_knows => 'http://example.org/bob'
},
callback => $model,
);
is $model->size, 2, 'added two statements';
my $aref = encode_aref $model;
is_deeply $aref, {
_id => 'http://example.org/alice',
a => 'foaf_Person',
foaf_knows => ''
}, 'encode_aref from RDF::Trine::Model';
decode_aref({ _id => 'http://example.org/alice', a => 'foaf_Person' }, callback => $model);
decode_aref({ _id => 'http://example.org/bob', a => 'foaf_Person' }, callback => $model);
is $model->size, 3, 'added another statement';
is_deeply( encode_aref($model), {
'http://example.org/alice' => {
'a' => 'foaf_Person',
'foaf_knows' => ''
},
'http://example.org/bob' => {
'a' => 'foaf_Person'
}
}, 'converted predicate map to subject map');
# bnodes
$model = RDF::Trine::Model->new;
my $decoder = RDF::aREF::Decoder->new( callback => $model );
my $aref = { _id => '', foaf_knows => { foaf_name => 'alice' } };
$decoder->decode( $aref );
$decoder->decode( $aref );
is $model->size, 4, 'no bnode collision';
is $decoder->bnode_count, 2;
$decoder->bnode_count(1);
$decoder->decode( $aref );
is $model->size, 4, 'bnode collision';
# errors
my $warning;
local $SIG{__WARN__} = sub { $warning = shift };
decode_aref( {
_id => 'isbn:123',
rdfs_seeAlso => [
'isbn:456 x', # looks like an IRI to aREF but rejected by Trine
'isbn:789'
]
},
callback => $model,
);
ok $warning, "bad IRI";
is $model->size, 5, 'ignored illformed URI';
my $encoder = RDF::aREF::Encoder->new(ns => '20140910');
is_deeply $encoder->triple(
iri('http://example.org/'),
iri('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
iri('http://xmlns.com/foaf/0.1/Agent'),
), { _id => 'http://example.org/', a => 'foaf_Agent' }, 'triple';
is_deeply $encoder->triple(
iri('http://example.org/'),
iri('http://xmlns.com/foaf/0.1/name'),
literal('Anne','de'),
), { _id => 'http://example.org/', foaf_name => 'Anne@de' }, 'triple';
# TODO encode_aref $model / $iterator
done_testing;
META.json 100644 001750 001750 3307 12475316027 13121 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25 {
"abstract" : "Another RDF Encoding Form",
"author" : [
"Jakob Voß"
],
"dynamic_config" : 0,
"generated_by" : "Dist::Milla version v1.0.8, Dist::Zilla version 5.020, CPAN::Meta::Converter version 2.141520",
"license" : [
"perl_5"
],
"meta-spec" : {
"url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec",
"version" : "2"
},
"name" : "RDF-aREF",
"no_index" : {
"directory" : [
"t",
"xt",
"inc",
"share",
"eg",
"examples"
]
},
"prereqs" : {
"configure" : {
"requires" : {
"Module::Build::Tiny" : "0.039"
}
},
"develop" : {
"requires" : {
"Dist::Milla" : "v1.0.8",
"Test::Pod" : "1.41"
}
},
"runtime" : {
"requires" : {
"RDF::NS" : "20140908",
"perl" : "v5.10.0"
},
"suggests" : {
"RDF::Trine" : "1.010",
"Unicode::Normalize" : "0"
}
},
"test" : {
"requires" : {
"JSON" : "0",
"RDF::Trine" : "0",
"Unicode::Normalize" : "0"
}
}
},
"release_status" : "stable",
"resources" : {
"bugtracker" : {
"web" : "https://github.com/nichtich/RDF-aREF/issues"
},
"homepage" : "https://github.com/nichtich/RDF-aREF",
"repository" : {
"type" : "git",
"url" : "https://github.com/nichtich/RDF-aREF.git",
"web" : "https://github.com/nichtich/RDF-aREF"
}
},
"version" : "0.25",
"x_contributors" : [
"Jakob Voss ",
"Jakob Voss "
]
}
decoder.t 100644 001750 001750 7660 12475316027 13543 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t use strict;
use warnings;
use Test::More;
use RDF::aREF::Decoder;
my $_rdf = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
my $_foaf = "http://xmlns.com/foaf/0.1/";
my $_xsd = 'http://www.w3.org/2001/XMLSchema#';
my $_ex = "http://example.org/";
my $alice = "http://example.org/alice";
sub decode {
my @triples;
my ($aref, %options) = ref $_[0] eq 'HASH' ? ($_[0]) : (@{$_[0]});
RDF::aREF::Decoder->new(
callback => sub {
push @triples, join " ", map {
(ref $_ ? '?'.$$_ : $_) // ''
} @_;
}, %options
)->decode( $aref );
join "\n", sort @triples;
}
sub test_decode(@) { ## no critic
local $Test::Builder::Level = $Test::Builder::Level + 1;
is decode($_[0]), $_[1];
}
# many ways to encode the same simple triple
test_decode $_, "$alice ${_rdf}type ${_foaf}Person" for
# predicate map
{ _id => $alice, a => "foaf_Person" },
{ _id => $alice, a => "${_foaf}Person" },
{ _id => $alice, a => "<${_foaf}Person>" },
{ _id => $alice, rdf_type => "foaf_Person" },
{ _id => $alice, "${_rdf}type" => "foaf_Person" },
{ _id => $alice, "<${_rdf}type>" => "foaf_Person" },
# subject map
{ $alice => { a => "foaf_Person" } },
{ $alice => { a => "${_foaf}Person" } },
{ $alice => { a => "<${_foaf}Person>" } },
{ $alice => { rdf_type => "foaf_Person" } },
{ $alice => { "${_rdf}type" => "foaf_Person" } },
{ $alice => { "<${_rdf}type>" => "foaf_Person" } },
{ _ns => { x => $_ex }, x_alice => { a => "foaf_Person" } },
;
# simple literals
test_decode $_, "$alice ${_foaf}name Alice " for
{ $alice => { foaf_name => "Alice" } },
{ $alice => { foaf_name => "Alice@" } },
{ $alice => { foaf_name => "Alice^<${_xsd}string>" } },
{ $alice => { foaf_name => "Alice^xsd_string" } },
;
# datatypes
test_decode $_, "$alice ${_foaf}age 42 ${_xsd}integer" for
{ $alice => { foaf_age => "42^xsd_integer" } },
{ $alice => { foaf_age => "42^<${_xsd}integer>" } },
;
# language tags
test_decode { $alice => { foaf_name => "Alice\@$_" } },
"$alice ${_foaf}name Alice ".lc($_) for qw(en en-US abcdefgh-x-12345678);
# blank nodes
test_decode $_, "$alice ${_foaf}knows _:b1" for
{ $alice => { foaf_knows => { } } },
{ $alice => { foaf_knows => { _id => '_:b1' } } },
;
test_decode [ $_, bnode_prefix => 'x' ], "$alice ${_foaf}knows _:x1" for
{ $alice => { foaf_knows => { } } },
{ $alice => { foaf_knows => { _id => '_:b1' } } },
;
test_decode $_, "_:b1 ${_rdf}type ${_foaf}Person\n$alice ${_foaf}knows _:b1" for
# { $alice => { foaf_knows => { a => 'foaf_Person' } } },
# { $alice => { foaf_knows => { _id => '_:b1', a => 'foaf_Person' } } },
{ $alice => { foaf_knows => '_:b1' }, '_:b1' => { a => 'foaf_Person' } },
;
# TODO: more blank nodes
=cut
# valid
decode_aref { '' => { a => undef } }, complain => 2;
decode_aref { '' => { a => 'foaf_Person' } }, complain => 2, null => '';
ok !$error, 'not strict by default';
my $rdf = decode_aref { '' => { a => '' } }, complain => 2, null => '';
ok !$rdf, 'empty string as null';
decode_aref { '' => { a => '' } }, %handler, strict => 1;
ok !$error && $rdf, 'empty string not null by default';
=cut
=cut
my @looks_like_error = (
{ '0' => { a => 'foaf_Person' }, _ns => 'x:' },
{ _id => '0', a => 'foaf_Person', _ns => 'x:' },
);
my $rdf;
my $decoder = RDF::aREF::Decoder->new( callback => sub { $rdf++ } );
foreach (@looks_like_error) {
$decoder->decode($_);
ok $rdf, 'triple';
$rdf = 0;
}
=cut
note '->plain_literal';
my %tests = (
"a@" => "a",
"0^" => "0",
"http://example.org/" => undef,
"http://example.org/@" => 'http://example.org/',
"" => undef,
"<>" => "<>", # (sic!)
);
use RDF::aREF::Decoder;
my $decoder = RDF::aREF::Decoder->new;
while (my ($aref, $literal) = each %tests) {
my $got = $decoder->plain_literal($aref);
is $got, $literal, $aref;
}
done_testing;
encoder.t 100644 001750 001750 7277 12475316027 13561 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t use strict;
use warnings;
use Test::More;
use RDF::aREF::Encoder;
sub test_encoder(@) {
my ($encoder, $method, @tests) = @_;
note "RDF::aREF::Encoder::$method";
while (@tests) {
my $input = shift @tests;
my $expect = shift @tests;
local $Test::Builder::Level = $Test::Builder::Level + 1;
if ( ref $expect ) {
is_deeply $encoder->$method($input), $expect, $expect;
} else {
is $encoder->$method($input), $expect, $expect;
}
}
}
my $encoder = RDF::aREF::Encoder->new( ns => '20140910' );
test_encoder $encoder => 'subject',
# RDF/JSON
{
type => 'uri',
value => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'
} => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type',
{
type => 'bnode',
value => '_:foo'
} => '_:foo',
# RDF::Trine
['URI','http://www.w3.org/1999/02/22-rdf-syntax-ns#type']
=> 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type',
['BLANK', 0 ] => '_:0',
# else
'http://example.org/' => undef,
;
test_encoder $encoder => 'predicate',
# RDF/JSON
{
type => 'uri',
value => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'
} => 'a',
{
type => 'uri',
value => 'http://purl.org/dc/terms/title'
} => 'dct_title',
# RDF::Trine
['URI','http://www.w3.org/1999/02/22-rdf-syntax-ns#type'] => 'a',
['URI','http://undefinednamespace.foo'] => 'http://undefinednamespace.foo',
['BLANK', 0 ] => undef,
# else
'http://example.org/' => undef,
;
test_encoder $encoder => 'object',
# RDF/JSON
{
type => 'uri',
value => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'
} => 'rdf_type',
{
type => 'uri',
value => 'http://www.w3.org/2006/vcard/ns#street-address',
} => '',
{
type => 'literal',
value => 'hello, world!',
lang => 'en'
} => 'hello, world!@en',
{
type => 'literal',
value => '12',
datatype => 'http://www.w3.org/2001/XMLSchema#integer'
} => '12^xs_integer',
{
type => 'bnode',
value => '_:12',
} => '_:12',
# RDF::Trine
['URI','http://www.w3.org/1999/02/22-rdf-syntax-ns#type'] => 'rdf_type',
['URI','http://www.w3.org/1999/02/22-rdf-syntax-ns#type'] => 'rdf_type',
['BLANK', 0 ] => '_:0',
['hello, world!', 'en', undef ] => 'hello, world!@en',
['hello, world!' ] => 'hello, world!@',
[42, undef, 'http://www.w3.org/2001/XMLSchema#integer'] => '42^xs_integer',
# else
'http://example.org/' => undef,
;
# RDF::aREF::Encoder::uri( ... )
test_encoder $encoder => 'uri',
'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' => 'rdf_type',
'http://undefinednamespace.foo' => ''
;
# RDF::aREF::Encoder::literal( ... )
test_encoder $encoder => 'literal',
'' => '@'
;
# RDF::aREF::Encoder::bnode( ... )
test_encoder $encoder => 'bnode',
abc => '_:abc',
0 => '_:0',
'_:0' => undef,
;
# RDF::aREF::Encoder::qname( ... )
test_encoder $encoder => 'qname',
'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' => 'rdf_type',
'http://schema.org/Review' => 'schema_Review',
'http://www.w3.org/2006/vcard/ns#street-address' => undef,
;
# encoder methods with ns => 0
$encoder = RDF::aREF::Encoder->new( ns => 0 );
is $encoder->literal( 42, undef, 'http://www.w3.org/2001/XMLSchema#integer'),
'42^xsd_integer', 'literal (ns=0)';
is $encoder->qname('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
'rdf_type', 'qname (ns=0)';
is $encoder->predicate('http://purl.org/dc/terms/title'),
undef, 'predicate (ns=0)';
done_testing;
objects.t 100644 001750 001750 2327 12475316027 13562 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t use strict;
use warnings;
use Test::More;
use RDF::aREF qw(decode_aref);
my @tests = (
'@' => [ '', undef ],
'' => [ '', undef ],
'^xsd_string' => [ '', undef ],
'^' => [ '', undef ],
'@^xsd_string' => [ '@', undef ],
'@@' => [ '@', undef ],
'alice@' => [ 'alice', undef ],
'alice@en' => [ 'alice', 'en' ],
'alice@example.com' => [ 'alice@example.com', undef ],
'123' => [ '123', undef ],
'123^xsd_integer' => [ '123', undef, "http://www.w3.org/2001/XMLSchema#integer" ],
'123^' => [ '123', undef, "xsd:integer" ],
'忍者@ja' => [ '忍者', 'ja' ],
'Ninja@en@' => [ 'Ninja@en', undef ],
'rdf_type' => [ 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' ],
'' => [ 'rdf:type' ],
'geo:48.2010,16.3695,183' => [ 'geo:48.2010,16.3695,183' ],
'geo_Point' => [ 'http://www.w3.org/2003/01/geo/wgs84_pos#Point' ],
);
while (defined (my $input = shift @tests)) {
my ($expect, $object, $error) = shift @tests;
decode_aref
{ 'x:subject' => { '' => $input } },
callback => sub { shift; shift; $object = \@_; };
is_deeply $object, $expect, "\"$input\"";
}
done_testing;
aref-query.t 100644 001750 001750 7610 12475316027 14211 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t use strict;
use warnings;
use Test::More;
use RDF::aREF qw(aref_query aref_query_map);
use RDF::aREF::Query;
use Scalar::Util qw(reftype);
BEGIN {
eval { require JSON; 1; }
or plan skip_all => "test requires JSON";
}
my $rdf = JSON::from_json(do { local (@ARGV, $/) = "t/doi-example.json"; <> });
my $uri = "http://dx.doi.org/10.2474/trol.7.147";
my @res = aref_query($rdf, $uri, '.');
is reftype $res[0], 'HASH';
# FIXME:
# is_deeply [ aref_query($rdf, $uri, '@') ], [ ], 'empty query (@)';
is_deeply [ aref_query($rdf, $uri, '') ], [ $uri ], 'empty query';
is_deeply [ aref_query($rdf, $uri, 'dct_title') ],
['Frictional Coefficient under Banana Skin'], 'dct_title';
is_deeply [ aref_query($rdf, $uri, 'dct_title@') ],
['Frictional Coefficient under Banana Skin'], 'dct_title@';
is_deeply [ aref_query($rdf, $uri, 'dct_title^xsd_string') ],
['Frictional Coefficient under Banana Skin'], 'dct_title^xsd_string';
is_deeply [ aref_query($rdf, $uri, 'dct_title@en') ],
[ ], 'dct_title@';
is_deeply [ sort(aref_query($rdf->{$uri}, 'dct_publisher')) ], [
'Japanese Society of Tribologists',
'http://d-nb.info/gnd/5027072-2',
], 'dct_publisher';
is_deeply [ aref_query($rdf->{$uri}, 'dct_publisher.') ], [
'http://d-nb.info/gnd/5027072-2',
], 'dct_publisher.';
is_deeply [ aref_query($rdf->{$uri}, 'dct_date') ], ["2012"], 'dct_date';
is_deeply [ aref_query($rdf->{$uri}, 'dct_date^xsd_gYear') ], ["2012"], 'dct_date^xsd_gYear';
is_deeply [ aref_query($rdf->{$uri}, 'dct_date^xsd_foo') ], [], 'dct_date^xsd_foo';
is_deeply [ aref_query($rdf, $uri, 'dct_creator.a') ],
[ map { 'http://xmlns.com/foaf/0.1/Person' } 1..4 ], 'a is a valid property';
foreach my $query (qw(dct_creator dct_creator.)) {
is_deeply [ sort (aref_query($rdf, $uri, $query)) ], [
"http://id.crossref.org/contributor/daichi-uchijima-y2ol1uygjx72",
"http://id.crossref.org/contributor/kensei-tanaka-y2ol1uygjx72",
"http://id.crossref.org/contributor/kiyoshi-mabuchi-y2ol1uygjx72",
"http://id.crossref.org/contributor/rina-sakai-y2ol1uygjx72",
], $query;
}
is join(' ',sort(aref_query($rdf,$uri,'dct_creator.foaf_familyName'))),
"Mabuchi Sakai Tanaka Uchijima", 'dct_creator.foaf_familyName';
my %names = (
'dct_creator.foaf_name' => 4,
'dct_creator.foaf_name@' => 4,
'dct_creator.foaf_name@en' => 4,
'dct_creator.foaf_name@ja' => 0,
);
while ( my ($query, $count) = each %names ) {
my @names = aref_query( $rdf, $uri, $query );
is scalar @names, $count, $query;
}
is scalar @{[ aref_query( $rdf, $uri,
qw(dct_creator. schema_author. dct_publisher.)) ]}, 5, 'multiple queries';
is_deeply [
aref_query( $rdf, $uri, 'bibo_pageStart|bibo_unknown|bibo_pageEnd' )
], [qw(147 151)], 'multiple items';
foreach my $query ( "dct_title@#", "dct_date^_" ) {
eval { RDF::Query->new($query) };
ok $@, 'error in aREF query';
}
{
my $rdf = {
'http://example.org/book' => {
dct_creator => [
'http://example.org/alice',
"Bob"
]
},
'http://example.org/alice' => {
foaf_name => "Alice"
},
};
my $uri = 'http://example.org/book';
is_deeply [ sort(aref_query($rdf, $uri, 'dct_creator')) ],
[qw(Bob http://example.org/alice)], 'literal and URI';
my $record = aref_query_map( $rdf, $uri, {
'dct_creator@' => 'creator',
'dct_creator.foaf_name' => 'creator',
});
is_deeply [ sort @{$record->{creator}} ], [qw(Alice Bob)], 'aref_query_map';
}
{
my $url = "http://example.org/";
my $aref = {
dct_title => "Hello@",
_id => $url,
};
is_deeply [ aref_query($aref, $url, 'dct_title@de','dct_title@') ],
["Hello"], "query from property map";
is_deeply [ aref_query($aref, undef, 'dct_title@de','dct_title@') ],
["Hello"], "query from property map";
}
done_testing;
example.json 100644 001750 001750 754 12475316027 14254 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t {
"_ns": {
"dct": "http://purl.org/dc/terms/",
"foaf": "http://xmlns.com/foaf/0.1/"
},
"_id": "http://example.com/people#alice",
"a": "foaf:Person",
"foaf_name": "Alice Smisth",
"foaf_age": "42^xsd_integer",
"foaf_homepage": [
"http://personal.example.org/~alice/",
"http://work.example.com/asmith/"
],
"foaf_knows": {
"_id": "_:1",
"foaf_name": "John",
"dct_description": "a nice guy@en"
}
}
RDF 000755 001750 001750 0 12475316027 12516 5 ustar 00voj voj 000000 000000 RDF-aREF-0.25/lib aREF.pm 100644 001750 001750 14551 12475316027 14017 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/lib/RDF package RDF::aREF;
use strict;
use warnings;
use v5.10;
our $VERSION = '0.25';
use RDF::aREF::Query;
use RDF::aREF::Decoder;
use RDF::aREF::Encoder;
use Scalar::Util qw(blessed reftype);
use Carp qw(croak);
use parent 'Exporter';
our @EXPORT = qw(decode_aref encode_aref aref_query aref_query_map);
our %EXPORT_TAGS = (all => [@EXPORT]);
our @CARP_NOT = qw(RDF::aREF::Query RDF::aREF::Decoder RDF::aREF::Encoder);
sub decode_aref(@) { ## no critic
my ($aref, %options) = @_;
RDF::aREF::Decoder->new(%options)->decode($aref);
}
sub encode_aref(@) { ## no critic
my ($source, %options) = @_;
my $encoder = RDF::aREF::Encoder->new(%options);
my $aref = $options{to} // {};
if (blessed $source and $source->isa('RDF::Trine::Iterator')) {
$encoder->add_iterator( $source, $aref );
} elsif (blessed $source and $source->DOES('Attean::API::TripleIterator')) {
$encoder->add_iterator( $source, $aref );
} elsif (blessed $source and $source->isa('RDF::Trine::Model')) {
$encoder->add_iterator( $source->as_stream, $aref );
} elsif (blessed $source and $source->DOES('Attean::API::TripleStore')) {
$encoder->add_iterator( $source->get_triples, $aref );
} elsif (ref $source and reftype $source eq 'HASH') {
$encoder->add_hashref( $source, $aref );
} elsif (!ref $source) {
eval { require RDF::Trine::Model; require RDF::Trine::Parser };
croak "RDF::Trine missing: encoding aREF from URL or file not supported!" if $@;
my $model = RDF::Trine::Model->new;
# TODO: directly use iterator
if ($source =~ qr{^https?://}) {
RDF::Trine::Parser->parse_url_into_model($source, $model);
} elsif (-f $source) {
my $parser = RDF::Trine::Parser->guess_parser_by_filename($source);
$parser->parse_file_into_model("file://$source", $source, $model)
} else {
croak 'invalid RDF graph, given as string';
}
$encoder->add_iterator( $model->as_stream, $aref );
}
return $aref;
}
sub aref_query(@) { ## no critic
my ($graph, $origin, @queries) = @_ < 3 ? ($_[0], undef, $_[1]) : @_;
RDF::aREF::Query->new( query => join '|', @queries )->apply($graph, $origin);
}
sub aref_query_map(@) { ## no critic
my ($graph, $origin, $map) = @_ < 3 ? ($_[0], undef, $_[1]) : @_;
my %record;
while (my ($query, $field) = each %$map) {
my @values = aref_query( $origin ? ($graph, $origin, $query)
: ($graph, $query) );
if (@values) {
if ($record{$field}) {
if (ref $record{$field}) {
push @{$record{$field}}, @values;
} else {
$record{$field} = [ $record{$field}, @values ];
}
} else {
$record{$field} = @values > 1 ? \@values : $values[0];
}
}
}
\%record;
}
1;
__END__
=head1 NAME
RDF::aREF - Another RDF Encoding Form
=begin markdown
# STATUS
[](https://travis-ci.org/nichtich/RDF-aREF)
[](https://coveralls.io/r/nichtich/RDF-aREF)
[](http://cpants.cpanauthors.org/dist/RDF-aREF)
=end markdown
=head1 SYNOPSIS
use RDF::aREF;
my $rdf = {
_id => 'http://example.com/people#alice',
foaf_name => 'Alice Smith',
foaf_age => '42^xsd_integer',
foaf_homepage => [
{
_id => 'http://personal.example.org/',
dct_modified => '2010-05-29^xsd_date',
},
'http://work.example.com/asmith/',
],
foaf_knows => {
dct_description => 'a nice guy@en',
},
};
decode_aref( $rdf,
callback => sub {
my ($subject, $predicate, $object, $language, $datatype) = @_;
...
}
);
my @lastmod = aref_query $rdf, 'foaf_homepage.dct_modified^';
my $model = RDF::Trine::Model->new;
decode_aref( $rdf, callback => $model );
print RDF::Trine::Serializer->new('Turtle')->serialize_model_to_string($model);
my $model = RDF::Trine::Model->new;
RDF::Trine::Parser->parse_url_into_model($url, $model);
my $aref = encode_aref $model;
=head1 DESCRIPTION
B (L) is an
encoding of RDF graphs in form of arrays, hashes, and Unicode strings. This
module provides methods for decoding from aREF data to RDF triples
(L), for encoding RDF data in aREF (L),
and for querying parts of an RDF graph (L).
=head1 EXPORTED FUNCTIONS
The following functions are exported by default.
=head2 decode_aref $aref [, %options ]
Decodes an aREF document given as hash reference with L.
Equivalent to C<< RDF::aREF::Decoder->new(%options)->decode($aref) >>.
=head2 encode_aref $graph [, %options ]
Construct an aREF subject mapfrom an RDF graph. The L for
possible options. The C<$graph> can be supplied as:
=over
=item
instance of L
=item
instance of L
=item
an URL or a filename (only if L is installed)
=item
instance of L (experimental)
=item
instance of L (experimental)
=item
hash reference with L format (as
returned by method C in L)
=back
=head2 aref_query $graph, [ $origin ], @queries
Query parts of an aREF data structure by L and return a list.
See L for details.
=head2 aref_query_map( $graph, [ $origin ], $query_map )
Map parts of an aREF data structure to a flat key-value structure.
=head1 SEE ALSO
=over
=item
aREF is specified at L.
=item
See L for an application of this module.
=item
Usee L for more elaborated handling of RDF data in Perl.
=item
See L for a similar (outdated) RDF encoding in YAML.
=back
=head1 COPYRIGHT AND LICENSE
Copyright Jakob Voss, 2014-
This library is free software; you can redistribute it and/or modify it under
the same terms as Perl itself.
=cut
encode_aref.t 100644 001750 001750 2425 12475316027 14362 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t use strict;
use warnings;
use Test::More;
use RDF::aREF;
use RDF::aREF::Encoder;
my $example = { # RDF/JSON
"http://example.org/about" => {
"http://purl.org/dc/terms/title" => [
{ value => "Anna's Homepage",
type => "literal",
lang => "en" } ]
}
};
my $aref = encode_aref($example, ns => '20140910');
is_deeply $aref, {
_id => "http://example.org/about",
dct_title => "Anna's Homepage\@en"
}, 'add_hashref';
encode_aref( {
"http://example.org/about" => {
"x:y" => [{ "type" => "uri", value => "x:z" }]
} },
subject_map => 1,
to => $aref
);
is_deeply $aref, {
'http://example.org/about' => {
dct_title => "Anna's Homepage\@en",
"x:y" => "",
} }, 'encode_aref( $rdfjson, subject_map => 1, to => $aref )';
# TODO: normalize/avoid duplicate triples
is_deeply encode_aref($example, ns => 0), {
_id => "http://example.org/about",
"http://purl.org/dc/terms/title" => "Anna's Homepage\@en"
}, 'encode_aref( $rdfjson, ns => 0)';
is_deeply encode_aref($example, ns => 0, subject_map => 1), {
"http://example.org/about" => {
"http://purl.org/dc/terms/title" => "Anna's Homepage\@en"
}
}, 'encode_aref( $rdfjson, ns => 0, subject_map => 1)';
done_testing;
decode-errors.t 100644 001750 001750 3555 12475316027 14672 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t use strict;
use warnings;
use Test::More;
use RDF::aREF::Decoder;
use Scalar::Util qw(reftype);
sub check_errors(@) {
my $errors = pop;
my %options = @_;
my $msg = delete $options{msg};
my $decoder = RDF::aREF::Decoder->new( complain => 2, %options );
local $Test::Builder::Level = $Test::Builder::Level + 1;
while (@$errors) {
my $aref = shift @$errors;
eval { $decoder->decode( $aref ) };
if (@$errors and reftype $errors->[0] eq reftype qr//) {
my $expect = shift @$errors;
like($@, $expect, ($msg // $expect));
} else {
ok $@, $msg // '';
}
}
}
check_errors [
{ _ns => [] },
=> qr{^namespace map must be map or string},
# invalid subjects
{ [] => { a => 'foaf:Person' } }
=> qr{^invalid subject: ARRAY\(},
{ '' => { a => 'foaf:Person' } }
=> qr{^invalid subject},
# invalid predicates
{ 'x:subject' => { \"" => "" } }
=> qr{^invalid predicate IRI SCALAR\(},
# TODO: check different forms of same IRI
# invalid objects
{ 'x:subject' => { a => \"" } }
=> qr{^object must not be reference to SCALAR},
{ 'x:subject' => { a => [ \"" ] } }
=> qr{^object must not be reference to SCALAR},
{ _ns => { 1 => 'http://example.org/' },
'x:subject' => { a => 'foaf:Person' } }
=> qr{^invalid prefix: 1},
{ _ns => { x => 'foo' },
'x:subject' => { a => 'foaf:Person' } }
=> qr{^invalid namespace: foo}
];
check_errors strict => 1, msg => 'strict makes undef error',
[ { '' => { a => undef } } => qr{.} ];
check_errors strict => 1, null => '', msg => 'strict makes null value error',
[ { '' => { a => 'foaf_Person' } } ];
check_errors strict => 1, msg => 'empty string not null by default',
[ { '' => { a => 'foaf_Person' } } ];
done_testing;
suite 000755 001750 001750 0 12475316027 12731 5 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t README.md 100644 001750 001750 464 12475316027 14334 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t/suite This directory contains a test suite with test cases for aREF decoding. Each
example consists of an aREF document in JSON (with extension `.json`) and an
expected result, as RDF graph in form of canonical N-Triples (with extension
`.nt`) and/or sorted list of expected error message (with extension `.err`).
blanks.nt 100644 001750 001750 76 12475316027 14651 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t/suite _:g2 _:g1 .
doi-example.json 100644 001750 001750 5437 12475316027 15050 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t {
"http://id.crossref.org/contributor/kiyoshi-mabuchi-y2ol1uygjx72" : {
"foaf_name" : [
"Kiyoshi Mabuchi@en"
],
"a" : [
"foaf_Person"
],
"foaf_familyName" : [
"Mabuchi@"
],
"foaf_givenName" : [
"Kiyoshi@"
]
},
"http://id.crossref.org/contributor/daichi-uchijima-y2ol1uygjx72" : {
"foaf_givenName" : [
"Daichi@"
],
"foaf_familyName" : [
"Uchijima@"
],
"a" : [
"foaf_Person"
],
"foaf_name" : [
"Daichi Uchijima@en"
]
},
"http://dx.doi.org/10.2474/trol.7.147" : {
"prism21_volume" : [
"7@"
],
"owl_sameAs" : [
"doi:10.2474/trol.7.147",
"info:doi/10.2474/trol.7.147"
],
"prism21_doi" : [
"10.2474/trol.7.147@"
],
"bibo_doi" : [
"10.2474/trol.7.147@"
],
"dct_date" : [
"2012^xsd_gYear"
],
"prism21_startingPage" : [
"147@"
],
"bibo_pageStart" : [
"147@"
],
"bibo_volume" : [
"7@"
],
"dct_title" : [
"Frictional Coefficient under Banana Skin@"
],
"dct_creator" : [
"",
"",
"http://id.crossref.org/contributor/kiyoshi-mabuchi-y2ol1uygjx72",
"http://id.crossref.org/contributor/rina-sakai-y2ol1uygjx72"
],
"dct_publisher" : [
"Japanese Society of Tribologists@",
"http://d-nb.info/gnd/5027072-2"
],
"dct_isPartOf" : [
""
],
"prism21_endingPage" : [
"151@"
],
"bibo_pageEnd" : [
"151@"
],
"dct_identifier" : [
"10.2474/trol.7.147@"
]
},
"http://id.crossref.org/contributor/rina-sakai-y2ol1uygjx72" : {
"foaf_familyName" : [
"Sakai@"
],
"foaf_givenName" : [
"Rina@"
],
"foaf_name" : [
"Rina Sakai@en"
],
"a" : [
"foaf_Person"
]
},
"http://id.crossref.org/contributor/kensei-tanaka-y2ol1uygjx72" : {
"a" : [
"foaf_Person"
],
"foaf_name" : [
"Kensei Tanaka@en"
],
"foaf_givenName" : [
"Kensei@"
],
"foaf_familyName" : [
"Tanaka@"
]
},
"http://id.crossref.org/issn/1881-2198" : {
"bibo_issn" : [
"1881-2198@"
],
"a" : [
"bibo_Journal"
],
"prism21_issn" : [
"1881-2198@"
],
"dct_title" : [
"Tribology Online@"
],
"owl_sameAs" : [
"urn:issn:1881-2198"
]
}
}
blanks.json 100644 001750 001750 25 12475316027 15173 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t/suite {"_:x":{"a":"_:y"}}
aREF 000755 001750 001750 0 12475316027 13273 5 ustar 00voj voj 000000 000000 RDF-aREF-0.25/lib/RDF Query.pm 100644 001750 001750 14047 12475316027 15124 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/lib/RDF/aREF package RDF::aREF::Query;
use strict;
use warnings;
use v5.10;
our $VERSION = '0.25';
use RDF::aREF::Decoder qw(qName languageTag);
use Carp qw(croak);
use RDF::NS;
sub new {
my ($class, %options) = @_;
my $expression = $options{query} // croak "query required";
my $ns = $options{ns} // RDF::NS->new;
my $decoder = $options{decoder} // RDF::aREF::Decoder->new( ns => $ns );
my $self = bless {
items => [],
decoder => $decoder
}, $class;
my @items = split /\s*\|\s*/, $expression;
foreach my $expr ( @items ? @items : '' ) {
my $type = 'any';
my ($language, $datatype);
if ($expr =~ /^(.*)\.$/) {
$type = 'resource';
$expr = $1;
} elsif ( $expr =~ /^([^@]*)@([^@]*)$/ ) {
($expr, $language) = ($1, $2);
if ( $language eq '' or $language =~ languageTag ) {
$type = 'literal';
} else {
croak 'invalid languageTag in aREF query';
}
} elsif ( $expr =~ /^([^^]*)\^([^^]*)$/ ) { # TODO: support explicit IRI
($expr, $datatype) = ($1, $2);
if ( $datatype =~ qName ) {
$type = 'literal';
$datatype = $decoder->prefixed_name( split '_', $datatype );
$datatype = undef if $datatype eq $decoder->prefixed_name('xsd','string');
} else {
croak 'invalid datatype qName in aREF query';
}
}
my @path = split /\./, $expr;
foreach (@path) {
croak "invalid aref path expression: $_" if $_ !~ qName and $_ ne 'a';
}
push @{$self->{items}}, {
path => \@path,
type => $type,
language => $language,
datatype => $datatype,
};
}
$self;
}
sub query {
my ($self) = @_;
join '|', map {
my $q = join '.', @{$_->{path}};
if ($_->{type} eq 'literal') {
if ($_->{datatype}) {
$q .= '^' . $_->{datatype};
} else {
$q .= '@' . ($_->{language} // '');
}
} elsif ($_->{type} eq 'resource') {
$q .= '.';
}
$q;
} @{$self->{items}}
}
sub apply {
my ($self, $rdf, $subject) = @_;
map { $self->_apply_item($_, $rdf, $subject) } @{$self->{items}};
}
sub _apply_item {
my ($self, $item, $rdf, $subject) = @_;
my $decoder = $self->{decoder};
# TODO: Support RDF::Trine::Model
# TODO: try abbreviated *and* full URI?
my @current = $rdf;
if ($subject) {
if ($rdf->{_id}) {
return if $rdf->{_id} ne $subject;
} else {
@current = ($rdf->{$subject});
}
}
my @path = @{$item->{path}};
if (!@path and $item->{type} ne 'resource') {
if ($item->{type} eq 'any') {
return ($subject ? $subject : $rdf->{_id});
}
}
while (my $field = shift @path) {
# get objects in aREF
@current = grep { defined }
map { (ref $_ and ref $_ eq 'ARRAY') ? @$_ : $_ }
map { $_->{$field} } @current;
return if !@current;
if (@path or $item->{type} eq 'resource') {
# get resources
@current = grep { defined }
map { $decoder->resource($_) } @current;
if (@path) {
# TODO: only if RDF given as predicate map!
@current = grep { defined } map { $rdf->{$_} } @current;
}
}
}
# last path element
@current = grep { defined } map { $decoder->object($_) } @current;
if ($item->{type} eq 'literal') {
@current = grep { @$_ > 1 } @current;
if ($item->{language}) { # TODO: use language tag substring
@current = grep { $_->[1] and $_->[1] eq $item->{language} } @current;
} elsif ($item->{datatype}) { # TODO: support qName and explicit IRI
@current = grep { $_->[2] and $_->[2] eq $item->{datatype} } @current;
}
}
map { $_->[0] } @current; # IRI or string value
}
1;
__END__
=head1 NAME
RDF::aREF::Query - aREF query expression
=head1 SYNOPSIS
my $rdf = {
'http://example.org/book' => {
dct_creator => [
'http://example.org/alice',
'http://example.org/bob'
]
},
'http://example.org/alice' => {
foaf_name => "Alice"
},
'http://example.org/bob' => {
foaf_name => "Bob"
}
};
my $getnames = RDF::aREF::Query->new(
query => 'dct_creator.foaf_name'
);
my @names = $getnames->apply( $rdf, 'http://example.org/boo' );
$getnames->query; # 'dct_creator.foaf_name'
use RDF::aREF qw(aref_query_map);
my $record = aref_query_map( $rdf, $publication, {
'dct_creator@' => 'creator',
'dct_creator.foaf_name' => 'creator',
});
=head1 DESCRIPTION
Implements L, a
query language to access strings and nodes from agiven RDF graph.
See also functions C and C in L for
convenient application.
=head1 CONFIGURATION
The constructor expects the following options:
=over
=item query
L expression
=item decoder
Instance of L to map qNames to URIs. A new instance is
created unless given.
=item ns
Optional namespace map (L), passed to the constructor of
L if no decoder is given.
=back
=head1 METHODS
=head1 apply( $graph [, $origin ] )
Perform the query on a given RDF graph. The graph can be given as aREF
structure (subject map or predicate map) or as instance of
L. An origin subject node must be provided unless the RDF
graph is provided as L.
=head1 query
Returns the aREF query expression
=head1 SEE ALSO
Use SPARQL for more complex queries, e.g. with L.
=cut
json-ld-14.nt 100644 001750 001750 347 12475316027 15210 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t/suite .
.
json-ld-23.nt 100644 001750 001750 437 12475316027 15210 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t/suite "2010-05-29T14:17:39+02:00"^^ .
.
release-pod-syntax.t 100644 001750 001750 456 12475316027 15636 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/t #!perl
BEGIN {
unless ($ENV{RELEASE_TESTING}) {
require Test::More;
Test::More::plan(skip_all => 'these tests are for release candidate testing');
}
}
# This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests.
use Test::More;
use Test::Pod 1.41;
all_pod_files_ok();
Decoder.pm 100644 001750 001750 33446 12475316027 15370 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/lib/RDF/aREF package RDF::aREF::Decoder;
use strict;
use warnings;
use v5.10;
our $VERSION = '0.25';
use RDF::NS;
use Carp qw(croak carp);
use Scalar::Util qw(refaddr reftype blessed);
use parent 'Exporter';
our @EXPORT_OK = qw(prefix localName qName blankNode blankNodeIdentifier
IRIlike languageString languageTag datatypeString);
our ($PREFIX, $NAME);
BEGIN {
my $nameChar = 'A-Z_a-z\N{U+00C0}-\N{U+00D6}\N{U+00D8}-\N{U+00F6}\N{U+00F8}-\N{U+02FF}\N{U+0370}-\N{U+037D}\N{U+037F}-\N{U+1FFF}\N{U+200C}-\N{U+200D}\N{U+2070}-\N{U+218F}\N{U+2C00}-\N{U+2FEF}\N{U+3001}-\N{U+D7FF}\N{U+F900}-\N{U+FDCF}\N{U+FDF0}-\N{U+FFFD}\N{U+10000}-\N{U+EFFFF}';
my $nameStartChar = $nameChar.'0-9\N{U+00B7}\N{U+0300}\N{U+036F}\N{U+203F}-\N{U+2040}-';
our $PREFIX = '[a-z][a-z0-9]*';
our $NAME = "[$nameStartChar][$nameChar]*";
}
use constant localName => qr/^$NAME$/;
use constant prefix => qr/^$PREFIX$/;
use constant qName => qr/^($PREFIX)_($NAME)$/;
use constant blankNodeIdentifier => qr/^([a-zA-Z0-9]+)$/;
use constant blankNode => qr/^_:([a-zA-Z0-9]+)$/;
use constant IRIlike => qr/^[a-z][a-z0-9+.-]*:/;
use constant languageString => qr/^(.*)@([a-z]{2,8}(-[a-z0-9]{1,8})*)$/i;
use constant languageTag => qr/^[a-z]{2,8}(-[a-z0-9]{1,8})*$/i;
use constant datatypeString => qr/^(.*?)[\^]
((($PREFIX)?_($NAME))|<([a-z][a-z0-9+.-]*:.*)>)$/x;
use constant explicitIRIlike => qr/^<(.+)>$/;
use constant xsd_string => 'http://www.w3.org/2001/XMLSchema#string';
sub new {
my ($class, %options) = @_;
my $self = bless {
ns => $options{ns},
complain => $options{complain} // 1,
strict => $options{strict} // 0,
null => $options{null}, # undef by default
bnode_prefix => $options{bnode_prefix} || 'b',
bnode_count => $options{bnode_count} || 0,
bnode_map => { },
}, $class;
# facilitate use of this module together with RDF::Trine
my $callback = $options{callback} // sub { };
if (blessed $callback and $callback->isa('RDF::Trine::Model')) {
require RDF::Trine::Statement;
my $model = $callback;
$callback = sub {
eval {
$model->add_statement( trine_statement(@_) )
};
$self->error($@) if $@;
};
}
$self->{callback} = $callback;
return $self;
}
sub namespace_map { # sets the local namespace map
my ($self, $map) = @_;
# TODO: copy on write because this is expensive!
# copy default namespace map
# TODO: respect '_' and default map!
my $ns = ref $self->{ns}
? bless { %{$self->{ns}} }, 'RDF::NS'
: RDF::NS->new($self->{ns});
if (ref $map) {
if (ref $map eq 'HASH') {
while (my ($prefix,$namespace) = each %$map) {
$prefix = '' if $prefix eq '_';
if ($prefix !~ prefix) {
$self->error("invalid prefix: $prefix");
} elsif ($namespace !~ IRIlike) {
$self->error("invalid namespace: $namespace");
} else {
$ns->{$prefix} = $namespace;
}
}
} else {
$self->error("namespace map must be map or string");
}
}
$self->{ns} = $ns;
}
sub decode {
my ($self, $map, %options) = @_;
unless ($options{keep_bnode_map}) {
$self->{bnode_map} = { };
}
$self->{visited} = { };
$self->namespace_map( $map->{"_ns"} );
if (exists $map->{_id}) {
# predicate map
my $id = $map->{_id};
return if $self->is_null($id,'_id');
my $subject = $id ne '' ? $self->expect_resource($id,'subject') : undef;
if (defined $subject and $subject ne '') {
$self->predicate_map( $subject, $map );
} elsif ($self->{strict}) {
$self->error("invalid subject", $id);
}
} else {
# 3.4.1 subject maps
foreach my $key (grep { $_ ne '_' and $_ !~ /^_[^:]/ } keys %$map) {
next if $self->is_null($key,'subject');
my $subject = $self->subject($key);
if (!$subject) {
$self->error("invalid subject", $key);
next;
}
my $predicates = $map->{$key};
if (exists $predicates->{_id} and ($self->resource($predicates->{_id}) // '') ne $subject) {
$self->error("subject _id must be <$subject>");
} else {
$self->predicate_map( $subject, $predicates );
}
}
}
}
sub predicate_map {
my ($self, $subject, $map) = @_;
$self->{visited}{refaddr $map} = 1;
for (keys %$map) {
next if $_ eq '_id' or $_ eq '_ns';
my $predicate = do {
if ($_ eq 'a') {
'http://www.w3.org/1999/02/22-rdf-syntax-ns#type';
} elsif ( $_ =~ /^<(.+)>$/ ) {
$self->iri($1);
} elsif ( $_ =~ qName ) {
$self->prefixed_name($1,$2);
} elsif ( $_ =~ IRIlike ) {
$self->iri($_);
} else {
$self->error("invalid predicate IRI $_")
if $_ ne '' or $self->{strict};
next;
}
} or next;
my $value = $map->{$_};
# encoded_object
foreach my $o (ref $value eq 'ARRAY' ? @$value : $value) {
if ($self->is_null($o,'object')) {
next;
} elsif (!ref $o) {
if (my $object = $self->object($o)) {
$self->triple( $subject, $predicate, $object );
}
next;
} elsif (ref $o eq 'HASH') {
my $object = exists $o->{_id}
? ($self->expect_resource($o->{_id},'object _id') // next)
: $self->blank_identifier();
$self->triple( $subject, $predicate, [$object] );
unless( ref $object and $self->{visited}{refaddr $object} ) {
$self->predicate_map( $object, $o );
}
} else {
$self->error('object must not be reference to '.ref $o);
}
}
}
}
sub is_null {
my ($self, $value, $check) = @_;
if ( !defined $value or (defined $self->{null} and $value eq $self->{null} ) ) {
if ($check and $self->{strict}) {
$self->error("$check must not be null")
}
1;
} else {
0;
}
}
sub expect_resource {
my ($self, $r, $expect) = @_;
if (my $resource = $self->resource($r)) {
return $resource;
} else {
if (!$self->is_null($r, $expect)) {
$expect .= ": " . (ref $r ? reftype $r : $r);
$self->error("invalid $expect");
}
return;
}
}
sub resource {
my ($self, $r) = @_;
return unless defined $r;
if ( $r =~ explicitIRIlike ) {
$self->iri($1);
} elsif ( $r =~ blankNode ) {
$self->blank_identifier($1);
} elsif ( $r =~ qName ) {
$self->prefixed_name($1,$2);
} elsif ( $r =~ IRIlike ) {
$self->iri($r);
} else {
undef
}
}
sub subject { # plain IRI, qName, or blank node
my ($self, $s) = @_;
return unless defined $s;
if ( $s =~ IRIlike ) {
$self->iri($s);
} elsif ( $s =~ qName ) {
$self->prefixed_name($1,$2);
} elsif ( $s =~ blankNode ) {
$self->blank_identifier($1);
} else {
undef
}
}
sub object {
my ($self, $o) = @_;
if (!defined $o) {
undef;
} elsif ( $o =~ explicitIRIlike ) {
[$self->iri($1)];
} elsif ( $o =~ blankNode ) {
[$self->blank_identifier($1)];
} elsif ( $o =~ qName ) {
[$self->prefixed_name($1,$2)];
} elsif ( $o =~ languageString ) {
[$1, lc($2)];
} elsif ( $o =~ /^(.*)[@]$/ ) {
[$1, undef];
} elsif ( $o =~ datatypeString ) {
if ($6) {
my $datatype = $self->iri($6) // return;
if ($datatype eq xsd_string) {
[$1,undef];
} else {
[$1,undef,$datatype];
}
} else {
my $datatype = $self->prefixed_name($4,$5) // return;
if ($datatype eq xsd_string) {
[$1,undef];
} else {
[$1,undef,$datatype];
}
}
} elsif ( $o =~ IRIlike ) {
[$self->iri($o)];
} else {
[$o, undef];
}
}
sub plain_literal {
my ($self, $object) = @_;
my $obj = $self->object($object);
return if @$obj == 1; # resource or blank
return $obj->[0];
}
sub iri {
my ($self, $iri) = @_;
# TODO: check full RFC form of IRIs
if ( $iri !~ IRIlike ) {
return $self->error("invalid IRI $iri");
} else {
return $iri;
}
}
sub prefixed_name {
my ($self, $prefix, $name) = @_;
my $base = $self->{ns}{$prefix // ''}
// return $self->error(
$prefix // '' ne ''
? "unknown prefix: $prefix" : "not an URI: $name");
$self->iri($base.$name);
}
sub triple {
my $self = shift;
my $subject = ref $_[0] ? '_:'.${$_[0]} : $_[0];
my $predicate = $_[1];
my @object = @{$_[2]};
$object[0] = '_:'.${$object[0]} if ref $object[0];
$self->{callback}->($subject, $predicate, @object);
}
sub error {
my ($self, $message, $value, $context) = @_;
# TODO: include $context (bless $message, 'RDF::aREF::Error')
if (defined $value) {
$message .= ': ' . (ref $value ? reftype $value : $value);
}
if (!$self->{complain}) {
return;
} elsif ($self->{complain} == 1) {
carp $message;
} else {
croak $message;
}
}
sub bnode_count {
$_[0]->{bnode_count} = $_[1] if @_ > 1;
$_[0]->{bnode_count};
}
# TODO: test this
sub blank_identifier {
my ($self, $id) = @_;
my $bnode;
if ( defined $id ) {
$bnode = ($self->{bnode_map}{$id} //= $self->{bnode_prefix} . ++$self->{bnode_count});
} else {
$bnode = $self->{bnode_prefix} . ++$self->{bnode_count};
}
return \$bnode;
}
sub clean_bnodes {
my ($self) = @_;
$self->{bnode_count} = 0;
$self->{bnode_map} = {};
}
# TODO: test this
sub trine_statement {
RDF::Trine::Statement->new(
# subject
(substr($_[0],0,2) eq '_:' ? RDF::Trine::Node::Blank->new(substr $_[0], 2)
: RDF::Trine::Node::Resource->new($_[0])),
# predicate
RDF::Trine::Node::Resource->new($_[1]),
# object
do {
if (@_ == 3) {
if (substr($_[2],0,2) eq '_:') {
RDF::Trine::Node::Blank->new(substr $_[2], 2);
} else {
RDF::Trine::Node::Resource->new($_[2]);
}
} else {
RDF::Trine::Node::Literal->new($_[2],$_[3],$_[4]);
}
}
);
}
1;
__END__
=head1 NAME
RDF::aREF::Decoder - decode another RDF Encoding Form (to RDF triples)
=head1 SYNOPSIS
use RDF::aREF::Decoder;
RDF::aREF::Decoder->new( %options )->decode( $aref );
=head1 DESCRIPTION
This module implements a decoder from another RDF Encoding Form (aREF), given
as in form of Perl arrays, hashes, and Unicode strings, to RDF triples.
=head1 OPTIONS
=head2 ns
A default namespace map, given either as hash reference or as version string of
module L. Set to the most recent version of RDF::NS by default, but relying
on a default value is not recommended!
=head2 callback
A code reference that is called for each triple with a list of three to five
elements:
=over
=item subject
The subject IRI or subject blank node as string. Blank nodes always start with
C<_:>.
=item predicate
The predicate IRI.
=item object
The object IRI or object blank node or literal object as string. Blank nodes
always start with C<_:> and literal objects can be detected by existence of the
(possibly empty) language or datatype element.
=item language
The language tag (possible the empty string) for literal objects.
=item datatype
The object's datatype if object is a literal and datatype is not
C.
=back
For convenience an instance of L can also be used as
callback.
=head2 complain
What to do on errors. Set to 1 be default (warn). Set to 0 to ignore. Other
values will die on errors.
=head2 strict
Enables errors on undefined subjects, predicates, and objects. By default
the Perl value C in any part of an encoded RDF triple will silently
ignore the triple, so aREF structures can easily be used as templates with
optional values.
=head2 null
A null object that is treated equivalent to C if found as object. For
instance setting this to the empty string will ignore all triples with the
empty string as literal value.
=head2 bnode_prefix
A prefix for blank node identifiers. Defaults to "b", so blank node identifiers
will be "b1", "b2", "b3" etc.
=head2 bnode_count
An integer to start creating blank node identifiers with. The default value "0"
results in blank node identifiers starting from "b1". This option can be useful
to avoid collision of blank node identifiers when merging multiple aREF
instances. The current counter value is accessible as accessor.
=head1 METHODS
=head2 decode( $aref [, keep_bnode_map => 1 ] )
Encode RDF data given in aREF. Resets all blank node identifier mappings unless
option c is set.
=head2 clean_bnodes
Delete blank node identifier mapping and reset bnode_count.
=head1 EXPORTABLE CONSTANTS
On request this module exports the following regular expressions, as defined in the
L:
=over
=item qName
=item blankNode
=item IRIlike
=item languageString
=item languageTag
=item datatypeString
=back
=head1 SEE ALSO
L
=cut
Encoder.pm 100644 001750 001750 24560 12475316027 15377 0 ustar 00voj voj 000000 000000 RDF-aREF-0.25/lib/RDF/aREF package RDF::aREF::Encoder;
use strict;
use warnings;
use v5.10;
our $VERSION = '0.25';
use RDF::NS;
use RDF::aREF::Decoder qw(localName blankNodeIdentifier);
use Scalar::Util qw(blessed reftype);
use Carp qw(croak);
sub new {
my ($class, %options) = @_;
if (!defined $options{ns}) {
$options{ns} = RDF::NS->new;
} elsif (!$options{ns}) {
$options{ns} = bless {
rdf => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
rdfs => 'http://www.w3.org/2000/01/rdf-schema#',
owl => 'http://www.w3.org/2002/07/owl#',
xsd => 'http://www.w3.org/2001/XMLSchema#',
}, 'RDF::NS';
} elsif ( !blessed $options{ns} or !$options{ns}->isa('RDF::NS') ) {
$options{ns} = RDF::NS->new($options{ns});
}
$options{sn} = $options{ns}->REVERSE;
$options{subject_map} = !!$options{subject_map};
if ($options{NFC}) {
eval { require Unicode::Normalize };
croak "Missing Unicode::Normalize: NFC normalization disabled!\n" if $@;
}
bless \%options, $class;
}
sub qname {
my ($self, $uri) = @_;
return unless $self->{sn};
my @qname = $self->{sn}->qname($uri);
return $qname[0] if @qname == 1;
return join('_',@qname) if @qname and $qname[1] =~ localName;
return;
}
sub uri {
my ($self, $uri) = @_;
if ( my $qname = $self->qname($uri) ) {
return $qname;
} else {
return "<$uri>";
}
}
sub subject {
my ($self, $subject) = @_;
return do {
if (!reftype $subject) {
undef
# RDF/JSON
} elsif (reftype $subject eq 'HASH') {
if ($subject->{type} eq 'uri' or $subject->{type} eq 'bnode') {
$subject->{value}
}
# RDF::Trine::Node
} elsif (reftype $subject eq 'ARRAY') {
if (@$subject == 2 ) {
if ($subject->[0] eq 'URI') {
"".$subject->[1];
} elsif ($subject->[0] eq 'BLANK') {
$self->bnode($subject->[1])
}
}
}
};
}
sub predicate {
my ($self, $predicate) = @_;
$predicate = do {
if (!reftype $predicate) {
undef
# RDF/JSON
} elsif (reftype $predicate eq 'HASH' and $predicate->{type} eq 'uri') {
$predicate->{value}
# RDF::Trine::Node
} elsif (reftype $predicate eq 'ARRAY') {
(@$predicate == 2 and $predicate->[0] eq 'URI')
? "".$predicate->[1] : undef;
}
};
return do {
if ( !defined $predicate ) {
undef
} elsif ( $predicate eq 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' ) {
'a'
} elsif ( my $qname = $self->qname($predicate) ) {
$qname
} else {
$predicate
}
};
}
sub object {
my ($self, $object) = @_;
return do {
if (!reftype $object) {
undef
# RDF/JSON
} elsif (reftype $object eq 'HASH') {
if ($object->{type} eq 'literal') {
$self->literal( $object->{value}, $object->{lang}, $object->{datatype} )
} elsif ($object->{type} eq 'bnode') {
$object->{value}
} else {
$self->uri($object->{value})
}
# RDF::Trine::Node
} elsif (reftype $object eq 'ARRAY') {
if (@$object != 2 ) {
$self->literal(@$object)
} elsif ($object->[0] eq 'URI') {
$self->uri("".$object->[1])
} elsif ($object->[0] eq 'BLANK') {
$self->bnode($object->[1])
}
}
};
}
sub literal {
my ($self, $value, $language, $datatype) = @_;
if ($self->{NFC}) {
$value = Unicode::Normalize::NFC($value);
}
if ($language) {
$value.'@'.$language
} elsif ($datatype) {
$value.'^'.$self->uri($datatype)
} else {
$value.'@'
}
}
sub bnode {
$_[1] =~ blankNodeIdentifier ? '_:'.$_[1] : undef;
}
sub triple {
my ($self, $subject, $predicate, $object, $aref) = @_;
$subject = $self->subject($subject) // return;
$predicate = $self->predicate($predicate) // return;
$object = $self->object($object) // return;
$aref //= { };
# empty
if ( !keys %$aref and !$self->{subject_map} ) {
$aref->{_id} = $subject;
$aref->{$predicate} = $object;
# predicate map
} elsif ( $aref->{_id} ) {
if ( $aref->{_id} eq $subject and !$self->{subject_map} ) {
$self->_add_object_to_predicate_map( $aref, $predicate, $object );
} else {
# convert predicate map to subject map
my $s = delete $aref->{_id};
my $pm = { };
foreach (keys %$aref) {
$pm->{$_} = delete $aref->{$_};
}
if ($s eq $subject) {
$self->_add_object_to_predicate_map( $pm, $predicate, $object );
} else {
$aref->{$subject} = { $predicate => $object };
}
$aref->{$s} = $pm;
}
} else { # subject map
if ( $aref->{$subject} ) {
$self->_add_object_to_predicate_map( $aref->{$subject}, $predicate, $object );
} else {
$aref->{$subject} = { $predicate => $object };
}
}
return $aref;
}
sub _add_object_to_predicate_map {
my ($self, $map, $predicate, $object) = @_;
if (ref $map->{$predicate}) {
push @{$map->{$predicate}}, $object;
} elsif (defined $map->{$predicate}) {
$map->{$predicate} = [ $map->{$predicate}, $object ];
} else {
$map->{$predicate} = $object;
}
}
sub add_iterator {
my ($self, $iterator, $aref) = @_;
while (my $s = $iterator->next) {
$self->triple($s->subject, $s->predicate, $s->object, $aref);
}
}
sub add_hashref {
my ($self, $hashref, $aref) = @_;
while (my ($s,$ps) = each %$hashref) {
my $subject = $s =~ /^_:/ ? ['BLANK',substr($s, 2)] : ['URI',$s];
foreach my $p (keys %$ps) {
my $predicate = ['URI',$p];
foreach my $object (@{ $hashref->{$s}->{$p} }) {
$self->triple($subject, $predicate, $object, $aref);
}
}
}
}
1;
__END__
=head1 NAME
RDF::aREF::Encoder - encode RDF to another RDF Encoding Form
=head1 SYNOPSIS
use RDF::aREF::Encoder;
my $encoder = RDF::aREF::Encoder->new;
# encode parts of aREF
my $qname = $encoder->qname('http://schema.org/Review'); # 'schema_Review'
my $predicate = $encoder->predicate({
type => 'uri',
value => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type'
}); # 'a'
my $object = $encoder->object({
type => 'literal',
value => 'hello, world!',
lang => 'en'
}); # 'hello, world!@en'
# method also accepts RDF::Trine::Node instances
my $object = $encoder->object( RDF::Trine::Resource->new($iri) );
# encode RDF graphs (see also function 'encode_aref' in RDF::aREF)
use RDF::Trine::Parser;
my $aref = { };
RDF::Trine::Parser->parse_file ( $base_uri, $fh, sub {
my $s = shift;
$encoder->triple( $s->subject, $s->predicate, $s->object, $aref );
} );
=head1 DESCRIPTION
This module provides methods to encode RDF data in another RDF Encoding Form
(aREF). As aREF was designed to facilitate creation of RDF data, it may be
easier to create aREF "by hand" instead of using this module!
=head1 OPTIONS
=head2 ns
A default namespace map, given as version string of module L for
stable qNames or as instance of L. The most recent installed version
of L is used by default. The value C<0> can be used to only use
required namespace mappings (rdf, rdfs, owl and xsd).
=head2 subject_map
By default RDF graphs with common subject are encoded as aREF predicate map:
{
_id => $subject, $predicate => $object
}
Enable this option to always encode as aREF subject map:
{
$subject => { $predicate => $object }
}
=head1 METHODS
Note that no syntax checking is applied, e.g. whether a given URI is a valid
URI or whether a given language is a valid language tag!
=head2 qname( $uri )
Abbreviate an URI as qName or return C. For instance
C is abbreviated to "C".
=head2 uri( $uri )
Abbreviate an URI or as qName or enclose it in angular brackets.
=head2 literal( $value, $language_tag, $datatype_uri )
Encode a literal RDF node by either appending "C<@>" and an optional
language tag, or "C<^>" and an datatype URI.
=head2 bnode( $identifier )
Encode a blank node by prepending "C<_:>" to its identifier.
=head2 subject( $subject )
=head2 predicate( $predicate )
=head2 object( $object )
Encode an RDF subject, predicate, or object respectively. The argument must
either be given as hash reference, as defined in
L format (see also method
C of L), or as array reference as internally
used by L.
A hash reference is expected to have the following fields:
=over
=item type
one of C, C or C (required)
=item value
the URI of the object, its lexical value or a blank node label depending on
whether the object is a uri, literal or bnode
=item lang
the language of a literal value (optional but if supplied it must not be empty)
=item datatype
the datatype URI of the literal value (optional)
=back
An array reference is expected to consists of
=over
=item
three elements (value, language tag, and datatype uri) for literal nodes,
=item
two elements "C" and the URI for URI nodes,
=item
two elements "C" and the blank node identifier for blank nodes.
=back
=head2 triple( $subject, $predicate, $object, [, $aref ] )
Encode an RDF triple, its elements given as explained for method C,
C, and C