json-ld 2.1.2 → 2.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -184,23 +184,48 @@ module JSON::LD
184
184
  end
185
185
 
186
186
  ##
187
- # Replace @preserve keys with the values, also replace @null with null
187
+ # Recursively find and count blankNode identifiers.
188
+ # @return [Hash{String => Integer}]
189
+ def count_blank_node_identifiers(input, results = {})
190
+ case input
191
+ when Array
192
+ input.map {|o| count_blank_node_identifiers(o, results)}
193
+ when Hash
194
+ input.each do |k, v|
195
+ count_blank_node_identifiers(v, results)
196
+ end
197
+ when String
198
+ if input.start_with?('_:')
199
+ results[input] ||= 0
200
+ results[input] += 1
201
+ end
202
+ end
203
+ results
204
+ end
205
+
206
+ ##
207
+ # Replace @preserve keys with the values, also replace @null with null.
208
+ #
209
+ # Optionally, remove BNode identifiers only used once.
188
210
  #
189
211
  # @param [Array, Hash] input
212
+ # @param [Array<String>] bnodes_to_clear
190
213
  # @return [Array, Hash]
191
- def cleanup_preserve(input)
214
+ def cleanup_preserve(input, bnodes_to_clear)
192
215
  result = case input
193
216
  when Array
194
217
  # If, after replacement, an array contains only the value null remove the value, leaving an empty array.
195
- input.map {|o| cleanup_preserve(o)}.compact
218
+ input.map {|o| cleanup_preserve(o, bnodes_to_clear)}.compact
196
219
  when Hash
197
220
  output = Hash.new
198
221
  input.each do |key, value|
199
222
  if key == '@preserve'
200
223
  # replace all key-value pairs where the key is @preserve with the value from the key-pair
201
- output = cleanup_preserve(value)
224
+ output = cleanup_preserve(value, bnodes_to_clear)
225
+ elsif context.expand_iri(key) == '@id' && bnodes_to_clear.include?(value)
226
+ # Don't add this to output, as it is pruned as being superfluous
202
227
  else
203
- v = cleanup_preserve(value)
228
+ v = cleanup_preserve(value, bnodes_to_clear)
204
229
 
205
230
  # Because we may have added a null value to an array, we need to clean that up, if we possible
206
231
  v = v.first if v.is_a?(Array) && v.length == 1 &&
@@ -13,8 +13,9 @@ module JSON::LD
13
13
  # @param [Array<RDF::Statement>, RDF::Enumerable] input
14
14
  # @param [Boolean] useRdfType (false)
15
15
  # If set to `true`, the JSON-LD processor will treat `rdf:type` like a normal property instead of using `@type`.
16
+ # @param [Boolean] useNativeTypes (false) use native representations
16
17
  # @return [Array<Hash>] the JSON-LD document in normalized form
17
- def from_statements(input, useRdfType: false)
18
+ def from_statements(input, useRdfType: false, useNativeTypes: false)
18
19
  default_graph = {}
19
20
  graph_map = {'@default' => default_graph}
20
21
  node_usages_map = {}
@@ -48,7 +49,7 @@ module JSON::LD
48
49
  end
49
50
 
50
51
  # Set value to the result of using the RDF to Object Conversion algorithm, passing object and use native types.
51
- value = ec.expand_value(nil, statement.object, useNativeTypes: @options[:useNativeTypes], log_depth: @options[:log_depth])
52
+ value = ec.expand_value(nil, statement.object, useNativeTypes: useNativeTypes, log_depth: @options[:log_depth])
52
53
 
53
54
  merge_value(node, statement.predicate.to_s, value)
54
55
 
@@ -77,7 +78,7 @@ module JSON::LD
77
78
  # If property equals rdf:rest, the value associated to the usages member of node has exactly 1 entry, node has a rdf:first and rdf:rest property, both of which have as value an array consisting of a single element, and node has no other members apart from an optional @type member whose value is an array with a single item equal to rdf:List, node represents a well-formed list node. Continue with the following steps:
78
79
  #log_debug("list element?") {node.to_json(JSON_STATE) rescue 'malformed json'}
79
80
  while property == RDF.rest.to_s &&
80
- node_usages_map[node['@id']].uniq.length == 1 &&
81
+ Array(node_usages_map[node['@id']]).uniq.length == 1 &&
81
82
  blank_node?(node) &&
82
83
  node.keys.none? {|k| !["@id", '@type', :usages, RDF.first.to_s, RDF.rest.to_s].include?(k)} &&
83
84
  Array(node[:usages]).length == 1 &&
@@ -8,311 +8,323 @@ describe JSON::LD::API do
8
8
  describe ".compact" do
9
9
  {
10
10
  "prefix" => {
11
- input: {
12
- "@id" => "http://example.com/a",
13
- "http://example.com/b" => {"@id" => "http://example.com/c"}
14
- },
15
- context: {"ex" => "http://example.com/"},
16
- output: {
17
- "@context" => {"ex" => "http://example.com/"},
18
- "@id" => "ex:a",
19
- "ex:b" => {"@id" => "ex:c"}
20
- }
11
+ input: %({
12
+ "@id": "http://example.com/a",
13
+ "http://example.com/b": {"@id": "http://example.com/c"}
14
+ }),
15
+ context: %({"ex": "http://example.com/"}),
16
+ output: %({
17
+ "@context": {"ex": "http://example.com/"},
18
+ "@id": "ex:a",
19
+ "ex:b": {"@id": "ex:c"}
20
+ })
21
21
  },
22
22
  "term" => {
23
- input: {
24
- "@id" => "http://example.com/a",
25
- "http://example.com/b" => {"@id" => "http://example.com/c"}
26
- },
27
- context: {"b" => "http://example.com/b"},
28
- output: {
29
- "@context" => {"b" => "http://example.com/b"},
30
- "@id" => "http://example.com/a",
31
- "b" => {"@id" => "http://example.com/c"}
32
- }
23
+ input: %({
24
+ "@id": "http://example.com/a",
25
+ "http://example.com/b": {"@id": "http://example.com/c"}
26
+ }),
27
+ context: %({"b": "http://example.com/b"}),
28
+ output: %({
29
+ "@context": {"b": "http://example.com/b"},
30
+ "@id": "http://example.com/a",
31
+ "b": {"@id": "http://example.com/c"}
32
+ })
33
33
  },
34
34
  "integer value" => {
35
- input: {
36
- "@id" => "http://example.com/a",
37
- "http://example.com/b" => {"@value" => 1}
38
- },
39
- context: {"b" => "http://example.com/b"},
40
- output: {
41
- "@context" => {"b" => "http://example.com/b"},
42
- "@id" => "http://example.com/a",
43
- "b" => 1
44
- }
35
+ input: %({
36
+ "@id": "http://example.com/a",
37
+ "http://example.com/b": {"@value": 1}
38
+ }),
39
+ context: %({"b": "http://example.com/b"}),
40
+ output: %({
41
+ "@context": {"b": "http://example.com/b"},
42
+ "@id": "http://example.com/a",
43
+ "b": 1
44
+ })
45
45
  },
46
46
  "boolean value" => {
47
- input: {
48
- "@id" => "http://example.com/a",
49
- "http://example.com/b" => {"@value" => true}
50
- },
51
- context: {"b" => "http://example.com/b"},
52
- output: {
53
- "@context" => {"b" => "http://example.com/b"},
54
- "@id" => "http://example.com/a",
55
- "b" => true
56
- }
47
+ input: %({
48
+ "@id": "http://example.com/a",
49
+ "http://example.com/b": {"@value": true}
50
+ }),
51
+ context: %({"b": "http://example.com/b"}),
52
+ output: %({
53
+ "@context": {"b": "http://example.com/b"},
54
+ "@id": "http://example.com/a",
55
+ "b": true
56
+ })
57
57
  },
58
58
  "@id" => {
59
- input: {"@id" => "http://example.org/test#example"},
59
+ input: %({"@id": "http://example.org/test#example"}),
60
60
  context: {},
61
61
  output: {}
62
62
  },
63
63
  "@id coercion" => {
64
- input: {
65
- "@id" => "http://example.com/a",
66
- "http://example.com/b" => {"@id" => "http://example.com/c"}
67
- },
68
- context: {"b" => {"@id" => "http://example.com/b", "@type" => "@id"}},
69
- output: {
70
- "@context" => {"b" => {"@id" => "http://example.com/b", "@type" => "@id"}},
71
- "@id" => "http://example.com/a",
72
- "b" => "http://example.com/c"
73
- }
64
+ input: %({
65
+ "@id": "http://example.com/a",
66
+ "http://example.com/b": {"@id": "http://example.com/c"}
67
+ }),
68
+ context: %({"b": {"@id": "http://example.com/b", "@type": "@id"}}),
69
+ output: %({
70
+ "@context": {"b": {"@id": "http://example.com/b", "@type": "@id"}},
71
+ "@id": "http://example.com/a",
72
+ "b": "http://example.com/c"
73
+ })
74
74
  },
75
75
  "xsd:date coercion" => {
76
- input: {
77
- "http://example.com/b" => {"@value" => "2012-01-04", "@type" => RDF::XSD.date.to_s}
78
- },
79
- context: {
80
- "xsd" => RDF::XSD.to_s,
81
- "b" => {"@id" => "http://example.com/b", "@type" => "xsd:date"}
82
- },
83
- output: {
84
- "@context" => {
85
- "xsd" => RDF::XSD.to_s,
86
- "b" => {"@id" => "http://example.com/b", "@type" => "xsd:date"}
76
+ input: %({
77
+ "http://example.com/b": {"@value": "2012-01-04", "@type": "http://www.w3.org/2001/XMLSchema#date"}
78
+ }),
79
+ context: %({
80
+ "xsd": "http://www.w3.org/2001/XMLSchema#",
81
+ "b": {"@id": "http://example.com/b", "@type": "xsd:date"}
82
+ }),
83
+ output: %({
84
+ "@context": {
85
+ "xsd": "http://www.w3.org/2001/XMLSchema#",
86
+ "b": {"@id": "http://example.com/b", "@type": "xsd:date"}
87
87
  },
88
- "b" => "2012-01-04"
89
- }
88
+ "b": "2012-01-04"
89
+ })
90
90
  },
91
- "@list coercion" => {
92
- input: {
93
- "http://example.com/b" => {"@list" => ["c", "d"]}
94
- },
95
- context: {"b" => {"@id" => "http://example.com/b", "@container" => "@list"}},
96
- output: {
97
- "@context" => {"b" => {"@id" => "http://example.com/b", "@container" => "@list"}},
98
- "b" => ["c", "d"]
99
- }
91
+ "@list coercion": {
92
+ input: %({
93
+ "http://example.com/b": {"@list": ["c", "d"]}
94
+ }),
95
+ context: %({"b": {"@id": "http://example.com/b", "@container": "@list"}}),
96
+ output: %({
97
+ "@context": {"b": {"@id": "http://example.com/b", "@container": "@list"}},
98
+ "b": ["c", "d"]
99
+ })
100
100
  },
101
101
  "@list coercion (integer)" => {
102
- input: {
103
- "http://example.com/term" => [
104
- {"@list" => [1]},
102
+ input: %({
103
+ "http://example.com/term": [
104
+ {"@list": [1]}
105
105
  ]
106
- },
107
- context: {
108
- "term4" => {"@id" => "http://example.com/term", "@container" => "@list"},
109
- "@language" => "de"
110
- },
111
- output: {
112
- "@context" => {
113
- "term4" => {"@id" => "http://example.com/term", "@container" => "@list"},
114
- "@language" => "de"
106
+ }),
107
+ context: %({
108
+ "term4": {"@id": "http://example.com/term", "@container": "@list"},
109
+ "@language": "de"
110
+ }),
111
+ output: %({
112
+ "@context": {
113
+ "term4": {"@id": "http://example.com/term", "@container": "@list"},
114
+ "@language": "de"
115
115
  },
116
- "term4" => [1],
117
- }
116
+ "term4": [1]
117
+ })
118
118
  },
119
119
  "@set coercion" => {
120
- input: {
121
- "http://example.com/b" => {"@set" => ["c"]}
122
- },
123
- context: {"b" => {"@id" => "http://example.com/b", "@container" => "@set"}},
124
- output: {
125
- "@context" => {"b" => {"@id" => "http://example.com/b", "@container" => "@set"}},
126
- "b" => ["c"]
127
- }
120
+ input: %({
121
+ "http://example.com/b": {"@set": ["c"]}
122
+ }),
123
+ context: %({"b": {"@id": "http://example.com/b", "@container": "@set"}}),
124
+ output: %({
125
+ "@context": {"b": {"@id": "http://example.com/b", "@container": "@set"}},
126
+ "b": ["c"]
127
+ })
128
128
  },
129
129
  "empty @set coercion" => {
130
- input: {
131
- "http://example.com/b" => []
132
- },
133
- context: {"b" => {"@id" => "http://example.com/b", "@container" => "@set"}},
134
- output: {
135
- "@context" => {"b" => {"@id" => "http://example.com/b", "@container" => "@set"}},
136
- "b" => []
137
- }
130
+ input: %({
131
+ "http://example.com/b": []
132
+ }),
133
+ context: %({"b": {"@id": "http://example.com/b", "@container": "@set"}}),
134
+ output: %({
135
+ "@context": {"b": {"@id": "http://example.com/b", "@container": "@set"}},
136
+ "b": []
137
+ })
138
138
  },
139
139
  "@type with string @id" => {
140
- input: {
141
- "@id" => "http://example.com/",
142
- "@type" => "#{RDF::RDFS.Resource}"
143
- },
140
+ input: %({
141
+ "@id": "http://example.com/",
142
+ "@type": "#{RDF::RDFS.Resource}"
143
+ }),
144
144
  context: {},
145
- output: {
146
- "@id" => "http://example.com/",
147
- "@type" => "#{RDF::RDFS.Resource}"
148
- },
145
+ output: %({
146
+ "@id": "http://example.com/",
147
+ "@type": "#{RDF::RDFS.Resource}"
148
+ }),
149
149
  },
150
150
  "@type with array @id" => {
151
- input: {
152
- "@id" => "http://example.com/",
153
- "@type" => ["#{RDF::RDFS.Resource}"]
154
- },
151
+ input: %({
152
+ "@id": "http://example.com/",
153
+ "@type": ["#{RDF::RDFS.Resource}"]
154
+ }),
155
155
  context: {},
156
- output: {
157
- "@id" => "http://example.com/",
158
- "@type" => "#{RDF::RDFS.Resource}"
159
- },
156
+ output: %({
157
+ "@id": "http://example.com/",
158
+ "@type": "#{RDF::RDFS.Resource}"
159
+ }),
160
160
  },
161
161
  "default language" => {
162
- input: {
163
- "http://example.com/term" => [
162
+ input: %({
163
+ "http://example.com/term": [
164
164
  "v5",
165
- {"@value" => "plain literal"}
165
+ {"@value": "plain literal"}
166
166
  ]
167
- },
168
- context: {
169
- "term5" => {"@id" => "http://example.com/term", "@language" => nil},
170
- "@language" => "de"
171
- },
172
- output: {
173
- "@context" => {
174
- "term5" => {"@id" => "http://example.com/term", "@language" => nil},
175
- "@language" => "de"
167
+ }),
168
+ context: %({
169
+ "term5": {"@id": "http://example.com/term", "@language": null},
170
+ "@language": "de"
171
+ }),
172
+ output: %({
173
+ "@context": {
174
+ "term5": {"@id": "http://example.com/term", "@language": null},
175
+ "@language": "de"
176
176
  },
177
- "term5" => [ "v5", "plain literal" ]
178
- }
177
+ "term5": [ "v5", "plain literal" ]
178
+ })
179
179
  },
180
180
  }.each_pair do |title, params|
181
- it title do
182
- jld = JSON::LD::API.compact(params[:input], params[:context], logger: logger)
183
- expect(jld).to produce(params[:output], logger)
184
- end
181
+ it(title) {run_compact(params)}
185
182
  end
186
183
 
187
184
  context "keyword aliasing" do
188
185
  {
189
186
  "@id" => {
190
- input: {
191
- "@id" => "",
192
- "@type" => "#{RDF::RDFS.Resource}"
193
- },
194
- context: {"id" => "@id"},
195
- output: {
196
- "@context" => {"id" => "@id"},
197
- "id" => "",
198
- "@type" => "#{RDF::RDFS.Resource}"
199
- }
200
- },
201
- "@type" => {
202
- input: {
203
- "@type" => RDF::RDFS.Resource.to_s,
204
- "http://example.org/foo" => {"@value" => "bar", "@type" => "http://example.com/type"}
205
- },
206
- context: {"type" => "@type"},
207
- output: {
208
- "@context" => {"type" => "@type"},
209
- "type" => RDF::RDFS.Resource.to_s,
210
- "http://example.org/foo" => {"@value" => "bar", "type" => "http://example.com/type"}
211
- }
187
+ input: %({
188
+ "@id": "",
189
+ "@type": "#{RDF::RDFS.Resource}"
190
+ }),
191
+ context: %({"id": "@id"}),
192
+ output: %({
193
+ "@context": {"id": "@id"},
194
+ "id": "",
195
+ "@type": "#{RDF::RDFS.Resource}"
196
+ })
197
+ },
198
+ "@type": {
199
+ input: %({
200
+ "@type": "http://www.w3.org/2000/01/rdf-schema#Resource",
201
+ "http://example.org/foo": {"@value": "bar", "@type": "http://example.com/type"}
202
+ }),
203
+ context: %({"type": "@type"}),
204
+ output: %({
205
+ "@context": {"type": "@type"},
206
+ "type": "http://www.w3.org/2000/01/rdf-schema#Resource",
207
+ "http://example.org/foo": {"@value": "bar", "type": "http://example.com/type"}
208
+ })
212
209
  },
213
210
  "@language" => {
214
- input: {
215
- "http://example.org/foo" => {"@value" => "bar", "@language" => "baz"}
216
- },
217
- context: {"language" => "@language"},
218
- output: {
219
- "@context" => {"language" => "@language"},
220
- "http://example.org/foo" => {"@value" => "bar", "language" => "baz"}
221
- }
211
+ input: %({
212
+ "http://example.org/foo": {"@value": "bar", "@language": "baz"}
213
+ }),
214
+ context: %({"language": "@language"}),
215
+ output: %({
216
+ "@context": {"language": "@language"},
217
+ "http://example.org/foo": {"@value": "bar", "language": "baz"}
218
+ })
222
219
  },
223
220
  "@value" => {
224
- input: {
225
- "http://example.org/foo" => {"@value" => "bar", "@language" => "baz"}
226
- },
227
- context: {"literal" => "@value"},
228
- output: {
229
- "@context" => {"literal" => "@value"},
230
- "http://example.org/foo" => {"literal" => "bar", "@language" => "baz"}
231
- }
221
+ input: %({
222
+ "http://example.org/foo": {"@value": "bar", "@language": "baz"}
223
+ }),
224
+ context: %({"literal": "@value"}),
225
+ output: %({
226
+ "@context": {"literal": "@value"},
227
+ "http://example.org/foo": {"literal": "bar", "@language": "baz"}
228
+ })
232
229
  },
233
230
  "@list" => {
234
- input: {
235
- "http://example.org/foo" => {"@list" => ["bar"]}
236
- },
237
- context: {"list" => "@list"},
238
- output: {
239
- "@context" => {"list" => "@list"},
240
- "http://example.org/foo" => {"list" => ["bar"]}
241
- }
231
+ input: %({
232
+ "http://example.org/foo": {"@list": ["bar"]}
233
+ }),
234
+ context: %({"list": "@list"}),
235
+ output: %({
236
+ "@context": {"list": "@list"},
237
+ "http://example.org/foo": {"list": ["bar"]}
238
+ })
242
239
  },
243
240
  }.each do |title, params|
244
- it title do
245
- jld = JSON::LD::API.compact(params[:input], params[:context], logger: logger)
246
- expect(jld).to produce(params[:output], logger)
247
- end
241
+ it(title) {run_compact(params)}
248
242
  end
249
243
  end
250
244
 
251
245
  context "term selection" do
252
246
  {
253
- "Uses term with nil language when two terms conflict on language" => {
254
- input: [{
255
- "http://example.com/term" => {"@value" => "v1"}
256
- }],
257
- context: {
258
- "term5" => {"@id" => "http://example.com/term","@language" => nil},
259
- "@language" => "de"
260
- },
261
- output: {
262
- "@context" => {
263
- "term5" => {"@id" => "http://example.com/term","@language" => nil},
264
- "@language" => "de"
247
+ "Uses term with null language when two terms conflict on language" => {
248
+ input: %([{
249
+ "http://example.com/term": {"@value": "v1"}
250
+ }]),
251
+ context: %({
252
+ "term5": {"@id": "http://example.com/term","@language": null},
253
+ "@language": "de"
254
+ }),
255
+ output: %({
256
+ "@context": {
257
+ "term5": {"@id": "http://example.com/term","@language": null},
258
+ "@language": "de"
265
259
  },
266
- "term5" => "v1",
267
- }
260
+ "term5": "v1"
261
+ })
268
262
  },
269
263
  "Uses subject alias" => {
270
- input: [{
271
- "@id" => "http://example.com/id1",
272
- "http://example.com/id1" => {"@value" => "foo", "@language" => "de"}
273
- }],
274
- context: {
275
- "id1" => "http://example.com/id1",
276
- "@language" => "de"
277
- },
278
- output: {
279
- "@context" => {
280
- "id1" => "http://example.com/id1",
281
- "@language" => "de"
264
+ input: %([{
265
+ "@id": "http://example.com/id1",
266
+ "http://example.com/id1": {"@value": "foo", "@language": "de"}
267
+ }]),
268
+ context: %({
269
+ "id1": "http://example.com/id1",
270
+ "@language": "de"
271
+ }),
272
+ output: %({
273
+ "@context": {
274
+ "id1": "http://example.com/id1",
275
+ "@language": "de"
282
276
  },
283
- "@id" => "http://example.com/id1",
284
- "id1" => "foo"
285
- }
277
+ "@id": "http://example.com/id1",
278
+ "id1": "foo"
279
+ })
286
280
  },
287
281
  "compact-0007" => {
288
- input: ::JSON.parse(%(
282
+ input: %(
289
283
  {"http://example.org/vocab#contains": "this-is-not-an-IRI"}
290
- )),
291
- context: ::JSON.parse(%({
284
+ ),
285
+ context: %({
292
286
  "ex": "http://example.org/vocab#",
293
287
  "ex:contains": {"@type": "@id"}
294
- })),
295
- output: ::JSON.parse(%({
288
+ }),
289
+ output: %({
296
290
  "@context": {
297
291
  "ex": "http://example.org/vocab#",
298
292
  "ex:contains": {"@type": "@id"}
299
293
  },
300
294
  "http://example.org/vocab#contains": "this-is-not-an-IRI"
301
- }))
295
+ })
302
296
  }
303
297
  }.each_pair do |title, params|
304
- it title do
305
- input = params[:input].is_a?(String) ? JSON.parse(params[:input]) : params[:input]
306
- ctx = params[:context].is_a?(String) ? JSON.parse(params[:context]) : params[:context]
307
- output = params[:output].is_a?(String) ? JSON.parse(params[:output]) : params[:output]
308
- jld = JSON::LD::API.compact(input, ctx, logger: logger)
309
- expect(jld).to produce(output, logger)
310
- end
298
+ it(title) {run_compact(params)}
311
299
  end
312
300
  end
313
301
 
314
- context "@reverse" do
302
+ context "@container: @reverse" do
315
303
  {
304
+ "@container: @reverse" => {
305
+ input: %([{
306
+ "@id": "http://example/one",
307
+ "@reverse": {
308
+ "http://example/forward": [
309
+ {
310
+ "@id": "http://example/two"
311
+ }
312
+ ]
313
+ }
314
+ }]),
315
+ context: %({
316
+ "@vocab": "http://example/",
317
+ "rev": { "@reverse": "forward", "@type": "@id"}
318
+ }),
319
+ output: %({
320
+ "@context": {
321
+ "@vocab": "http://example/",
322
+ "rev": { "@reverse": "forward", "@type": "@id"}
323
+ },
324
+ "@id": "http://example/one",
325
+ "rev": "http://example/two"
326
+ })
327
+ },
316
328
  "compact-0033" => {
317
329
  input: %([
318
330
  {
@@ -348,32 +360,28 @@ describe JSON::LD::API do
348
360
  })
349
361
  }
350
362
  }.each_pair do |title, params|
351
- it title do
352
- input = params[:input].is_a?(String) ? JSON.parse(params[:input]) : params[:input]
353
- ctx = params[:context].is_a?(String) ? JSON.parse(params[:context]) : params[:context]
354
- output = params[:output].is_a?(String) ? JSON.parse(params[:output]) : params[:output]
355
- jld = JSON::LD::API.compact(input, ctx, logger: logger)
356
- expect(jld).to produce(output, logger)
357
- end
363
+ it(title) {run_compact(params)}
358
364
  end
359
365
  end
360
366
 
361
367
  context "context as value" do
362
- it "includes the context in the output document" do
363
- ctx = {
364
- "foo" => "http://example.com/"
365
- }
366
- input = {
367
- "http://example.com/" => "bar"
368
- }
369
- expected = {
370
- "@context" => {
371
- "foo" => "http://example.com/"
372
- },
373
- "foo" => "bar"
368
+ {
369
+ "includes the context in the output document" => {
370
+ input: %({
371
+ "http://example.com/": "bar"
372
+ }),
373
+ context: %({
374
+ "foo": "http://example.com/"
375
+ }),
376
+ output: %({
377
+ "@context": {
378
+ "foo": "http://example.com/"
379
+ },
380
+ "foo": "bar"
381
+ })
374
382
  }
375
- jld = JSON::LD::API.compact(input, ctx, logger: logger, validate: true)
376
- expect(jld).to produce(expected, logger)
383
+ }.each_pair do |title, params|
384
+ it(title) {run_compact(params)}
377
385
  end
378
386
  end
379
387
 
@@ -382,13 +390,13 @@ describe JSON::LD::API do
382
390
  JSON::LD::API::RemoteDocument.new("http://example.com/context", %q({"@context": {"b": "http://example.com/b"}}))
383
391
  end
384
392
  it "uses referenced context" do
385
- input = {
386
- "http://example.com/b" => "c"
387
- }
388
- expected = {
389
- "@context" => "http://example.com/context",
390
- "b" => "c"
391
- }
393
+ input = ::JSON.parse %({
394
+ "http://example.com/b": "c"
395
+ })
396
+ expected = ::JSON.parse %({
397
+ "@context": "http://example.com/context",
398
+ "b": "c"
399
+ })
392
400
  allow(JSON::LD::API).to receive(:documentLoader).with("http://example.com/context", anything).and_yield(remote_doc)
393
401
  jld = JSON::LD::API.compact(input, "http://example.com/context", logger: logger, validate: true)
394
402
  expect(jld).to produce(expected, logger)
@@ -398,114 +406,885 @@ describe JSON::LD::API do
398
406
  context "@list" do
399
407
  {
400
408
  "1 term 2 lists 2 languages" => {
401
- input: [{
402
- "http://example.com/foo" => [
403
- {"@list" => [{"@value" => "en", "@language" => "en"}]},
404
- {"@list" => [{"@value" => "de", "@language" => "de"}]}
409
+ input: %([{
410
+ "http://example.com/foo": [
411
+ {"@list": [{"@value": "en", "@language": "en"}]},
412
+ {"@list": [{"@value": "de", "@language": "de"}]}
405
413
  ]
406
- }],
407
- context: {
408
- "foo_en" => {"@id" => "http://example.com/foo", "@container" => "@list", "@language" => "en"},
409
- "foo_de" => {"@id" => "http://example.com/foo", "@container" => "@list", "@language" => "de"}
410
- },
411
- output: {
412
- "@context" => {
413
- "foo_en" => {"@id" => "http://example.com/foo", "@container" => "@list", "@language" => "en"},
414
- "foo_de" => {"@id" => "http://example.com/foo", "@container" => "@list", "@language" => "de"}
414
+ }]),
415
+ context: %({
416
+ "foo_en": {"@id": "http://example.com/foo", "@container": "@list", "@language": "en"},
417
+ "foo_de": {"@id": "http://example.com/foo", "@container": "@list", "@language": "de"}
418
+ }),
419
+ output: %({
420
+ "@context": {
421
+ "foo_en": {"@id": "http://example.com/foo", "@container": "@list", "@language": "en"},
422
+ "foo_de": {"@id": "http://example.com/foo", "@container": "@list", "@language": "de"}
415
423
  },
416
- "foo_en" => ["en"],
417
- "foo_de" => ["de"]
418
- }
424
+ "foo_en": ["en"],
425
+ "foo_de": ["de"]
426
+ })
419
427
  },
420
428
  }.each_pair do |title, params|
421
- it title do
422
- jld = JSON::LD::API.compact(params[:input], params[:context], logger: logger)
423
- expect(jld).to produce(params[:output], logger)
424
- end
429
+ it(title) {run_compact(params)}
425
430
  end
426
431
  end
427
432
 
428
- context "language maps" do
433
+ context "@container: @index" do
434
+ {
435
+ "compact-0029" => {
436
+ input: %([{
437
+ "@id": "http://example.com/article",
438
+ "http://example.com/vocab/author": [{
439
+ "@id": "http://example.org/person/1",
440
+ "@index": "regular"
441
+ }, {
442
+ "@id": "http://example.org/guest/cd24f329aa",
443
+ "@index": "guest"
444
+ }]
445
+ }]),
446
+ context: %({
447
+ "author": {"@id": "http://example.com/vocab/author", "@container": "@index" }
448
+ }),
449
+ output: %({
450
+ "@context": {
451
+ "author": {
452
+ "@id": "http://example.com/vocab/author",
453
+ "@container": "@index"
454
+ }
455
+ },
456
+ "@id": "http://example.com/article",
457
+ "author": {
458
+ "regular": {
459
+ "@id": "http://example.org/person/1"
460
+ },
461
+ "guest": {
462
+ "@id": "http://example.org/guest/cd24f329aa"
463
+ }
464
+ }
465
+ })
466
+ },
467
+ }.each_pair do |title, params|
468
+ it(title) {run_compact(params)}
469
+ end
470
+ end
471
+
472
+ context "@container: @language" do
429
473
  {
430
474
  "compact-0024" => {
431
- input: [
475
+ input: %([
432
476
  {
433
- "@id" => "http://example.com/queen",
434
- "http://example.com/vocab/label" => [
435
- {"@value" => "The Queen", "@language" => "en"},
436
- {"@value" => "Die Königin", "@language" => "de"},
437
- {"@value" => "Ihre Majestät", "@language" => "de"}
477
+ "@id": "http://example.com/queen",
478
+ "http://example.com/vocab/label": [
479
+ {"@value": "The Queen", "@language": "en"},
480
+ {"@value": "Die Königin", "@language": "de"},
481
+ {"@value": "Ihre Majestät", "@language": "de"}
438
482
  ]
439
483
  }
440
- ],
441
- context: {
442
- "vocab" => "http://example.com/vocab/",
443
- "label" => {"@id" => "vocab:label", "@container" => "@language"}
444
- },
445
- output: {
446
- "@context" => {
447
- "vocab" => "http://example.com/vocab/",
448
- "label" => {"@id" => "vocab:label", "@container" => "@language"}
484
+ ]),
485
+ context: %({
486
+ "vocab": "http://example.com/vocab/",
487
+ "label": {"@id": "vocab:label", "@container": "@language"}
488
+ }),
489
+ output: %({
490
+ "@context": {
491
+ "vocab": "http://example.com/vocab/",
492
+ "label": {"@id": "vocab:label", "@container": "@language"}
493
+ },
494
+ "@id": "http://example.com/queen",
495
+ "label": {
496
+ "en": "The Queen",
497
+ "de": ["Die Königin", "Ihre Majestät"]
498
+ }
499
+ })
500
+ },
501
+ }.each_pair do |title, params|
502
+ it(title) {run_compact(params)}
503
+ end
504
+ end
505
+
506
+ context "@container: @id" do
507
+ {
508
+ "Indexes to object not having an @id" => {
509
+ input: %([{
510
+ "http://example/idmap": [
511
+ {"http://example/label": [{"@value": "Object with @id _:bar"}], "@id": "_:bar"},
512
+ {"http://example/label": [{"@value": "Object with @id <foo>"}], "@id": "http://example.org/foo"}
513
+ ]
514
+ }]),
515
+ context: %({
516
+ "@vocab": "http://example/",
517
+ "idmap": {"@container": "@id"}
518
+ }),
519
+ output: %({
520
+ "@context": {
521
+ "@vocab": "http://example/",
522
+ "idmap": {"@container": "@id"}
523
+ },
524
+ "idmap": {
525
+ "http://example.org/foo": {"label": "Object with @id <foo>"},
526
+ "_:bar": {"label": "Object with @id _:bar"}
527
+ }
528
+ }),
529
+ },
530
+ "Indexes to object already having an @id" => {
531
+ input: %([{
532
+ "http://example/idmap": [
533
+ {"@id": "_:foo", "http://example/label": [{"@value": "Object with @id _:bar"}]},
534
+ {"@id": "http://example.org/bar", "http://example/label": [{"@value": "Object with @id <foo>"}]}
535
+ ]
536
+ }]),
537
+ context: %({
538
+ "@vocab": "http://example/",
539
+ "idmap": {"@container": "@id"}
540
+ }),
541
+ output: %({
542
+ "@context": {
543
+ "@vocab": "http://example/",
544
+ "idmap": {"@container": "@id"}
545
+ },
546
+ "idmap": {
547
+ "_:foo": {"label": "Object with @id _:bar"},
548
+ "http://example.org/bar": {"label": "Object with @id <foo>"}
549
+ }
550
+ }),
551
+ },
552
+ "Indexes to object using compact IRI @id" => {
553
+ input: %([{
554
+ "http://example/idmap": [
555
+ {"http://example/label": [{"@value": "Object with @id <foo>"}], "@id": "http://example.org/foo"}
556
+ ]
557
+ }]),
558
+ context: %({
559
+ "@vocab": "http://example/",
560
+ "ex": "http://example.org/",
561
+ "idmap": {"@container": "@id"}
562
+ }),
563
+ output: %({
564
+ "@context": {
565
+ "@vocab": "http://example/",
566
+ "ex": "http://example.org/",
567
+ "idmap": {"@container": "@id"}
568
+ },
569
+ "idmap": {
570
+ "ex:foo": {"label": "Object with @id <foo>"}
571
+ }
572
+ })
573
+ },
574
+ }.each_pair do |title, params|
575
+ it(title) {run_compact({processingMode: "json-ld-1.1"}.merge(params))}
576
+ end
577
+ end
578
+
579
+ context "@container: @type" do
580
+ {
581
+ "Indexes to object not having an @type" => {
582
+ input: %([{
583
+ "http://example/typemap": [
584
+ {"http://example/label": [{"@value": "Object with @type _:bar"}], "@type": ["_:bar"]},
585
+ {"http://example/label": [{"@value": "Object with @type <foo>"}], "@type": ["http://example.org/foo"]}
586
+ ]
587
+ }]),
588
+ context: %({
589
+ "@vocab": "http://example/",
590
+ "typemap": {"@container": "@type"}
591
+ }),
592
+ output: %({
593
+ "@context": {
594
+ "@vocab": "http://example/",
595
+ "typemap": {"@container": "@type"}
596
+ },
597
+ "typemap": {
598
+ "http://example.org/foo": {"label": "Object with @type <foo>"},
599
+ "_:bar": {"label": "Object with @type _:bar"}
600
+ }
601
+ })
602
+ },
603
+ "Indexes to object already having an @type" => {
604
+ input: %([{
605
+ "http://example/typemap": [
606
+ {
607
+ "@type": ["_:bar", "_:foo"],
608
+ "http://example/label": [{"@value": "Object with @type _:bar"}]
609
+ },
610
+ {
611
+ "@type": ["http://example.org/foo", "http://example.org/bar"],
612
+ "http://example/label": [{"@value": "Object with @type <foo>"}]
613
+ }
614
+ ]
615
+ }]),
616
+ context: %({
617
+ "@vocab": "http://example/",
618
+ "typemap": {"@container": "@type"}
619
+ }),
620
+ output: %({
621
+ "@context": {
622
+ "@vocab": "http://example/",
623
+ "typemap": {"@container": "@type"}
624
+ },
625
+ "typemap": {
626
+ "http://example.org/foo": {"@type": "http://example.org/bar", "label": "Object with @type <foo>"},
627
+ "_:bar": {"@type": "_:foo", "label": "Object with @type _:bar"}
628
+ }
629
+ })
630
+ },
631
+ "Indexes to object already having multiple @type values" => {
632
+ input: %([{
633
+ "http://example/typemap": [
634
+ {
635
+ "@type": ["_:bar", "_:foo", "_:baz"],
636
+ "http://example/label": [{"@value": "Object with @type _:bar"}]
637
+ },
638
+ {
639
+ "@type": ["http://example.org/foo", "http://example.org/bar", "http://example.org/baz"],
640
+ "http://example/label": [{"@value": "Object with @type <foo>"}]
641
+ }
642
+ ]
643
+ }]),
644
+ context: %({
645
+ "@vocab": "http://example/",
646
+ "typemap": {"@container": "@type"}
647
+ }),
648
+ output: %({
649
+ "@context": {
650
+ "@vocab": "http://example/",
651
+ "typemap": {"@container": "@type"}
652
+ },
653
+ "typemap": {
654
+ "http://example.org/foo": {"@type": ["http://example.org/bar", "http://example.org/baz"], "label": "Object with @type <foo>"},
655
+ "_:bar": {"@type": ["_:foo", "_:baz"], "label": "Object with @type _:bar"}
656
+ }
657
+ })
658
+ },
659
+ "Indexes using compacted @type" => {
660
+ input: %([{
661
+ "http://example/typemap": [
662
+ {"http://example/label": [{"@value": "Object with @type <foo>"}], "@type": ["http://example/Foo"]}
663
+ ]
664
+ }]),
665
+ context: %({
666
+ "@vocab": "http://example/",
667
+ "typemap": {"@container": "@type"}
668
+ }),
669
+ output: %({
670
+ "@context": {
671
+ "@vocab": "http://example/",
672
+ "typemap": {"@container": "@type"}
673
+ },
674
+ "typemap": {
675
+ "Foo": {"label": "Object with @type <foo>"}
676
+ }
677
+ })
678
+ },
679
+ }.each_pair do |title, params|
680
+ it(title) {run_compact({processingMode: "json-ld-1.1"}.merge(params))}
681
+ end
682
+ end
683
+
684
+ context "@nest" do
685
+ {
686
+ "Indexes to @nest for property with @container: @nest" => {
687
+ input: %([{
688
+ "http://example.org/p1": [{"@value": "v1"}],
689
+ "http://example.org/p2": [{"@value": "v2"}]
690
+ }]),
691
+ context: %({
692
+ "@vocab": "http://example.org/",
693
+ "p2": {"@nest": "@nest"}
694
+ }),
695
+ output: %({
696
+ "@context": {
697
+ "@vocab": "http://example.org/",
698
+ "p2": {"@nest": "@nest"}
699
+ },
700
+ "p1": "v1",
701
+ "@nest": {
702
+ "p2": "v2"
703
+ }
704
+ })
705
+ },
706
+ "Indexes to @nest for all properties with @container: @nest" => {
707
+ input: %([{
708
+ "http://example.org/p1": [{"@value": "v1"}],
709
+ "http://example.org/p2": [{"@value": "v2"}]
710
+ }]),
711
+ context: %({
712
+ "@vocab": "http://example.org/",
713
+ "p1": {"@nest": "@nest"},
714
+ "p2": {"@nest": "@nest"}
715
+ }),
716
+ output: %({
717
+ "@context": {
718
+ "@vocab": "http://example.org/",
719
+ "p1": {"@nest": "@nest"},
720
+ "p2": {"@nest": "@nest"}
721
+ },
722
+ "@nest": {
723
+ "p1": "v1",
724
+ "p2": "v2"
725
+ }
726
+ })
727
+ },
728
+ "Nests using alias of @nest" => {
729
+ input: %([{
730
+ "http://example.org/p1": [{"@value": "v1"}],
731
+ "http://example.org/p2": [{"@value": "v2"}]
732
+ }]),
733
+ context: %({
734
+ "@vocab": "http://example.org/",
735
+ "nest": "@nest",
736
+ "p2": {"@nest": "nest"}
737
+ }),
738
+ output: %({
739
+ "@context": {
740
+ "@vocab": "http://example.org/",
741
+ "nest": "@nest",
742
+ "p2": {"@nest": "nest"}
743
+ },
744
+ "p1": "v1",
745
+ "nest": {
746
+ "p2": "v2"
747
+ }
748
+ })
749
+ },
750
+ "Arrays of nested values" => {
751
+ input: %([{
752
+ "http://example.org/p1": [{"@value": "v1"}],
753
+ "http://example.org/p2": [{"@value": "v2"}, {"@value": "v3"}]
754
+ }]),
755
+ context: %({
756
+ "@vocab": "http://example.org/",
757
+ "p2": {"@nest": "@nest"}
758
+ }),
759
+ output: %({
760
+ "@context": {
761
+ "@vocab": "http://example.org/",
762
+ "p2": {"@nest": "@nest"}
763
+ },
764
+ "p1": "v1",
765
+ "@nest": {
766
+ "p2": ["v2", "v3"]
767
+ }
768
+ })
769
+ },
770
+ "Nested @container: @list" => {
771
+ input: %([{
772
+ "http://example.org/list": [{"@list": [
773
+ {"@value": "a"},
774
+ {"@value": "b"}
775
+ ]}]
776
+ }]),
777
+ context: %({
778
+ "@vocab": "http://example.org/",
779
+ "list": {"@container": "@list", "@nest": "nestedlist"},
780
+ "nestedlist": "@nest"
781
+ }),
782
+ output: %({
783
+ "@context": {
784
+ "@vocab": "http://example.org/",
785
+ "list": {"@container": "@list", "@nest": "nestedlist"},
786
+ "nestedlist": "@nest"
449
787
  },
450
- "@id" => "http://example.com/queen",
451
- "label" => {
452
- "en" => "The Queen",
453
- "de" => ["Die Königin", "Ihre Majestät"]
788
+ "nestedlist": {
789
+ "list": ["a", "b"]
454
790
  }
455
- }
791
+ }),
792
+ },
793
+ "Nested @container: @index" => {
794
+ input: %([{
795
+ "http://example.org/index": [
796
+ {"@value": "a", "@index": "A"},
797
+ {"@value": "b", "@index": "B"}
798
+ ]
799
+ }]),
800
+ context: %({
801
+ "@vocab": "http://example.org/",
802
+ "index": {"@container": "@index", "@nest": "nestedindex"},
803
+ "nestedindex": "@nest"
804
+ }),
805
+ output: %({
806
+ "@context": {
807
+ "@vocab": "http://example.org/",
808
+ "index": {"@container": "@index", "@nest": "nestedindex"},
809
+ "nestedindex": "@nest"
810
+ },
811
+ "nestedindex": {
812
+ "index": {
813
+ "A": "a",
814
+ "B": "b"
815
+ }
816
+ }
817
+ }),
818
+ },
819
+ "Nested @container: @language" => {
820
+ input: %([{
821
+ "http://example.org/container": [
822
+ {"@value": "Die Königin", "@language": "de"},
823
+ {"@value": "The Queen", "@language": "en"}
824
+ ]
825
+ }]),
826
+ context: %({
827
+ "@vocab": "http://example.org/",
828
+ "container": {"@container": "@language", "@nest": "nestedlanguage"},
829
+ "nestedlanguage": "@nest"
830
+ }),
831
+ output: %({
832
+ "@context": {
833
+ "@vocab": "http://example.org/",
834
+ "container": {"@container": "@language", "@nest": "nestedlanguage"},
835
+ "nestedlanguage": "@nest"
836
+ },
837
+ "nestedlanguage": {
838
+ "container": {
839
+ "en": "The Queen",
840
+ "de": "Die Königin"
841
+ }
842
+ }
843
+ })
844
+ },
845
+ "Nested @container: @type" => {
846
+ input: %([{
847
+ "http://example/typemap": [
848
+ {"http://example/label": [{"@value": "Object with @type _:bar"}], "@type": ["_:bar"]},
849
+ {"http://example/label": [{"@value": "Object with @type <foo>"}], "@type": ["http://example.org/foo"]}
850
+ ]
851
+ }]),
852
+ context: %({
853
+ "@vocab": "http://example/",
854
+ "typemap": {"@container": "@type", "@nest": "nestedtypemap"},
855
+ "nestedtypemap": "@nest"
856
+ }),
857
+ output: %({
858
+ "@context": {
859
+ "@vocab": "http://example/",
860
+ "typemap": {"@container": "@type", "@nest": "nestedtypemap"},
861
+ "nestedtypemap": "@nest"
862
+ },
863
+ "nestedtypemap": {
864
+ "typemap": {
865
+ "_:bar": {"label": "Object with @type _:bar"},
866
+ "http://example.org/foo": {"label": "Object with @type <foo>"}
867
+ }
868
+ }
869
+ })
870
+ },
871
+ "Nested @container: @id" => {
872
+ input: %([{
873
+ "http://example/idmap": [
874
+ {"http://example/label": [{"@value": "Object with @id _:bar"}], "@id": "_:bar"},
875
+ {"http://example/label": [{"@value": "Object with @id <foo>"}], "@id": "http://example.org/foo"}
876
+ ]
877
+ }]),
878
+ context: %({
879
+ "@vocab": "http://example/",
880
+ "idmap": {"@container": "@id", "@nest": "nestedidmap"},
881
+ "nestedidmap": "@nest"
882
+ }),
883
+ output: %({
884
+ "@context": {
885
+ "@vocab": "http://example/",
886
+ "idmap": {"@container": "@id", "@nest": "nestedidmap"},
887
+ "nestedidmap": "@nest"
888
+ },
889
+ "nestedidmap": {
890
+ "idmap": {
891
+ "http://example.org/foo": {"label": "Object with @id <foo>"},
892
+ "_:bar": {"label": "Object with @id _:bar"}
893
+ }
894
+ }
895
+ })
896
+ },
897
+ "Multiple nest aliases" => {
898
+ input: %({
899
+ "http://example.org/foo": "bar",
900
+ "http://example.org/bar": "foo"
901
+ }),
902
+ context: %({
903
+ "@vocab": "http://example.org/",
904
+ "foonest": "@nest",
905
+ "barnest": "@nest",
906
+ "foo": {"@nest": "foonest"},
907
+ "bar": {"@nest": "barnest"}
908
+ }),
909
+ output: %({
910
+ "@context": {
911
+ "@vocab": "http://example.org/",
912
+ "foonest": "@nest",
913
+ "barnest": "@nest",
914
+ "foo": {"@nest": "foonest"},
915
+ "bar": {"@nest": "barnest"}
916
+ },
917
+ "barnest": {"bar": "foo"},
918
+ "foonest": {"foo": "bar"}
919
+ })
920
+ },
921
+ "Nest term not defined" => {
922
+ input: %({
923
+ "http://example/foo": "bar"
924
+ }),
925
+ context: %({
926
+ "term": {"@id": "http://example/foo", "@nest": "unknown"}
927
+ }),
928
+ exception: JSON::LD::JsonLdError::InvalidNestValue
456
929
  },
457
930
  }.each_pair do |title, params|
458
- it title do
459
- jld = JSON::LD::API.compact(params[:input], params[:context], logger: logger)
460
- expect(jld).to produce(params[:output], logger)
461
- end
931
+ it(title) {run_compact({processingMode: "json-ld-1.1"}.merge(params))}
462
932
  end
463
933
  end
464
934
 
465
935
  context "@graph" do
466
936
  {
467
937
  "Uses @graph given mutliple inputs" => {
468
- input: [
469
- {"http://example.com/foo" => ["foo"]},
470
- {"http://example.com/bar" => ["bar"]}
471
- ],
472
- context: {"ex" => "http://example.com/"},
473
- output: {
474
- "@context" => {"ex" => "http://example.com/"},
475
- "@graph" => [
476
- {"ex:foo" => "foo"},
477
- {"ex:bar" => "bar"}
938
+ input: %([
939
+ {"http://example.com/foo": ["foo"]},
940
+ {"http://example.com/bar": ["bar"]}
941
+ ]),
942
+ context: %({"ex": "http://example.com/"}),
943
+ output: %({
944
+ "@context": {"ex": "http://example.com/"},
945
+ "@graph": [
946
+ {"ex:foo": "foo"},
947
+ {"ex:bar": "bar"}
478
948
  ]
479
- }
949
+ })
480
950
  },
481
951
  }.each_pair do |title, params|
482
- it title do
483
- jld = JSON::LD::API.compact(params[:input], params[:context], logger: logger)
484
- expect(jld).to produce(params[:output], logger)
485
- end
952
+ it(title) {run_compact(params)}
953
+ end
954
+ end
955
+
956
+ context "scoped context" do
957
+ {
958
+ "adding new term" => {
959
+ input: %([{
960
+ "http://example/foo": [{"http://example.org/bar": [{"@value": "baz"}]}]
961
+ }]),
962
+ context: %({
963
+ "@vocab": "http://example/",
964
+ "foo": {"@context": {"bar": "http://example.org/bar"}}
965
+ }),
966
+ output: %({
967
+ "@context": {
968
+ "@vocab": "http://example/",
969
+ "foo": {"@context": {"bar": "http://example.org/bar"}}
970
+ },
971
+ "foo": {
972
+ "bar": "baz"
973
+ }
974
+ })
975
+ },
976
+ "overriding a term" => {
977
+ input: %([
978
+ {
979
+ "http://example/foo": [{"http://example/bar": [{"@id": "http://example/baz"}]}]
980
+ }
981
+ ]),
982
+ context: %({
983
+ "@vocab": "http://example/",
984
+ "foo": {"@context": {"bar": {"@type": "@id"}}},
985
+ "bar": {"@type": "http://www.w3.org/2001/XMLSchema#string"}
986
+ }),
987
+ output: %({
988
+ "@context": {
989
+ "@vocab": "http://example/",
990
+ "foo": {"@context": {"bar": {"@type": "@id"}}},
991
+ "bar": {"@type": "http://www.w3.org/2001/XMLSchema#string"}
992
+ },
993
+ "foo": {
994
+ "bar": "http://example/baz"
995
+ }
996
+ }),
997
+ },
998
+ "property and value with different terms mapping to the same expanded property" => {
999
+ input: %([
1000
+ {
1001
+ "http://example/foo": [{
1002
+ "http://example/bar": [
1003
+ {"@value": "baz"}
1004
+ ]}
1005
+ ]
1006
+ }
1007
+ ]),
1008
+ context: %({
1009
+ "@vocab": "http://example/",
1010
+ "foo": {"@context": {"Bar": {"@id": "bar"}}}
1011
+ }),
1012
+ output: %({
1013
+ "@context": {
1014
+ "@vocab": "http://example/",
1015
+ "foo": {"@context": {"Bar": {"@id": "bar"}}}
1016
+ },
1017
+ "foo": {
1018
+ "Bar": "baz"
1019
+ }
1020
+ }),
1021
+ },
1022
+ "deep @context affects nested nodes" => {
1023
+ input: %([
1024
+ {
1025
+ "http://example/foo": [{
1026
+ "http://example/bar": [{
1027
+ "http://example/baz": [{"@id": "http://example/buzz"}]
1028
+ }]
1029
+ }]
1030
+ }
1031
+ ]),
1032
+ context: %({
1033
+ "@vocab": "http://example/",
1034
+ "foo": {"@context": {"baz": {"@type": "@vocab"}}}
1035
+ }),
1036
+ output: %({
1037
+ "@context": {
1038
+ "@vocab": "http://example/",
1039
+ "foo": {"@context": {"baz": {"@type": "@vocab"}}}
1040
+ },
1041
+ "foo": {
1042
+ "bar": {
1043
+ "baz": "buzz"
1044
+ }
1045
+ }
1046
+ }),
1047
+ },
1048
+ "scoped context layers on intemediate contexts" => {
1049
+ input: %([{
1050
+ "http://example/a": [{
1051
+ "http://example.com/c": [{"@value": "C in example.com"}],
1052
+ "http://example/b": [{
1053
+ "http://example.com/a": [{"@value": "A in example.com"}],
1054
+ "http://example.org/c": [{"@value": "C in example.org"}]
1055
+ }]
1056
+ }],
1057
+ "http://example/c": [{"@value": "C in example"}]
1058
+ }]),
1059
+ context: %({
1060
+ "@vocab": "http://example/",
1061
+ "b": {"@context": {"c": "http://example.org/c"}}
1062
+ }),
1063
+ output: %({
1064
+ "@context": {
1065
+ "@vocab": "http://example/",
1066
+ "b": {"@context": {"c": "http://example.org/c"}}
1067
+ },
1068
+ "a": {
1069
+ "b": {
1070
+ "c": "C in example.org",
1071
+ "http://example.com/a": "A in example.com"
1072
+ },
1073
+ "http://example.com/c": "C in example.com"
1074
+ },
1075
+ "c": "C in example"
1076
+ }),
1077
+ },
1078
+ "Raises InvalidTermDefinition if processingMode is not specified" => {
1079
+ input: %([{
1080
+ "http://example/foo": [{"http://example.org/bar": [{"@value": "baz"}]}]
1081
+ }]),
1082
+ context: %({
1083
+ "@vocab": "http://example/",
1084
+ "foo": {"@context": {"bar": "http://example.org/bar"}}
1085
+ }),
1086
+ processingMode: nil,
1087
+ exception: JSON::LD::JsonLdError::InvalidTermDefinition
1088
+ },
1089
+ }.each_pair do |title, params|
1090
+ it(title) {run_compact({processingMode: "json-ld-1.1"}.merge(params))}
1091
+ end
1092
+ end
1093
+
1094
+ context "scoped context on @type" do
1095
+ {
1096
+ "adding new term" => {
1097
+ input: %([
1098
+ {
1099
+ "http://example/a": [{
1100
+ "@type": ["http://example/Foo"],
1101
+ "http://example.org/bar": [{"@value": "baz"}]
1102
+ }]
1103
+ }
1104
+ ]),
1105
+ context: %({
1106
+ "@vocab": "http://example/",
1107
+ "Foo": {"@context": {"bar": "http://example.org/bar"}}
1108
+ }),
1109
+ output: %({
1110
+ "@context": {
1111
+ "@vocab": "http://example/",
1112
+ "Foo": {"@context": {"bar": "http://example.org/bar"}}
1113
+ },
1114
+ "a": {"@type": "Foo", "bar": "baz"}
1115
+ })
1116
+ },
1117
+ "overriding a term" => {
1118
+ input: %([
1119
+ {
1120
+ "http://example/a": [{
1121
+ "@type": ["http://example/Foo"],
1122
+ "http://example/bar": [{"@id": "http://example/baz"}]
1123
+ }]
1124
+ }
1125
+ ]),
1126
+ context: %({
1127
+ "@vocab": "http://example/",
1128
+ "Foo": {"@context": {"bar": {"@type": "@id"}}},
1129
+ "bar": {"@type": "http://www.w3.org/2001/XMLSchema#string"}
1130
+ }),
1131
+ output: %({
1132
+ "@context": {
1133
+ "@vocab": "http://example/",
1134
+ "Foo": {"@context": {"bar": {"@type": "@id"}}},
1135
+ "bar": {"@type": "http://www.w3.org/2001/XMLSchema#string"}
1136
+ },
1137
+ "a": {"@type": "Foo", "bar": "http://example/baz"}
1138
+ }),
1139
+ },
1140
+ "alias of @type" => {
1141
+ input: %([
1142
+ {
1143
+ "http://example/a": [{
1144
+ "@type": ["http://example/Foo"],
1145
+ "http://example.org/bar": [{"@value": "baz"}]
1146
+ }]
1147
+ }
1148
+ ]),
1149
+ context: %({
1150
+ "@vocab": "http://example/",
1151
+ "type": "@type",
1152
+ "Foo": {"@context": {"bar": "http://example.org/bar"}}
1153
+ }),
1154
+ output: %({
1155
+ "@context": {
1156
+ "@vocab": "http://example/",
1157
+ "type": "@type",
1158
+ "Foo": {"@context": {"bar": "http://example.org/bar"}}
1159
+ },
1160
+ "a": {"type": "Foo", "bar": "baz"}
1161
+ }),
1162
+ },
1163
+ "deep @context affects nested nodes" => {
1164
+ input: %([
1165
+ {
1166
+ "@type": ["http://example/Foo"],
1167
+ "http://example/bar": [{
1168
+ "http://example/baz": [{"@id": "http://example/buzz"}]
1169
+ }]
1170
+ }
1171
+ ]),
1172
+ context: %({
1173
+ "@vocab": "http://example/",
1174
+ "Foo": {"@context": {"baz": {"@type": "@vocab"}}}
1175
+ }),
1176
+ output: %({
1177
+ "@context": {
1178
+ "@vocab": "http://example/",
1179
+ "Foo": {"@context": {"baz": {"@type": "@vocab"}}}
1180
+ },
1181
+ "@type": "Foo",
1182
+ "bar": {"baz": "buzz"}
1183
+ }),
1184
+ },
1185
+ "scoped context layers on intemediate contexts" => {
1186
+ input: %([{
1187
+ "http://example/a": [{
1188
+ "@type": ["http://example/B"],
1189
+ "http://example.com/a": [{"@value": "A in example.com"}],
1190
+ "http://example.org/c": [{"@value": "C in example.org"}]
1191
+ }],
1192
+ "http://example/c": [{"@value": "C in example"}]
1193
+ }]),
1194
+ context: %({
1195
+ "@vocab": "http://example/",
1196
+ "B": {"@context": {"c": "http://example.org/c"}}
1197
+ }),
1198
+ output: %({
1199
+ "@context": {
1200
+ "@vocab": "http://example/",
1201
+ "B": {"@context": {"c": "http://example.org/c"}}
1202
+ },
1203
+ "a": {
1204
+ "@type": "B",
1205
+ "c": "C in example.org",
1206
+ "http://example.com/a": "A in example.com"
1207
+ },
1208
+ "c": "C in example"
1209
+ }),
1210
+ },
1211
+ "with @container: @type" => {
1212
+ input: %([{
1213
+ "http://example/typemap": [
1214
+ {"http://example.org/a": [{"@value": "Object with @type <Type>"}], "@type": ["http://example/Type"]}
1215
+ ]
1216
+ }]),
1217
+ context: %({
1218
+ "@vocab": "http://example/",
1219
+ "typemap": {"@container": "@type"},
1220
+ "Type": {"@context": {"a": "http://example.org/a"}}
1221
+ }),
1222
+ output: %({
1223
+ "@context": {
1224
+ "@vocab": "http://example/",
1225
+ "typemap": {"@container": "@type"},
1226
+ "Type": {"@context": {"a": "http://example.org/a"}}
1227
+ },
1228
+ "typemap": {
1229
+ "Type": {"a": "Object with @type <Type>"}
1230
+ }
1231
+ })
1232
+ },
1233
+ "Raises InvalidTermDefinition if processingMode is not specified" => {
1234
+ input: %([
1235
+ {
1236
+ "http://example/a": [{
1237
+ "@type": ["http://example/Foo"],
1238
+ "http://example.org/bar": [{"@value": "baz"}]
1239
+ }]
1240
+ }
1241
+ ]),
1242
+ context: %({
1243
+ "@vocab": "http://example/",
1244
+ "Foo": {"@context": {"bar": "http://example.org/bar"}}
1245
+ }),
1246
+ processingMode: nil,
1247
+ exception: JSON::LD::JsonLdError::InvalidTermDefinition
1248
+ },
1249
+ }.each_pair do |title, params|
1250
+ it(title) {run_compact({processingMode: "json-ld-1.1"}.merge(params))}
486
1251
  end
487
1252
  end
488
1253
 
489
1254
  context "exceptions" do
490
1255
  {
491
1256
  "@list containing @list" => {
492
- input: {
493
- "http://example.org/foo" => {"@list" => [{"@list" => ["baz"]}]}
494
- },
1257
+ input: %({
1258
+ "http://example.org/foo": {"@list": [{"@list": ["baz"]}]}
1259
+ }),
1260
+ context: {},
495
1261
  exception: JSON::LD::JsonLdError::ListOfLists
496
1262
  },
497
1263
  "@list containing @list (with coercion)" => {
498
- input: {
499
- "@context" => {"http://example.org/foo" => {"@container" => "@list"}},
500
- "http://example.org/foo" => [{"@list" => ["baz"]}]
501
- },
1264
+ input: %({
1265
+ "@context": {"http://example.org/foo": {"@container": "@list"}},
1266
+ "http://example.org/foo": [{"@list": ["baz"]}]
1267
+ }),
1268
+ context: {},
502
1269
  exception: JSON::LD::JsonLdError::ListOfLists
503
1270
  },
504
1271
  }.each do |title, params|
505
- it title do
506
- expect {JSON::LD::API.compact(params[:input], {})}.to raise_error(params[:exception])
507
- end
1272
+ it(title) {run_compact(params)}
508
1273
  end
509
1274
  end
510
1275
  end
1276
+
1277
+ def run_compact(params)
1278
+ input, output, context, processingMode = params[:input], params[:output], params[:context], params[:processingMode]
1279
+ input = ::JSON.parse(input) if input.is_a?(String)
1280
+ output = ::JSON.parse(output) if output.is_a?(String)
1281
+ context = ::JSON.parse(context) if context.is_a?(String)
1282
+ pending params.fetch(:pending, "test implementation") unless input
1283
+ if params[:exception]
1284
+ expect {JSON::LD::API.compact(input, context, logger: logger, processingMode: processingMode)}.to raise_error(params[:exception])
1285
+ else
1286
+ jld = JSON::LD::API.compact(input, context, logger: logger, processingMode: processingMode)
1287
+ expect(jld).to produce(output, logger)
1288
+ end
1289
+ end
511
1290
  end