Skip to content

Commit

Permalink
Leverage RDF::Turtle improvements on writing lists, and checks from J…
Browse files Browse the repository at this point in the history
…SON-LD for list elements shared across graphs.
  • Loading branch information
gkellogg committed Sep 14, 2018
1 parent 7745d44 commit b18f57b
Show file tree
Hide file tree
Showing 2 changed files with 175 additions and 20 deletions.
17 changes: 11 additions & 6 deletions lib/rdf/trig/writer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,16 @@ def write_epilogue

# Pre-process statements again, but in the specified graph
@graph.each {|st| preprocess_statement(st)}

# Remove lists that are referenced and have non-list properties,
# or are present in more than one graph, or have elements
# that are present in more than one graph;
# these are legal, but can't be serialized as lists
@lists.reject! do |node, list|
ref_count(node) > 0 && non_list_prop_count(node) > 0 ||
list.subjects.any? {|elt| !resource_in_single_graph?(elt)}
end

order_subjects.each do |subject|
unless is_done?(subject)
statement(subject)
Expand All @@ -180,15 +190,10 @@ def write_epilogue
protected

# Add additional constraint that the resource must be in a single graph
def blankNodePropertyList?(subject)
def blankNodePropertyList?(subject, position)
super && resource_in_single_graph?(subject)
end

# Add additional constraint that the resource must be in a single graph
def p_squared?(resource, position)
super && resource_in_single_graph?(resource)
end

def resource_in_single_graph?(resource)
graph_names = @repo.query(subject: resource).map(&:graph_name)
graph_names += @repo.query(object: resource).map(&:graph_name)
Expand Down
178 changes: 164 additions & 14 deletions spec/writer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

describe RDF::TriG::Writer do
let(:logger) {RDF::Spec.logger}
after(:each) {|example| puts logger.to_s if example.exception}

it_behaves_like 'an RDF::Writer' do
let(:writer) {RDF::TriG::Writer.new}
Expand Down Expand Up @@ -33,7 +32,7 @@
"relative URIs with base" => {
input: %({<http://a/b> <http://a/c> <http://a/d> .}),
regexp: [ %r(^@base <http://a/> \.$), %r(^<b> <c> <d> \.$)],
base: "http://a/"
base_uri: "http://a/"
},
"pname URIs with prefix" => {
input: %({<http://example.com/b> <http://example.com/c> <http://example.com/d> .}),
Expand All @@ -53,11 +52,11 @@
},
}.each do |name, params|
it name do
serialize(params[:input], params[:base], params[:regexp], params)
serialize(params[:input], params[:regexp], params)
end

it "#{name} (stream)" do
serialize(params[:input], params[:base], params.fetch(:regexp_stream, params[:regexp]), params.merge(stream: true))
serialize(params[:input], params.fetch(:regexp_stream, params[:regexp]), params.merge(stream: true))
end
end
end
Expand Down Expand Up @@ -117,16 +116,165 @@
],
}.each_pair do |title, (input, matches, matches_stream)|
it title do
serialize(input, nil, matches)
serialize(input, matches)
end

it "#{title} (stream)" do
matches_stream ||= matches
serialize(input, nil, matches_stream, stream: true)
serialize(input, matches_stream, stream: true)
end
end
end


describe "lists" do
{
"bare list": {
input: %(@prefix ex: <http://example.com/> . (ex:a ex:b) .),
regexp: [%r(^\(\s*ex:a ex:b\s*\) \.$)]
},
"literal list": {
input: %(@prefix ex: <http://example.com/> . ex:a ex:b ( "apple" "banana" ) .),
regexp: [%r(^ex:a ex:b \(\s*"apple" "banana"\s*\) \.$)]
},
"empty list": {
input: %(@prefix ex: <http://example.com/> . ex:a ex:b () .),
regexp: [%r(^ex:a ex:b \(\s*\) \.$)]
},
"empty list as subject": {
input: %(@prefix ex: <http://example.com/> . () ex:a ex:b .),
regexp: [%r(^\(\s*\) ex:a ex:b \.$)]
},
"list as subject": {
input: %(@prefix ex: <http://example.com/> . (ex:a) ex:b ex:c .),
regexp: [%r(^\(\s*ex:a\s*\) ex:b ex:c \.$)]
},
"list of empties": {
input: %(@prefix ex: <http://example.com/> . [ex:listOf2Empties (() ())] .),
regexp: [%r(\[\s*ex:listOf2Empties \(\s*\(\s*\) \(\s*\)\s*\)\s*\] \.$)]
},
"list anon": {
input: %(@prefix ex: <http://example.com/> . [ex:twoAnons ([a ex:mother] [a ex:father])] .),
regexp: [%r(\[\s*ex:twoAnons \(\s*\[\s*a ex:mother\s*\] \[\s*a ex:father\s*\]\)\] \.$)]
},
"owl:unionOf list": {
input: %(
@prefix ex: <http://example.com/> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:a rdfs:domain [
a owl:Class;
owl:unionOf [
a owl:Class;
rdf:first ex:b;
rdf:rest [
a owl:Class;
rdf:first ex:c;
rdf:rest rdf:nil
]
]
] .
),
regexp: [
%r(ex:a rdfs:domain \[\s*a owl:Class;\s+owl:unionOf\s+\(\s*ex:b\s+ex:c\s*\)\s*\]\s*\.$)m,
%r(@prefix ex: <http://example.com/> \.),
%r(@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> \.),
]
},
"list with first subject a URI": {
input: %(
<http://example.com> <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "1"^^<http://www.w3.org/2001/XMLSchema#integer> .
<http://example.com> <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:g47006741228480 .
_:g47006741228480 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "2"^^<http://www.w3.org/2001/XMLSchema#integer> .
_:g47006741228480 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:g47006737917560 .
_:g47006737917560 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "3"^^<http://www.w3.org/2001/XMLSchema#integer> .
_:g47006737917560 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> .
),
regexp: [
%r(@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> \.),
%r(<http://example.com> rdf:first 1;),
%r(rdf:rest \(\s*2 3\s*\) \.),
],
standard_prefixes: true, format: :nquads
},
"list pattern without rdf:nil": {
input: %(
<http://example.com> <http://example.com/property> _:a .
_:a <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "a" .
_:a <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:b .
_:b <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "b" .
_:b <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:c .
_:c <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "c" .
),
regexp: [%r(<http://example.com> <http://example.com/property> \[),
%r(rdf:first "a";),
%r(rdf:rest \[),
%r(rdf:first "b";),
%r(rdf:rest \[\s*rdf:first "c"\s*\]),
],
standard_prefixes: true, format: :nquads
},
"list pattern with extra properties": {
input: %(
<http://example.com> <http://example.com/property> _:a .
_:a <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "a" .
_:a <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:b .
_:b <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "b" .
_:a <http://example.com/other-property> "This list node has also properties other than rdf:first and rdf:rest" .
_:b <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:c .
_:c <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "c" .
_:c <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> .
),
regexp: [%r(<http://example.com> <http://example.com/property> \[),
%r(<http://example.com/other-property> "This list node has also properties other than rdf:first and rdf:rest";),
%r(rdf:first "a";),
%r(rdf:rest \(\s*"b" "c"\s*\)),
],
standard_prefixes: true, format: :nquads
},
"list with node shared across graphs": {
input: %(
<http://www.example.com/z> <http://www.example.com/q> _:z0 <http://www.example.com/G> .
_:z0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "cell-A" <http://www.example.com/G> .
_:z0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:z1 <http://www.example.com/G> .
_:z1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "cell-B" <http://www.example.com/G> .
_:z1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> <http://www.example.com/G> .
<http://www.example.com/x> <http://www.example.com/p> _:z1 <http://www.example.com/G1> .
),
regexp: [
%r(<http://www.example.com/G> {\s+<http://www.example.com/z> <http://www.example.com/q> \[)m,
%r(rdf:first "cell-A";),
%r(rdf:rest _:z1),
%r(_:z1 rdf:first "cell-B";),
%r(rdf:rest \(\)),
%r(<http://www.example.com/G1> {\s+<http://www.example.com/x> <http://www.example.com/p> _:z1 .)m
],
standard_prefixes: true, format: :nquads
},
"list with node shared across graphs (same triple in different graphs)": {
input: %(
<http://www.example.com/z> <http://www.example.com/q> _:z0 <http://www.example.com/G> .
_:z0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "cell-A" <http://www.example.com/G> .
_:z0 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> _:z1 <http://www.example.com/G> .
_:z1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#first> "cell-B" <http://www.example.com/G> .
_:z1 <http://www.w3.org/1999/02/22-rdf-syntax-ns#rest> <http://www.w3.org/1999/02/22-rdf-syntax-ns#nil> <http://www.example.com/G> .
<http://www.example.com/z> <http://www.example.com/q> _:z0 <http://www.example.com/G1> .
),
regexp: [
%r(<http://www.example.com/G> {\s+<http://www.example.com/z> <http://www.example.com/q> _:z0 .)m,
%r(_:z0 rdf:first "cell-A";),
%r(rdf:rest \(\s*"cell-B"\) .),
%r(<http://www.example.com/G1> {\s+<http://www.example.com/z> <http://www.example.com/q> _:z0 .)m,
],
standard_prefixes: true, format: :nquads
}
}.each do |name, params|
it name do
serialize(params[:input], params[:regexp], **params)
end
end
end

# W3C TriG Test suite
describe "w3c trig tests" do
require 'suite_helper'
Expand All @@ -139,7 +287,7 @@
specify "#{t.name}: #{t.comment}" do
pending("native literals canonicalized") if t.name == "trig-subm-26"
repo = parse(t.expected, format: :nquads)
trig = serialize(repo, t.base, [], base_uri: t.base, standard_prefixes: true)
trig = serialize(repo, [], base_uri: t.base, standard_prefixes: true)
logger.info t.inspect
logger.info "source: #{t.expected}"
logger.info "serialized: #{trig}"
Expand All @@ -150,7 +298,7 @@
specify "#{t.name}: #{t.comment} (stream)" do
pending("native literals canonicalized") if t.name == "trig-subm-26"
repo = parse(t.expected, format: :nquads)
trig = serialize(repo, t.base, [], stream: true, base_uri: t.base, standard_prefixes: true)
trig = serialize(repo, [], stream: true, base_uri: t.base, standard_prefixes: true)
logger.info t.inspect
logger.info "source: #{t.expected}"
logger.info "serialized: #{trig}"
Expand All @@ -169,21 +317,23 @@ def parse(input, options = {})
end

# Serialize ntstr to a string and compare against regexps
def serialize(ntstr, base = nil, regexps = [], options = {})
def serialize(ntstr, regexps = [], base_uri: nil, **options)
prefixes = options[:prefixes] || {}
repo = ntstr.is_a?(RDF::Enumerable) ? ntstr : parse(ntstr, base_uri: base, prefixes: prefixes, validate: false, logger: [])
repo = ntstr.is_a?(RDF::Enumerable) ? ntstr : parse(ntstr, base_uri: base_uri, prefixes: prefixes, validate: false, logger: [], **options)
logger.info "serialized: #{ntstr}"
result = RDF::TriG::Writer.buffer(options.merge(
logger: logger,
base_uri: base,
base_uri: base_uri,
prefixes: prefixes,
encoding: Encoding::UTF_8
)) do |writer|
writer << repo
end


logger.info "result: #{result}"
regexps.each do |re|
expect(result).to match_re(re, about: base, logger: logger, input: ntstr)
logger.info "match: #{re.inspect}"
expect(result).to match_re(re, about: base_uri, logger: logger, input: ntstr), logger.to_s
end

result
Expand Down

0 comments on commit b18f57b

Please sign in to comment.