json-ld 3.1.1 → 3.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -21,20 +21,21 @@ module JSON::LD
21
21
  # @param [Array, Hash] input
22
22
  # @param [String] active_property
23
23
  # @param [Context] context
24
- # @param [Boolean] ordered (true)
25
- # Ensure output objects have keys ordered properly
26
24
  # @param [Boolean] framing (false)
27
25
  # Special rules for expanding a frame
28
26
  # @param [Boolean] from_map
29
27
  # Expanding from a map, which could be an `@type` map, so don't clear out context term definitions
28
+ #
30
29
  # @return [Array<Hash{String => Object}>]
31
- def expand(input, active_property, context, ordered: false, framing: false, from_map: false)
32
- #log_debug("expand") {"input: #{input.inspect}, active_property: #{active_property.inspect}, context: #{context.inspect}"}
30
+ def expand(input, active_property, context,
31
+ framing: false, from_map: false, log_depth: nil)
32
+ log_debug("expand", depth: log_depth.to_i) {"input: #{input.inspect}, active_property: #{active_property.inspect}, context: #{context.inspect}"}
33
33
  framing = false if active_property == '@default'
34
- expanded_active_property = context.expand_iri(active_property, vocab: true, as_string: true) if active_property
34
+ expanded_active_property = context.expand_iri(active_property, vocab: true, as_string: true, base: @options[:base]) if active_property
35
35
 
36
36
  # Use a term-specific context, if defined, based on the non-type-scoped context.
37
37
  property_scoped_context = context.term_definitions[active_property].context if active_property && context.term_definitions[active_property]
38
+ log_debug("expand", depth: log_depth.to_i) {"property_scoped_context: #{property_scoped_context.inspect}"} unless property_scoped_context.nil?
38
39
 
39
40
  result = case input
40
41
  when Array
@@ -42,7 +43,10 @@ module JSON::LD
42
43
  is_list = context.container(active_property).include?('@list')
43
44
  value = input.each_with_object([]) do |v, memo|
44
45
  # Initialize expanded item to the result of using this algorithm recursively, passing active context, active property, and item as element.
45
- v = expand(v, active_property, context, ordered: ordered, framing: framing, from_map: from_map)
46
+ v = expand(v, active_property, context,
47
+ framing: framing,
48
+ from_map: from_map,
49
+ log_depth: log_depth.to_i + 1)
46
50
 
47
51
  # If the active property is @list or its container mapping is set to @list and v is an array, change it to a list object
48
52
  v = {"@list" => v} if is_list && v.is_a?(Array)
@@ -57,23 +61,29 @@ module JSON::LD
57
61
  value
58
62
  when Hash
59
63
  if context.previous_context
60
- expanded_key_map = input.keys.inject({}) {|memo, key| memo.merge(key => context.expand_iri(key, vocab: true, as_string: true))}
64
+ expanded_key_map = input.keys.inject({}) do |memo, key|
65
+ memo.merge(key => context.expand_iri(key, vocab: true, as_string: true, base: @options[:base]))
66
+ end
61
67
  # Revert any previously type-scoped term definitions, unless this is from a map, a value object or a subject reference
62
68
  revert_context = !from_map &&
63
69
  !expanded_key_map.values.include?('@value') &&
64
70
  !(expanded_key_map.values == ['@id'])
65
71
 
66
72
  # If there's a previous context, the context was type-scoped
73
+ log_debug("expand", depth: log_depth.to_i) {"previous_context: #{context.previous_context.inspect}"} if revert_context
67
74
  context = context.previous_context if revert_context
68
75
  end
69
76
 
70
77
  # Apply property-scoped context after reverting term-scoped context
71
- context = property_scoped_context ? context.parse(property_scoped_context, override_protected: true) : context
78
+ unless property_scoped_context.nil?
79
+ context = context.parse(property_scoped_context, base: @options[:base], override_protected: true)
80
+ end
81
+ log_debug("expand", depth: log_depth.to_i) {"after property_scoped_context: #{context.inspect}"} unless property_scoped_context.nil?
72
82
 
73
83
  # If element contains the key @context, set active context to the result of the Context Processing algorithm, passing active context and the value of the @context key as local context.
74
84
  if input.has_key?('@context')
75
- context = context.parse(input.delete('@context'))
76
- #log_debug("expand") {"context: #{context.inspect}"}
85
+ context = context.parse(input.delete('@context'), base: @options[:base])
86
+ log_debug("expand", depth: log_depth.to_i) {"context: #{context.inspect}"}
77
87
  end
78
88
 
79
89
  # Set the type-scoped context to the context on input, for use later
@@ -84,25 +94,28 @@ module JSON::LD
84
94
  # See if keys mapping to @type have terms with a local context
85
95
  type_key = nil
86
96
  input.keys.sort.
87
- select {|k| context.expand_iri(k, vocab: true, quite: true) == '@type'}.
97
+ select {|k| context.expand_iri(k, vocab: true, base: @options[:base]) == '@type'}.
88
98
  each do |tk|
89
99
 
90
100
  type_key ||= tk # Side effect saves the first found key mapping to @type
91
101
  Array(input[tk]).sort.each do |term|
92
102
  term_context = type_scoped_context.term_definitions[term].context if type_scoped_context.term_definitions[term]
93
- context = term_context ? context.parse(term_context, propagate: false) : context
103
+ unless term_context.nil?
104
+ log_debug("expand", depth: log_depth.to_i) {"term_context: #{term_context.inspect}"}
105
+ context = context.parse(term_context, base: @options[:base], propagate: false)
106
+ end
94
107
  end
95
108
  end
96
109
 
97
110
  # Process each key and value in element. Ignores @nesting content
98
111
  expand_object(input, active_property, context, output_object,
99
112
  expanded_active_property: expanded_active_property,
100
- type_scoped_context: type_scoped_context,
113
+ framing: framing,
101
114
  type_key: type_key,
102
- ordered: ordered,
103
- framing: framing)
115
+ type_scoped_context: type_scoped_context,
116
+ log_depth: log_depth.to_i + 1)
104
117
 
105
- #log_debug("output object") {output_object.inspect}
118
+ log_debug("output object", depth: log_depth.to_i) {output_object.inspect}
106
119
 
107
120
  # If result contains the key @value:
108
121
  if value?(output_object)
@@ -110,13 +123,13 @@ module JSON::LD
110
123
  unless (keys - KEYS_VALUE_LANGUAGE_TYPE_INDEX_DIRECTION).empty?
111
124
  # The result must not contain any keys other than @direction, @value, @language, @type, and @index. It must not contain both the @language key and the @type key. Otherwise, an invalid value object error has been detected and processing is aborted.
112
125
  raise JsonLdError::InvalidValueObject,
113
- "value object has unknown keys: #{output_object.inspect}"
126
+ "value object has unknown keys: #{output_object.inspect}"
114
127
  end
115
128
 
116
129
  if keys.include?('@type') && !(keys & %w(@language @direction)).empty?
117
130
  # @type is inconsistent with either @language or @direction
118
131
  raise JsonLdError::InvalidValueObject,
119
- "value object must not include @type with either @language or @direction: #{output_object.inspect}"
132
+ "value object must not include @type with either @language or @direction: #{output_object.inspect}"
120
133
  end
121
134
 
122
135
  output_object.delete('@language') if output_object.key?('@language') && Array(output_object['@language']).empty?
@@ -133,9 +146,16 @@ module JSON::LD
133
146
  # Otherwise, if the value of result's @value member is not a string and result contains the key @language, an invalid language-tagged value error has been detected (only strings can be language-tagged) and processing is aborted.
134
147
  raise JsonLdError::InvalidLanguageTaggedValue,
135
148
  "when @language is used, @value must be a string: #{output_object.inspect}"
136
- elsif !Array(output_object['@type']).all? {|t|
137
- t.is_a?(String) && RDF::URI(t).valid? && !t.start_with?('_:') ||
138
- t.is_a?(Hash) && t.empty?}
149
+ elsif output_object['@type'] &&
150
+ (!Array(output_object['@type']).all? {|t|
151
+ t.is_a?(String) && RDF::URI(t).valid? && !t.start_with?('_:') ||
152
+ t.is_a?(Hash) && t.empty?} ||
153
+ !framing && !output_object['@type'].is_a?(String))
154
+ # Otherwise, if the result has a @type member and its value is not an IRI, an invalid typed value error has been detected and processing is aborted.
155
+ raise JsonLdError::InvalidTypedValue,
156
+ "value of @type must be an IRI or '@json': #{output_object.inspect}"
157
+ elsif !framing && !output_object.fetch('@type', '').is_a?(String) &&
158
+ RDF::URI(t).valid? && !t.start_with?('_:')
139
159
  # Otherwise, if the result has a @type member and its value is not an IRI, an invalid typed value error has been detected and processing is aborted.
140
160
  raise JsonLdError::InvalidTypedValue,
141
161
  "value of @type must be an IRI or '@json': #{output_object.inspect}"
@@ -161,12 +181,12 @@ module JSON::LD
161
181
  if (expanded_active_property || '@graph') == '@graph' &&
162
182
  (output_object.key?('@value') || output_object.key?('@list') ||
163
183
  (output_object.keys - KEY_ID).empty? && !framing)
164
- #log_debug(" =>") { "empty top-level: " + output_object.inspect}
184
+ log_debug(" =>", depth: log_depth.to_i) { "empty top-level: " + output_object.inspect}
165
185
  return nil
166
186
  end
167
187
 
168
188
  # Re-order result keys if ordering
169
- if ordered
189
+ if @options[:ordered]
170
190
  output_object.keys.sort.each_with_object({}) {|kk, memo| memo[kk] = output_object[kk]}
171
191
  else
172
192
  output_object
@@ -176,12 +196,17 @@ module JSON::LD
176
196
  return nil if input.nil? || active_property.nil? || expanded_active_property == '@graph'
177
197
 
178
198
  # Apply property-scoped context
179
- context = property_scoped_context ? context.parse(property_scoped_context, override_protected: true) : context
199
+ unless property_scoped_context.nil?
200
+ context = context.parse(property_scoped_context,
201
+ base: @options[:base],
202
+ override_protected: true)
203
+ end
204
+ log_debug("expand", depth: log_depth.to_i) {"property_scoped_context: #{context.inspect}"} unless property_scoped_context.nil?
180
205
 
181
- context.expand_value(active_property, input, log_depth: @options[:log_depth])
206
+ context.expand_value(active_property, input, base: @options[:base])
182
207
  end
183
208
 
184
- #log_debug {" => #{result.inspect}"}
209
+ log_debug(depth: log_depth.to_i) {" => #{result.inspect}"}
185
210
  result
186
211
  end
187
212
 
@@ -190,21 +215,21 @@ module JSON::LD
190
215
  # Expand each key and value of element adding them to result
191
216
  def expand_object(input, active_property, context, output_object,
192
217
  expanded_active_property:,
193
- type_scoped_context:,
218
+ framing:,
194
219
  type_key:,
195
- ordered:,
196
- framing:)
220
+ type_scoped_context:,
221
+ log_depth: nil)
197
222
  nests = []
198
223
 
199
224
  input_type = Array(input[type_key]).last
200
- input_type = context.expand_iri(input_type, vocab: true, as_string: true) if input_type
225
+ input_type = context.expand_iri(input_type, vocab: true, as_string: true, base: @options[:base]) if input_type
201
226
 
202
227
  # Then, proceed and process each property and value in element as follows:
203
- keys = ordered ? input.keys.sort : input.keys
228
+ keys = @options[:ordered] ? input.keys.sort : input.keys
204
229
  keys.each do |key|
205
230
  # For each key and value in element, ordered lexicographically by key:
206
231
  value = input[key]
207
- expanded_property = context.expand_iri(key, vocab: true)
232
+ expanded_property = context.expand_iri(key, vocab: true, base: @options[:base])
208
233
 
209
234
  # If expanded property is null or it neither contains a colon (:) nor it is a keyword, drop key by continuing to the next key.
210
235
  next if expanded_property.is_a?(RDF::URI) && expanded_property.relative?
@@ -215,10 +240,10 @@ module JSON::LD
215
240
  expanded_property.to_s.start_with?("_:") &&
216
241
  context.processingMode('json-ld-1.1')
217
242
 
218
- #log_debug("expand property") {"ap: #{active_property.inspect}, expanded: #{expanded_property.inspect}, value: #{value.inspect}"}
243
+ #log_debug("expand property", depth: log_depth.to_i) {"ap: #{active_property.inspect}, expanded: #{expanded_property.inspect}, value: #{value.inspect}"}
219
244
 
220
245
  if expanded_property.nil?
221
- #log_debug(" => ") {"skip nil property"}
246
+ #log_debug(" => ", depth: log_depth.to_i) {"skip nil property"}
222
247
  next
223
248
  end
224
249
 
@@ -236,24 +261,35 @@ module JSON::LD
236
261
  # If expanded property is @id and value is not a string, an invalid @id value error has been detected and processing is aborted
237
262
  e_id = case value
238
263
  when String
239
- context.expand_iri(value, documentRelative: true, as_string: true)
264
+ context.expand_iri(value, as_string: true, base: @options[:base], documentRelative: true)
240
265
  when Array
241
266
  # Framing allows an array of IRIs, and always puts values in an array
242
267
  raise JsonLdError::InvalidIdValue,
243
268
  "value of @id must be a string unless framing: #{value.inspect}" unless framing
244
- context.expand_iri(value, documentRelative: true, as_string: true)
269
+ context.expand_iri(value, as_string: true, base: @options[:base], documentRelative: true)
245
270
  value.map do |v|
246
271
  raise JsonLdError::InvalidTypeValue,
247
272
  "@id value must be a string or array of strings for framing: #{v.inspect}" unless v.is_a?(String)
248
- context.expand_iri(v, documentRelative: true, as_string: true)
273
+ context.expand_iri(v, as_string: true, base: @options[:base], documentRelative: true)
249
274
  end
250
275
  when Hash
251
- raise JsonLdError::InvalidIdValue,
252
- "value of @id must be a string unless framing: #{value.inspect}" unless framing
253
- raise JsonLdError::InvalidTypeValue,
254
- "value of @id must be a an empty object for framing: #{value.inspect}" unless
255
- value.empty?
256
- [{}]
276
+ if framing
277
+ raise JsonLdError::InvalidTypeValue,
278
+ "value of @id must be a an empty object for framing: #{value.inspect}" unless
279
+ value.empty?
280
+ [{}]
281
+ elsif @options[:rdfstar]
282
+ # Result must have just a single statement
283
+ rei_node = expand(value, active_property, context, log_depth: log_depth.to_i + 1)
284
+ statements = to_enum(:item_to_rdf, rei_node)
285
+ raise JsonLdError::InvalidEmbeddedNode,
286
+ "Embedded node with #{statements.size} statements" unless
287
+ statements.count == 1
288
+ rei_node
289
+ else
290
+ raise JsonLdError::InvalidIdValue,
291
+ "value of @id must be a string unless framing: #{value.inspect}" unless framing
292
+ end
257
293
  else
258
294
  raise JsonLdError::InvalidIdValue,
259
295
  "value of @id must be a string or hash if framing: #{value.inspect}"
@@ -268,7 +304,9 @@ module JSON::LD
268
304
  when '@included'
269
305
  # Included blocks are treated as an array of separate object nodes sharing the same referencing active_property. For 1.0, it is skipped as are other unknown keywords
270
306
  next if context.processingMode('json-ld-1.0')
271
- included_result = as_array(expand(value, active_property, context, ordered: ordered, framing: framing))
307
+ included_result = as_array(expand(value, active_property, context,
308
+ framing: framing,
309
+ log_depth: log_depth.to_i + 1))
272
310
 
273
311
  # Expanded values must be node objects
274
312
  raise JsonLdError::InvalidIncludedValue, "values of @included must expand to node objects" unless included_result.all? {|e| node?(e)}
@@ -276,27 +314,39 @@ module JSON::LD
276
314
  Array(output_object['@included']) + included_result
277
315
  when '@type'
278
316
  # If expanded property is @type and value is neither a string nor an array of strings, an invalid type value error has been detected and processing is aborted. Otherwise, set expanded value to the result of using the IRI Expansion algorithm, passing active context, true for vocab, and true for document relative to expand the value or each of its items.
279
- #log_debug("@type") {"value: #{value.inspect}"}
317
+ #log_debug("@type", depth: log_depth.to_i) {"value: #{value.inspect}"}
280
318
  e_type = case value
281
319
  when Array
282
320
  value.map do |v|
283
321
  raise JsonLdError::InvalidTypeValue,
284
322
  "@type value must be a string or array of strings: #{v.inspect}" unless v.is_a?(String)
285
- type_scoped_context.expand_iri(v, vocab: true, documentRelative: true, as_string: true)
323
+ type_scoped_context.expand_iri(v,
324
+ as_string: true,
325
+ base: @options[:base],
326
+ documentRelative: true,
327
+ vocab: true)
286
328
  end
287
329
  when String
288
- type_scoped_context.expand_iri(value, vocab: true, documentRelative: true, as_string: true)
330
+ type_scoped_context.expand_iri(value,
331
+ as_string: true,
332
+ base: @options[:base],
333
+ documentRelative: true,
334
+ vocab: true)
289
335
  when Hash
290
336
  if !framing
291
337
  raise JsonLdError::InvalidTypeValue,
292
338
  "@type value must be a string or array of strings: #{value.inspect}"
293
339
  elsif value.keys.length == 1 &&
294
- type_scoped_context.expand_iri(value.keys.first, vocab: true) == '@default'
340
+ type_scoped_context.expand_iri(value.keys.first, vocab: true, base: @options[:base]) == '@default'
295
341
  # Expand values of @default, which must be a string, or array of strings expanding to IRIs
296
342
  [{'@default' => Array(value['@default']).map do |v|
297
343
  raise JsonLdError::InvalidTypeValue,
298
344
  "@type default value must be a string or array of strings: #{v.inspect}" unless v.is_a?(String)
299
- type_scoped_context.expand_iri(v, vocab: true, documentRelative: true, as_string: true)
345
+ type_scoped_context.expand_iri(v,
346
+ as_string: true,
347
+ base: @options[:base],
348
+ documentRelative: true,
349
+ vocab: true)
300
350
  end}]
301
351
  elsif !value.empty?
302
352
  raise JsonLdError::InvalidTypeValue,
@@ -314,7 +364,9 @@ module JSON::LD
314
364
  framing || e_type.length > 1 ? e_type : e_type.first
315
365
  when '@graph'
316
366
  # If expanded property is @graph, set expanded value to the result of using this algorithm recursively passing active context, @graph for active property, and value for element.
317
- value = expand(value, '@graph', context, ordered: ordered, framing: framing)
367
+ value = expand(value, '@graph', context,
368
+ framing: framing,
369
+ log_depth: log_depth.to_i + 1)
318
370
  as_array(value)
319
371
  when '@value'
320
372
  # If expanded property is @value and input contains @type: json, accept any value.
@@ -406,7 +458,9 @@ module JSON::LD
406
458
  next if (expanded_active_property || '@graph') == '@graph'
407
459
 
408
460
  # Otherwise, initialize expanded value to the result of using this algorithm recursively passing active context, active property, and value for element.
409
- value = expand(value, active_property, context, ordered: ordered, framing: framing)
461
+ value = expand(value, active_property, context,
462
+ framing: framing,
463
+ log_depth: log_depth.to_i + 1)
410
464
 
411
465
  # Spec FIXME: need to be sure that result is an array
412
466
  value = as_array(value)
@@ -414,7 +468,9 @@ module JSON::LD
414
468
  value
415
469
  when '@set'
416
470
  # If expanded property is @set, set expanded value to the result of using this algorithm recursively, passing active context, active property, and value for element.
417
- expand(value, active_property, context, ordered: ordered, framing: framing)
471
+ expand(value, active_property, context,
472
+ framing: framing,
473
+ log_depth: log_depth.to_i + 1)
418
474
  when '@reverse'
419
475
  # If expanded property is @reverse and value is not a JSON object, an invalid @reverse value error has been detected and processing is aborted.
420
476
  raise JsonLdError::InvalidReverseValue,
@@ -422,11 +478,13 @@ module JSON::LD
422
478
 
423
479
  # Otherwise
424
480
  # Initialize expanded value to the result of using this algorithm recursively, passing active context, @reverse as active property, and value as element.
425
- value = expand(value, '@reverse', context, ordered: ordered, framing: framing)
481
+ value = expand(value, '@reverse', context,
482
+ framing: framing,
483
+ log_depth: log_depth.to_i + 1)
426
484
 
427
485
  # If expanded value contains an @reverse member, i.e., properties that are reversed twice, execute for each of its property and item the following steps:
428
486
  if value.has_key?('@reverse')
429
- #log_debug("@reverse") {"double reverse: #{value.inspect}"}
487
+ #log_debug("@reverse", depth: log_depth.to_i) {"double reverse: #{value.inspect}"}
430
488
  value['@reverse'].each do |property, item|
431
489
  # If result does not have a property member, create one and set its value to an empty array.
432
490
  # Append item to the value of the property member of result.
@@ -455,7 +513,10 @@ module JSON::LD
455
513
  when '@default', '@embed', '@explicit', '@omitDefault', '@preserve', '@requireAll'
456
514
  next unless framing
457
515
  # Framing keywords
458
- [expand(value, expanded_property, context, ordered: ordered, framing: framing)].flatten
516
+ [expand(value, expanded_property, context,
517
+ framing: framing,
518
+ log_depth: log_depth.to_i + 1)
519
+ ].flatten
459
520
  when '@nest'
460
521
  # Add key to nests
461
522
  nests << key
@@ -467,7 +528,7 @@ module JSON::LD
467
528
  end
468
529
 
469
530
  # Unless expanded value is null, set the expanded property member of result to expanded value.
470
- #log_debug("expand #{expanded_property}") { expanded_value.inspect}
531
+ #log_debug("expand #{expanded_property}", depth: log_depth.to_i) { expanded_value.inspect}
471
532
  output_object[expanded_property] = expanded_value unless expanded_value.nil? && expanded_property == '@value' && input_type != '@json'
472
533
  next
473
534
  end
@@ -483,9 +544,9 @@ module JSON::LD
483
544
  ary = []
484
545
 
485
546
  # For each key-value pair language-language value in value, ordered lexicographically by language
486
- keys = ordered ? value.keys.sort : value.keys
547
+ keys = @options[:ordered] ? value.keys.sort : value.keys
487
548
  keys.each do |k|
488
- expanded_k = context.expand_iri(k, vocab: true, as_string: true)
549
+ expanded_k = context.expand_iri(k, vocab: true, as_string: true, base: @options[:base])
489
550
 
490
551
  if k !~ /^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/ && expanded_k != '@none'
491
552
  warn "@language must be valid BCP47: #{k.inspect}"
@@ -517,23 +578,35 @@ module JSON::LD
517
578
  context.previous_context
518
579
  elsif container.include?('@id') && context.term_definitions[key]
519
580
  id_context = context.term_definitions[key].context if context.term_definitions[key]
520
- id_context ? context.parse(id_context, propagate: false) : context
581
+ if id_context.nil?
582
+ context
583
+ else
584
+ log_debug("expand", depth: log_depth.to_i) {"id_context: #{id_context.inspect}"}
585
+ context.parse(id_context, base: @options[:base], propagate: false)
586
+ end
521
587
  else
522
588
  context
523
589
  end
524
590
 
525
591
  # For each key-value in the object:
526
- keys = ordered ? value.keys.sort : value.keys
592
+ keys = @options[:ordered] ? value.keys.sort : value.keys
527
593
  keys.each do |k|
528
594
  # If container mapping in the active context includes @type, and k is a term in the active context having a local context, use that context when expanding values
529
595
  map_context = container_context.term_definitions[k].context if container.include?('@type') && container_context.term_definitions[k]
530
- map_context = container_context.parse(map_context, propagate: false) if map_context
596
+ unless map_context.nil?
597
+ log_debug("expand", depth: log_depth.to_i) {"map_context: #{map_context.inspect}"}
598
+ map_context = container_context.parse(map_context, base: @options[:base],
599
+ propagate: false)
600
+ end
531
601
  map_context ||= container_context
532
602
 
533
- expanded_k = container_context.expand_iri(k, vocab: true, as_string: true)
603
+ expanded_k = container_context.expand_iri(k, vocab: true, as_string: true, base: @options[:base])
534
604
 
535
605
  # Initialize index value to the result of using this algorithm recursively, passing active context, key as active property, and index value as element.
536
- index_value = expand([value[k]].flatten, key, map_context, ordered: ordered, framing: framing, from_map: true)
606
+ index_value = expand([value[k]].flatten, key, map_context,
607
+ framing: framing,
608
+ from_map: true,
609
+ log_depth: log_depth.to_i + 1)
537
610
  index_value.each do |item|
538
611
  case
539
612
  when container.include?('@index')
@@ -547,8 +620,8 @@ module JSON::LD
547
620
  raise JsonLdError::InvalidValueObject, "Attempt to add illegal key to value object: #{index_key}"
548
621
  else
549
622
  # Expand key based on term
550
- expanded_k = k == '@none' ? '@none' : container_context.expand_value(index_key, k)
551
- index_property = container_context.expand_iri(index_key, vocab: true, as_string: true)
623
+ expanded_k = k == '@none' ? '@none' : container_context.expand_value(index_key, k, base: @options[:base])
624
+ index_property = container_context.expand_iri(index_key, vocab: true, as_string: true, base: @options[:base])
552
625
  item[index_property] = [expanded_k].concat(Array(item[index_property])) unless expanded_k == '@none'
553
626
  end
554
627
  when container.include?('@id')
@@ -557,7 +630,7 @@ module JSON::LD
557
630
  item = {'@graph' => as_array(item)}
558
631
  end
559
632
  # Expand k document relative
560
- expanded_k = container_context.expand_iri(k, documentRelative: true, as_string: true) unless expanded_k == '@none'
633
+ expanded_k = container_context.expand_iri(k, as_string: true, base: @options[:base], documentRelative: true) unless expanded_k == '@none'
561
634
  item['@id'] ||= expanded_k unless expanded_k == '@none'
562
635
  when container.include?('@type')
563
636
  item['@type'] = [expanded_k].concat(Array(item['@type'])) unless expanded_k == '@none'
@@ -570,26 +643,28 @@ module JSON::LD
570
643
  ary
571
644
  else
572
645
  # Otherwise, initialize expanded value to the result of using this algorithm recursively, passing active context, key for active property, and value for element.
573
- expand(value, key, context, ordered: ordered, framing: framing)
646
+ expand(value, key, context,
647
+ framing: framing,
648
+ log_depth: log_depth.to_i + 1)
574
649
  end
575
650
 
576
651
  # If expanded value is null, ignore key by continuing to the next key from element.
577
652
  if expanded_value.nil?
578
- #log_debug(" => skip nil value")
653
+ #log_debug(" => skip nil value", depth: log_depth.to_i)
579
654
  next
580
655
  end
581
- #log_debug {" => #{expanded_value.inspect}"}
656
+ #log_debug(depth: log_depth.to_i) {" => #{expanded_value.inspect}"}
582
657
 
583
658
  # If the container mapping associated to key in active context is @list and expanded value is not already a list object, convert expanded value to a list object by first setting it to an array containing only expanded value if it is not already an array, and then by setting it to a JSON object containing the key-value pair @list-expanded value.
584
659
  if container.first == '@list' && container.length == 1 && !list?(expanded_value)
585
- #log_debug(" => ") { "convert #{expanded_value.inspect} to list"}
660
+ #log_debug(" => ", depth: log_depth.to_i) { "convert #{expanded_value.inspect} to list"}
586
661
  expanded_value = {'@list' => as_array(expanded_value)}
587
662
  end
588
- #log_debug {" => #{expanded_value.inspect}"}
663
+ #log_debug(depth: log_depth.to_i) {" => #{expanded_value.inspect}"}
589
664
 
590
665
  # convert expanded value to @graph if container specifies it
591
666
  if container.first == '@graph' && container.length == 1
592
- #log_debug(" => ") { "convert #{expanded_value.inspect} to list"}
667
+ #log_debug(" => ", depth: log_depth.to_i) { "convert #{expanded_value.inspect} to list"}
593
668
  expanded_value = as_array(expanded_value).map do |v|
594
669
  {'@graph' => as_array(v)}
595
670
  end
@@ -625,16 +700,24 @@ module JSON::LD
625
700
 
626
701
  # For each key in nests, recusively expand content
627
702
  nests.each do |key|
703
+ nest_context = context.term_definitions[key].context if context.term_definitions[key]
704
+ nest_context = if nest_context.nil?
705
+ context
706
+ else
707
+ log_debug("expand", depth: log_depth.to_i) {"nest_context: #{nest_context.inspect}"}
708
+ context.parse(nest_context, base: @options[:base],
709
+ override_protected: true)
710
+ end
628
711
  nested_values = as_array(input[key])
629
712
  nested_values.each do |nv|
630
713
  raise JsonLdError::InvalidNestValue, nv.inspect unless
631
- nv.is_a?(Hash) && nv.keys.none? {|k| context.expand_iri(k, vocab: true) == '@value'}
632
- expand_object(nv, active_property, context, output_object,
714
+ nv.is_a?(Hash) && nv.keys.none? {|k| nest_context.expand_iri(k, vocab: true, base: @options[:base]) == '@value'}
715
+ expand_object(nv, active_property, nest_context, output_object,
716
+ framing: framing,
633
717
  expanded_active_property: expanded_active_property,
634
- type_scoped_context: type_scoped_context,
635
718
  type_key: type_key,
636
- ordered: ordered,
637
- framing: framing)
719
+ type_scoped_context: type_scoped_context,
720
+ log_depth: log_depth.to_i + 1)
638
721
  end
639
722
  end
640
723
  end