json-ld 2.1.2 → 2.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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