Skip to content

Commit 88ba1fd

Browse files
committed
RDF 1.2 Triple Terms.
1 parent 6e15813 commit 88ba1fd

21 files changed

+856
-1320
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
strategy:
2020
fail-fast: false
2121
matrix:
22-
ruby: ['3.0', 3.1, 3.2, 3.3, ruby-head, jruby]
22+
ruby: ['3.0', 3.1, 3.2, 3.3, 3.4, ruby-head, jruby]
2323
steps:
2424
- name: Clone repository
2525
uses: actions/checkout@v3
@@ -33,7 +33,7 @@ jobs:
3333
run: ruby --version; bundle exec rspec spec || $ALLOW_FAILURES
3434
- name: Coveralls GitHub Action
3535
uses: coverallsapp/github-action@v2
36-
if: "matrix.ruby == '3.2'"
36+
if: "matrix.ruby == '3.4'"
3737
with:
3838
github-token: ${{ secrets.GITHUB_TOKEN }}
3939
wintests:
@@ -42,11 +42,11 @@ jobs:
4242
runs-on: windows-latest
4343
env:
4444
CI: true
45-
ALLOW_FAILURES: ${{ matrix.ruby == '3.2' || matrix.ruby == 'jruby' }}
45+
ALLOW_FAILURES: ${{ matrix.ruby == 'jruby' }}
4646
strategy:
4747
fail-fast: false
4848
matrix:
49-
ruby: [3.1, 3.2]
49+
ruby: [3.1, 3.2, 3.3, 3.4]
5050
steps:
5151
- name: Clone repository
5252
uses: actions/checkout@v3

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ end
2020
group :development, :test do
2121
gem 'benchmark-ips'
2222
gem 'fasterer'
23+
gem 'readline'
24+
gem 'irb'
2325
gem 'psych', platforms: %i[mri rbx]
2426
gem 'rake'
2527
gem 'rubocop'

README.md

Lines changed: 68 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,70 @@ The [MultiJson](https://rubygems.org/gems/multi_json) gem is used for parsing an
4141

4242
The {JSON::LD::API.expand}, {JSON::LD::API.compact}, {JSON::LD::API.toRdf}, and {JSON::LD::API.fromRdf} API methods, along with the {JSON::LD::Reader} and {JSON::LD::Writer}, include provisional support for [JSON-LD-star][JSON-LD-star].
4343

44-
Internally, an `RDF::Statement` is treated as another resource, along with `RDF::URI` and `RDF::Node`, which allows an `RDF::Statement` to have a `#subject` or `#object` which is also an `RDF::Statement`.
44+
Internally, an `RDF::Statement` is treated as another resource, along with `RDF::URI` and `RDF::Node`, which allows an `RDF::Statement` to have an `#object` which is also an `RDF::Statement`.
4545

46-
In JSON-LD, with the `rdfstar` option set, the value of `@id`, in addition to an IRI or Blank Node Identifier, can be a JSON-LD node object having exactly one property with an optional `@id`, which may also be an embedded object. (It may also have `@context` and `@index` values).
46+
#### Triple Terms
47+
48+
In JSON-LD, with the `rdfstar` option set, a node object have an `@triple` property (or alias) instead of `@id`, identifies the value of `@triple` as a [Triple Term](https://www.w3.org/TR/rdf12-concepts/#dfn-triple-term). The constraint on the value of `@triple` can be a JSON-LD node object having exactly one property with an optional `@id`, which may also be an triple term. (It may also have `@context` and `@index` values).
4749

4850
{
49-
"@id": {
51+
"@triple": {
5052
"@context": {"foaf": "http://xmlns.com/foaf/0.1/"},
5153
"@index": "ignored",
5254
"@id": "bob",
5355
"foaf:age" 23
56+
}
57+
}
58+
59+
The equivalent turtle would be the following:
60+
61+
<<( :bob <http://xmlns.com/foaf/0.1/age> 21 )>>
62+
63+
An object including `@triple` **MUST NOT** define any extra properties for which that triple term would be used as the subject.
64+
65+
#### Reifying Triples
66+
67+
As RDF-star prefers the use of [reifiers](https://www.w3.org/TR/rdf12-concepts/#dfn-reifier) as a way to use a triple term. In this case the property `@refies` (or alias) defines one (or more) triples which are _reified_ by the subject of the containing _node object_. The interpretation of a node object including `@reifies` creates a triple term for the triple(s) defined by the content of the block. The subject of that node object (`@id` or a default blank node) becomes the _reifier_ of the associated triple term(s).
68+
69+
{
70+
"@context": {"foaf": "http://xmlns.com/foaf/0.1/", "ex": "http://example.com/"},
71+
"@id": "_:reif",
72+
"@reifies": {
73+
"@id": "bob",
74+
"foaf:age" 23
75+
},
76+
"ex:certainty": 0.9
77+
}
78+
79+
This is equivalent to the following JSON-LD using `@triple`:
80+
81+
{
82+
"@context": {
83+
"foaf": "http://xmlns.com/foaf/0.1/",
84+
"ex": "http://example.com/",
85+
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
5486
},
87+
"@id": "_:reif",
88+
"rdf:reifies": {
89+
"@triple": {
90+
"@id": "bob",
91+
"foaf:age" 23
92+
}
93+
}
5594
"ex:certainty": 0.9
5695
}
5796

97+
Or, in Turtle:
98+
99+
_:reif rdf:reifies <<( :bob <http://xmlns.com/foaf/0.1/age> 21 )>>;
100+
ex:certainty 0.9 .
101+
102+
Or, equivalently, using the Turtle [`reifiedTriple`](https://www.w3.org/TR/rdf12-turtle/#grammar-production-reifiedTriple) syntax:
103+
104+
<< :bob <http://xmlns.com/foaf/0.1/age> 21 ~ _:reif>> ex:certainty 0.9 .
105+
106+
#### Annotation
107+
58108
Additionally, the `@annotation` property (or alias) may be used on a node object or value object to annotate the statement for which the associated node is the object of a triple.
59109

60110
{
@@ -66,42 +116,46 @@ Additionally, the `@annotation` property (or alias) may be used on a node object
66116
}
67117
}
68118

69-
In the first case, the embedded node is not asserted, and only appears as the subject of a triple. In the second case, the triple is asserted and used as the subject in another statement which annotates it.
119+
In the case of Reifying Triples, the embedded triple term is not asserted, and only appears as the subject of a triple. In the second case, the triple is asserted and used as the subject in another statement which annotates it.
70120

71121
**Note: This feature is subject to change or elimination as the standards process progresses.**
72122

73-
#### Serializing a Graph containing embedded statements
123+
#### Serializing a Graph containing triple terms
74124

75125
require 'json/ld'
76-
statement = RDF::Statement(RDF::URI('bob'), RDF::Vocab::FOAF.age, RDF::Literal(23))
77-
graph = RDF::Graph.new << [statement, RDF::URI("ex:certainty"), RDF::Literal(0.9)]
126+
statement = RDF::Statement(RDF::URI('bob'), RDF::Vocab::FOAF.age, RDF::Literal(23), tripleTerm: true)
127+
reif = RDF::Node.new("reif")
128+
graph = RDF::Graph.new do |g|
129+
g << [reif, RDF.reifies, statement]
130+
g << [reif, RDF::URI("ex:certainty"), RDF::Literal(0.9)]
131+
end
78132
graph.dump(:jsonld, validate: false, standard_prefixes: true)
79-
# => {"@id": {"@id": "bob", "foaf:age" 23}, "ex:certainty": 0.9}
133+
# => {"@id": "_:reif", @reifies: {"@id": "bob", "foaf:age" 23}, "ex:certainty": 0.9}
80134

81135
Alternatively, using the {JSON::LD::API.fromRdf} method:
82136

83137
JSON::LD::API::fromRdf(graph)
84-
# => {"@id": {"@id": "bob", "foaf:age" 23}, "ex:certainty": 0.9}
138+
# => {"@id": "_:reif", @reifies: {"@id": "bob", "foaf:age" 23}, "ex:certainty": 0.9}
85139

86-
#### Reading a Graph containing embedded statements
140+
#### Reading a Graph containing triple terms
87141

88-
By default, {JSON::LD::API.toRdf} (and {JSON::LD::Reader}) will reject a document containing a subject resource.
142+
By default, {JSON::LD::API.toRdf} (and {JSON::LD::Reader}) will reject a document containing a triple term.
89143

90144
jsonld = %({
91-
"@id": {
145+
"@reifies": {
92146
"@id": "bob", "foaf:age" 23
93147
},
94148
"ex:certainty": 0.9
95149
})
96150
graph = RDF::Graph.new << JSON::LD::API.toRdf(input)
97-
# => JSON::LD::JsonLdError::InvalidIdValue
151+
# => JSON::LD::JsonLdError::InvalidReifier
98152

99153
{JSON::LD::API.toRdf} (and {JSON::LD::Reader}) support a boolean valued `rdfstar` option; only one statement is asserted, although the reified statement is contained within the graph.
100154

101155
graph = RDF::Graph.new do |graph|
102156
JSON::LD::Reader.new(jsonld, rdfstar: true) {|reader| graph << reader}
103157
end
104-
graph.count #=> 1
158+
graph.count #=> 3
105159

106160
## Examples
107161

lib/json/ld.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,11 @@ module LD
7373
@propagate
7474
@protected
7575
@preserve
76+
@reifier
7677
@requireAll
7778
@reverse
7879
@set
80+
@triple
7981
@type
8082
@value
8183
@version
@@ -128,6 +130,7 @@ class CollidingKeywords < JsonLdError; @code = 'colliding keywords'; end
128130
class ConflictingIndexes < JsonLdError; @code = 'conflicting indexes'; end
129131
class CyclicIRIMapping < JsonLdError; @code = 'cyclic IRI mapping'; end
130132
class InvalidAnnotation < JsonLdError; @code = 'invalid annotation'; end
133+
class InvalidBaseDirection < JsonLdError; @code = 'invalid base direction'; end
131134
class InvalidBaseIRI < JsonLdError; @code = 'invalid base IRI'; end
132135
class InvalidContainerMapping < JsonLdError; @code = 'invalid container mapping'; end
133136
class InvalidContextEntry < JsonLdError; @code = 'invalid context entry'; end
@@ -149,7 +152,6 @@ class InvalidLocalContext < JsonLdError; @code = 'invalid local context'; end
149152
class InvalidNestValue < JsonLdError; @code = 'invalid @nest value'; end
150153
class InvalidPrefixValue < JsonLdError; @code = 'invalid @prefix value'; end
151154
class InvalidPropagateValue < JsonLdError; @code = 'invalid @propagate value'; end
152-
class InvalidEmbeddedNode < JsonLdError; @code = 'invalid embedded node'; end
153155
class InvalidRemoteContext < JsonLdError; @code = 'invalid remote context'; end
154156
class InvalidReverseProperty < JsonLdError; @code = 'invalid reverse property'; end
155157
class InvalidReversePropertyMap < JsonLdError; @code = 'invalid reverse property map'; end
@@ -160,7 +162,7 @@ class InvalidScriptElement < JsonLdError; @code = 'invalid script element'; end
160162
class InvalidSetOrListObject < JsonLdError; @code = 'invalid set or list object'; end
161163
class InvalidStreamingKeyOrder < JsonLdError; @code = 'invalid streaming key order' end
162164
class InvalidTermDefinition < JsonLdError; @code = 'invalid term definition'; end
163-
class InvalidBaseDirection < JsonLdError; @code = 'invalid base direction'; end
165+
class InvalidTripleTerm < JsonLdError; @code = 'invalid triple term'; end
164166
class InvalidTypedValue < JsonLdError; @code = 'invalid typed value'; end
165167
class InvalidTypeMapping < JsonLdError; @code = 'invalid type mapping'; end
166168
class InvalidTypeValue < JsonLdError; @code = 'invalid type value'; end

lib/json/ld/compact.rb

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def compact(element,
2222
base: nil,
2323
property: nil,
2424
log_depth: nil)
25-
# log_debug("compact", depth: log_depth.to_i) {"element: #{element.inspect}, ec: #{context.inspect}"}
25+
log_debug("compact", depth: log_depth.to_i) {"element: #{element.inspect}, ec: #{context.inspect}"}
2626

2727
# If the term definition for active property itself contains a context, use that for compacting values.
2828
input_context = context
@@ -101,14 +101,7 @@ def compact(element,
101101

102102
if expanded_property == '@id'
103103
compacted_value = as_array(expanded_value).map do |expanded_id|
104-
if node?(expanded_id) && @options[:rdfstar]
105-
# This can only really happen for valid RDF-star
106-
compact(expanded_id, base: base,
107-
property: '@id',
108-
log_depth: log_depth.to_i + 1)
109-
else
110-
context.compact_iri(expanded_id, base: @options[:base])
111-
end
104+
context.compact_iri(expanded_id, base: @options[:base])
112105
end
113106

114107
kw_alias = context.compact_iri('@id', vocab: true)
@@ -170,6 +163,18 @@ def compact(element,
170163
next
171164
end
172165

166+
# If expanded property is @triple
167+
if expanded_property == '@triple'
168+
# Compact using `property`
169+
compacted_value = compact(expanded_value, base: base,
170+
property: property,
171+
log_depth: log_depth.to_i + 1)
172+
173+
al = context.compact_iri('@triple', vocab: true)
174+
result[al] = compacted_value
175+
next
176+
end
177+
173178
# Otherwise, if expanded property is @direction, @index, @value, or @language:
174179
if EXPANDED_PROPERTY_DIRECTION_INDEX_LANGUAGE_VALUE.include?(expanded_property)
175180
al = context.compact_iri(expanded_property, vocab: true)

0 commit comments

Comments
 (0)