json-ld 3.1.0 → 3.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,13 +9,8 @@ module JSON::LD
9
9
  include Utils
10
10
 
11
11
  # The following constant is used to reduce object allocations
12
- CONTAINER_INDEX_ID_TYPE = Set.new(%w(@index @id @type)).freeze
13
- CONTAINER_GRAPH_INDEX = %w(@graph @index).freeze
14
- CONTAINER_INDEX = %w(@index).freeze
15
- CONTAINER_ID = %w(@id).freeze
16
- CONTAINER_LIST = %w(@list).freeze
17
- CONTAINER_TYPE = %w(@type).freeze
18
- CONTAINER_GRAPH_ID = %w(@graph @id).freeze
12
+ CONTAINER_INDEX_ID_TYPE = Set['@index', '@id', '@type'].freeze
13
+ KEY_ID = %w(@id).freeze
19
14
  KEYS_VALUE_LANGUAGE_TYPE_INDEX_DIRECTION = %w(@value @language @type @index @direction).freeze
20
15
  KEYS_SET_LIST_INDEX = %w(@set @list @index).freeze
21
16
  KEYS_INCLUDED_TYPE = %w(@included @type).freeze
@@ -26,28 +21,32 @@ module JSON::LD
26
21
  # @param [Array, Hash] input
27
22
  # @param [String] active_property
28
23
  # @param [Context] context
29
- # @param [Boolean] ordered (true)
30
- # Ensure output objects have keys ordered properly
31
24
  # @param [Boolean] framing (false)
32
25
  # Special rules for expanding a frame
33
26
  # @param [Boolean] from_map
34
27
  # Expanding from a map, which could be an `@type` map, so don't clear out context term definitions
28
+ #
35
29
  # @return [Array<Hash{String => Object}>]
36
- def expand(input, active_property, context, ordered: false, framing: false, from_map: false)
37
- #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}"}
38
33
  framing = false if active_property == '@default'
39
- expanded_active_property = context.expand_iri(active_property, vocab: true).to_s if active_property
34
+ expanded_active_property = context.expand_iri(active_property, vocab: true, as_string: true, base: @options[:base]) if active_property
40
35
 
41
36
  # Use a term-specific context, if defined, based on the non-type-scoped context.
42
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?
43
39
 
44
40
  result = case input
45
41
  when Array
46
42
  # If element is an array,
47
- is_list = context.container(active_property) == CONTAINER_LIST
43
+ is_list = context.container(active_property).include?('@list')
48
44
  value = input.each_with_object([]) do |v, memo|
49
45
  # Initialize expanded item to the result of using this algorithm recursively, passing active context, active property, and item as element.
50
- 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)
51
50
 
52
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
53
52
  v = {"@list" => v} if is_list && v.is_a?(Array)
@@ -62,23 +61,29 @@ module JSON::LD
62
61
  value
63
62
  when Hash
64
63
  if context.previous_context
65
- expanded_key_map = input.keys.inject({}) {|memo, key| memo.merge(key => context.expand_iri(key, vocab: true).to_s)}
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
66
67
  # Revert any previously type-scoped term definitions, unless this is from a map, a value object or a subject reference
67
68
  revert_context = !from_map &&
68
69
  !expanded_key_map.values.include?('@value') &&
69
70
  !(expanded_key_map.values == ['@id'])
70
71
 
71
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
72
74
  context = context.previous_context if revert_context
73
75
  end
74
76
 
75
77
  # Apply property-scoped context after reverting term-scoped context
76
- 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?
77
82
 
78
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.
79
84
  if input.has_key?('@context')
80
- context = context.parse(input.delete('@context'))
81
- #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}"}
82
87
  end
83
88
 
84
89
  # Set the type-scoped context to the context on input, for use later
@@ -89,25 +94,28 @@ module JSON::LD
89
94
  # See if keys mapping to @type have terms with a local context
90
95
  type_key = nil
91
96
  input.keys.sort.
92
- select {|k| context.expand_iri(k, vocab: true, quite: true) == '@type'}.
97
+ select {|k| context.expand_iri(k, vocab: true, base: @options[:base]) == '@type'}.
93
98
  each do |tk|
94
99
 
95
100
  type_key ||= tk # Side effect saves the first found key mapping to @type
96
101
  Array(input[tk]).sort.each do |term|
97
102
  term_context = type_scoped_context.term_definitions[term].context if type_scoped_context.term_definitions[term]
98
- 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
99
107
  end
100
108
  end
101
109
 
102
110
  # Process each key and value in element. Ignores @nesting content
103
111
  expand_object(input, active_property, context, output_object,
104
112
  expanded_active_property: expanded_active_property,
105
- type_scoped_context: type_scoped_context,
113
+ framing: framing,
106
114
  type_key: type_key,
107
- ordered: ordered,
108
- framing: framing)
115
+ type_scoped_context: type_scoped_context,
116
+ log_depth: log_depth.to_i + 1)
109
117
 
110
- #log_debug("output object") {output_object.inspect}
118
+ log_debug("output object", depth: log_depth.to_i) {output_object.inspect}
111
119
 
112
120
  # If result contains the key @value:
113
121
  if value?(output_object)
@@ -115,13 +123,13 @@ module JSON::LD
115
123
  unless (keys - KEYS_VALUE_LANGUAGE_TYPE_INDEX_DIRECTION).empty?
116
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.
117
125
  raise JsonLdError::InvalidValueObject,
118
- "value object has unknown keys: #{output_object.inspect}"
126
+ "value object has unknown keys: #{output_object.inspect}"
119
127
  end
120
128
 
121
129
  if keys.include?('@type') && !(keys & %w(@language @direction)).empty?
122
130
  # @type is inconsistent with either @language or @direction
123
131
  raise JsonLdError::InvalidValueObject,
124
- "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}"
125
133
  end
126
134
 
127
135
  output_object.delete('@language') if output_object.key?('@language') && Array(output_object['@language']).empty?
@@ -138,9 +146,16 @@ module JSON::LD
138
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.
139
147
  raise JsonLdError::InvalidLanguageTaggedValue,
140
148
  "when @language is used, @value must be a string: #{output_object.inspect}"
141
- elsif !Array(output_object['@type']).all? {|t|
142
- t.is_a?(String) && RDF::URI(t).absolute? && !t.start_with?('_:') ||
143
- 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?('_:')
144
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.
145
160
  raise JsonLdError::InvalidTypedValue,
146
161
  "value of @type must be an IRI or '@json': #{output_object.inspect}"
@@ -164,14 +179,14 @@ module JSON::LD
164
179
 
165
180
  # If active property is null or @graph, drop free-floating values as follows:
166
181
  if (expanded_active_property || '@graph') == '@graph' &&
167
- (output_object.key?('@value') || output_object.key?('@list') ||
168
- (output_object.keys - CONTAINER_ID).empty? && !framing)
169
- #log_debug(" =>") { "empty top-level: " + output_object.inspect}
182
+ (output_object.key?('@value') || output_object.key?('@list') ||
183
+ (output_object.keys - KEY_ID).empty? && !framing)
184
+ log_debug(" =>", depth: log_depth.to_i) { "empty top-level: " + output_object.inspect}
170
185
  return nil
171
186
  end
172
187
 
173
188
  # Re-order result keys if ordering
174
- if ordered
189
+ if @options[:ordered]
175
190
  output_object.keys.sort.each_with_object({}) {|kk, memo| memo[kk] = output_object[kk]}
176
191
  else
177
192
  output_object
@@ -181,12 +196,17 @@ module JSON::LD
181
196
  return nil if input.nil? || active_property.nil? || expanded_active_property == '@graph'
182
197
 
183
198
  # Apply property-scoped context
184
- 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?
185
205
 
186
- context.expand_value(active_property, input, log_depth: @options[:log_depth])
206
+ context.expand_value(active_property, input, base: @options[:base])
187
207
  end
188
208
 
189
- #log_debug {" => #{result.inspect}"}
209
+ log_debug(depth: log_depth.to_i) {" => #{result.inspect}"}
190
210
  result
191
211
  end
192
212
 
@@ -195,21 +215,21 @@ module JSON::LD
195
215
  # Expand each key and value of element adding them to result
196
216
  def expand_object(input, active_property, context, output_object,
197
217
  expanded_active_property:,
198
- type_scoped_context:,
218
+ framing:,
199
219
  type_key:,
200
- ordered:,
201
- framing:)
220
+ type_scoped_context:,
221
+ log_depth: nil)
202
222
  nests = []
203
223
 
204
224
  input_type = Array(input[type_key]).last
205
- input_type = context.expand_iri(input_type, vocab: true, quiet: true) if input_type
225
+ input_type = context.expand_iri(input_type, vocab: true, as_string: true, base: @options[:base]) if input_type
206
226
 
207
227
  # Then, proceed and process each property and value in element as follows:
208
- keys = ordered ? input.keys.sort : input.keys
228
+ keys = @options[:ordered] ? input.keys.sort : input.keys
209
229
  keys.each do |key|
210
230
  # For each key and value in element, ordered lexicographically by key:
211
231
  value = input[key]
212
- expanded_property = context.expand_iri(key, vocab: true, quiet: true)
232
+ expanded_property = context.expand_iri(key, vocab: true, base: @options[:base])
213
233
 
214
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.
215
235
  next if expanded_property.is_a?(RDF::URI) && expanded_property.relative?
@@ -220,10 +240,10 @@ module JSON::LD
220
240
  expanded_property.to_s.start_with?("_:") &&
221
241
  context.processingMode('json-ld-1.1')
222
242
 
223
- #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}"}
224
244
 
225
245
  if expanded_property.nil?
226
- #log_debug(" => ") {"skip nil property"}
246
+ #log_debug(" => ", depth: log_depth.to_i) {"skip nil property"}
227
247
  next
228
248
  end
229
249
 
@@ -241,16 +261,16 @@ module JSON::LD
241
261
  # If expanded property is @id and value is not a string, an invalid @id value error has been detected and processing is aborted
242
262
  e_id = case value
243
263
  when String
244
- context.expand_iri(value, documentRelative: true, quiet: true).to_s
264
+ context.expand_iri(value, as_string: true, base: @options[:base], documentRelative: true)
245
265
  when Array
246
266
  # Framing allows an array of IRIs, and always puts values in an array
247
267
  raise JsonLdError::InvalidIdValue,
248
268
  "value of @id must be a string unless framing: #{value.inspect}" unless framing
249
- context.expand_iri(value, documentRelative: true, quiet: true).to_s
269
+ context.expand_iri(value, as_string: true, base: @options[:base], documentRelative: true)
250
270
  value.map do |v|
251
271
  raise JsonLdError::InvalidTypeValue,
252
272
  "@id value must be a string or array of strings for framing: #{v.inspect}" unless v.is_a?(String)
253
- context.expand_iri(v, documentRelative: true, quiet: true,).to_s
273
+ context.expand_iri(v, as_string: true, base: @options[:base], documentRelative: true)
254
274
  end
255
275
  when Hash
256
276
  raise JsonLdError::InvalidIdValue,
@@ -273,7 +293,9 @@ module JSON::LD
273
293
  when '@included'
274
294
  # 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
275
295
  next if context.processingMode('json-ld-1.0')
276
- included_result = as_array(expand(value, active_property, context, ordered: ordered, framing: framing))
296
+ included_result = as_array(expand(value, active_property, context,
297
+ framing: framing,
298
+ log_depth: log_depth.to_i + 1))
277
299
 
278
300
  # Expanded values must be node objects
279
301
  raise JsonLdError::InvalidIncludedValue, "values of @included must expand to node objects" unless included_result.all? {|e| node?(e)}
@@ -281,27 +303,39 @@ module JSON::LD
281
303
  Array(output_object['@included']) + included_result
282
304
  when '@type'
283
305
  # 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.
284
- #log_debug("@type") {"value: #{value.inspect}"}
306
+ #log_debug("@type", depth: log_depth.to_i) {"value: #{value.inspect}"}
285
307
  e_type = case value
286
308
  when Array
287
309
  value.map do |v|
288
310
  raise JsonLdError::InvalidTypeValue,
289
311
  "@type value must be a string or array of strings: #{v.inspect}" unless v.is_a?(String)
290
- type_scoped_context.expand_iri(v, vocab: true, documentRelative: true, quiet: true).to_s
312
+ type_scoped_context.expand_iri(v,
313
+ as_string: true,
314
+ base: @options[:base],
315
+ documentRelative: true,
316
+ vocab: true)
291
317
  end
292
318
  when String
293
- type_scoped_context.expand_iri(value, vocab: true, documentRelative: true, quiet: true).to_s
319
+ type_scoped_context.expand_iri(value,
320
+ as_string: true,
321
+ base: @options[:base],
322
+ documentRelative: true,
323
+ vocab: true)
294
324
  when Hash
295
325
  if !framing
296
326
  raise JsonLdError::InvalidTypeValue,
297
327
  "@type value must be a string or array of strings: #{value.inspect}"
298
328
  elsif value.keys.length == 1 &&
299
- type_scoped_context.expand_iri(value.keys.first, vocab: true, quiet: true).to_s == '@default'
329
+ type_scoped_context.expand_iri(value.keys.first, vocab: true, base: @options[:base]) == '@default'
300
330
  # Expand values of @default, which must be a string, or array of strings expanding to IRIs
301
331
  [{'@default' => Array(value['@default']).map do |v|
302
332
  raise JsonLdError::InvalidTypeValue,
303
333
  "@type default value must be a string or array of strings: #{v.inspect}" unless v.is_a?(String)
304
- type_scoped_context.expand_iri(v, vocab: true, documentRelative: true, quiet: true).to_s
334
+ type_scoped_context.expand_iri(v,
335
+ as_string: true,
336
+ base: @options[:base],
337
+ documentRelative: true,
338
+ vocab: true)
305
339
  end}]
306
340
  elsif !value.empty?
307
341
  raise JsonLdError::InvalidTypeValue,
@@ -319,7 +353,9 @@ module JSON::LD
319
353
  framing || e_type.length > 1 ? e_type : e_type.first
320
354
  when '@graph'
321
355
  # 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.
322
- value = expand(value, '@graph', context, ordered: ordered, framing: framing)
356
+ value = expand(value, '@graph', context,
357
+ framing: framing,
358
+ log_depth: log_depth.to_i + 1)
323
359
  as_array(value)
324
360
  when '@value'
325
361
  # If expanded property is @value and input contains @type: json, accept any value.
@@ -411,7 +447,9 @@ module JSON::LD
411
447
  next if (expanded_active_property || '@graph') == '@graph'
412
448
 
413
449
  # Otherwise, initialize expanded value to the result of using this algorithm recursively passing active context, active property, and value for element.
414
- value = expand(value, active_property, context, ordered: ordered, framing: framing)
450
+ value = expand(value, active_property, context,
451
+ framing: framing,
452
+ log_depth: log_depth.to_i + 1)
415
453
 
416
454
  # Spec FIXME: need to be sure that result is an array
417
455
  value = as_array(value)
@@ -419,7 +457,9 @@ module JSON::LD
419
457
  value
420
458
  when '@set'
421
459
  # 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.
422
- expand(value, active_property, context, ordered: ordered, framing: framing)
460
+ expand(value, active_property, context,
461
+ framing: framing,
462
+ log_depth: log_depth.to_i + 1)
423
463
  when '@reverse'
424
464
  # If expanded property is @reverse and value is not a JSON object, an invalid @reverse value error has been detected and processing is aborted.
425
465
  raise JsonLdError::InvalidReverseValue,
@@ -427,11 +467,13 @@ module JSON::LD
427
467
 
428
468
  # Otherwise
429
469
  # Initialize expanded value to the result of using this algorithm recursively, passing active context, @reverse as active property, and value as element.
430
- value = expand(value, '@reverse', context, ordered: ordered, framing: framing)
470
+ value = expand(value, '@reverse', context,
471
+ framing: framing,
472
+ log_depth: log_depth.to_i + 1)
431
473
 
432
474
  # 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:
433
475
  if value.has_key?('@reverse')
434
- #log_debug("@reverse") {"double reverse: #{value.inspect}"}
476
+ #log_debug("@reverse", depth: log_depth.to_i) {"double reverse: #{value.inspect}"}
435
477
  value['@reverse'].each do |property, item|
436
478
  # If result does not have a property member, create one and set its value to an empty array.
437
479
  # Append item to the value of the property member of result.
@@ -460,7 +502,10 @@ module JSON::LD
460
502
  when '@default', '@embed', '@explicit', '@omitDefault', '@preserve', '@requireAll'
461
503
  next unless framing
462
504
  # Framing keywords
463
- [expand(value, expanded_property, context, ordered: ordered, framing: framing)].flatten
505
+ [expand(value, expanded_property, context,
506
+ framing: framing,
507
+ log_depth: log_depth.to_i + 1)
508
+ ].flatten
464
509
  when '@nest'
465
510
  # Add key to nests
466
511
  nests << key
@@ -472,7 +517,7 @@ module JSON::LD
472
517
  end
473
518
 
474
519
  # Unless expanded value is null, set the expanded property member of result to expanded value.
475
- #log_debug("expand #{expanded_property}") { expanded_value.inspect}
520
+ #log_debug("expand #{expanded_property}", depth: log_depth.to_i) { expanded_value.inspect}
476
521
  output_object[expanded_property] = expanded_value unless expanded_value.nil? && expanded_property == '@value' && input_type != '@json'
477
522
  next
478
523
  end
@@ -481,16 +526,16 @@ module JSON::LD
481
526
  expanded_value = if context.coerce(key) == '@json'
482
527
  # In JSON-LD 1.1, values can be native JSON
483
528
  {"@value" => value, "@type" => "@json"}
484
- elsif container.length == 1 && container.first == '@language' && value.is_a?(Hash)
529
+ elsif container.include?('@language') && value.is_a?(Hash)
485
530
  # Otherwise, if key's container mapping in active context is @language and value is a JSON object then value is expanded from a language map as follows:
486
531
 
487
532
  # Set multilingual array to an empty array.
488
533
  ary = []
489
534
 
490
535
  # For each key-value pair language-language value in value, ordered lexicographically by language
491
- keys = ordered ? value.keys.sort : value.keys
536
+ keys = @options[:ordered] ? value.keys.sort : value.keys
492
537
  keys.each do |k|
493
- expanded_k = context.expand_iri(k, vocab: true, quiet: true).to_s
538
+ expanded_k = context.expand_iri(k, vocab: true, as_string: true, base: @options[:base])
494
539
 
495
540
  if k !~ /^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/ && expanded_k != '@none'
496
541
  warn "@language must be valid BCP47: #{k.inspect}"
@@ -510,7 +555,7 @@ module JSON::LD
510
555
  end
511
556
 
512
557
  ary
513
- elsif container.any? { |key| CONTAINER_INDEX_ID_TYPE.include?(key) } && value.is_a?(Hash)
558
+ elsif container.intersect?(CONTAINER_INDEX_ID_TYPE) && value.is_a?(Hash)
514
559
  # Otherwise, if key's container mapping in active context contains @index, @id, @type and value is a JSON object then value is expanded from an index map as follows:
515
560
 
516
561
  # Set ary to an empty array.
@@ -522,26 +567,38 @@ module JSON::LD
522
567
  context.previous_context
523
568
  elsif container.include?('@id') && context.term_definitions[key]
524
569
  id_context = context.term_definitions[key].context if context.term_definitions[key]
525
- id_context ? context.parse(id_context, propagate: false) : context
570
+ if id_context.nil?
571
+ context
572
+ else
573
+ log_debug("expand", depth: log_depth.to_i) {"id_context: #{id_context.inspect}"}
574
+ context.parse(id_context, base: @options[:base], propagate: false)
575
+ end
526
576
  else
527
577
  context
528
578
  end
529
579
 
530
580
  # For each key-value in the object:
531
- keys = ordered ? value.keys.sort : value.keys
581
+ keys = @options[:ordered] ? value.keys.sort : value.keys
532
582
  keys.each do |k|
533
583
  # 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
534
584
  map_context = container_context.term_definitions[k].context if container.include?('@type') && container_context.term_definitions[k]
535
- map_context = container_context.parse(map_context, propagate: false) if map_context
585
+ unless map_context.nil?
586
+ log_debug("expand", depth: log_depth.to_i) {"map_context: #{map_context.inspect}"}
587
+ map_context = container_context.parse(map_context, base: @options[:base],
588
+ propagate: false)
589
+ end
536
590
  map_context ||= container_context
537
591
 
538
- expanded_k = container_context.expand_iri(k, vocab: true, quiet: true).to_s
592
+ expanded_k = container_context.expand_iri(k, vocab: true, as_string: true, base: @options[:base])
539
593
 
540
594
  # Initialize index value to the result of using this algorithm recursively, passing active context, key as active property, and index value as element.
541
- index_value = expand([value[k]].flatten, key, map_context, ordered: ordered, framing: framing, from_map: true)
595
+ index_value = expand([value[k]].flatten, key, map_context,
596
+ framing: framing,
597
+ from_map: true,
598
+ log_depth: log_depth.to_i + 1)
542
599
  index_value.each do |item|
543
- case container
544
- when CONTAINER_GRAPH_INDEX, CONTAINER_INDEX
600
+ case
601
+ when container.include?('@index')
545
602
  # Indexed graph by graph name
546
603
  if !graph?(item) && container.include?('@graph')
547
604
  item = {'@graph' => as_array(item)}
@@ -552,19 +609,19 @@ module JSON::LD
552
609
  raise JsonLdError::InvalidValueObject, "Attempt to add illegal key to value object: #{index_key}"
553
610
  else
554
611
  # Expand key based on term
555
- expanded_k = k == '@none' ? '@none' : container_context.expand_value(index_key, k)
556
- index_property = container_context.expand_iri(index_key, vocab: true, quiet: true).to_s
612
+ expanded_k = k == '@none' ? '@none' : container_context.expand_value(index_key, k, base: @options[:base])
613
+ index_property = container_context.expand_iri(index_key, vocab: true, as_string: true, base: @options[:base])
557
614
  item[index_property] = [expanded_k].concat(Array(item[index_property])) unless expanded_k == '@none'
558
615
  end
559
- when CONTAINER_GRAPH_ID, CONTAINER_ID
616
+ when container.include?('@id')
560
617
  # Indexed graph by graph name
561
618
  if !graph?(item) && container.include?('@graph')
562
619
  item = {'@graph' => as_array(item)}
563
620
  end
564
621
  # Expand k document relative
565
- expanded_k = container_context.expand_iri(k, documentRelative: true, quiet: true).to_s unless expanded_k == '@none'
622
+ expanded_k = container_context.expand_iri(k, as_string: true, base: @options[:base], documentRelative: true) unless expanded_k == '@none'
566
623
  item['@id'] ||= expanded_k unless expanded_k == '@none'
567
- when CONTAINER_TYPE
624
+ when container.include?('@type')
568
625
  item['@type'] = [expanded_k].concat(Array(item['@type'])) unless expanded_k == '@none'
569
626
  end
570
627
 
@@ -575,26 +632,28 @@ module JSON::LD
575
632
  ary
576
633
  else
577
634
  # Otherwise, initialize expanded value to the result of using this algorithm recursively, passing active context, key for active property, and value for element.
578
- expand(value, key, context, ordered: ordered, framing: framing)
635
+ expand(value, key, context,
636
+ framing: framing,
637
+ log_depth: log_depth.to_i + 1)
579
638
  end
580
639
 
581
640
  # If expanded value is null, ignore key by continuing to the next key from element.
582
641
  if expanded_value.nil?
583
- #log_debug(" => skip nil value")
642
+ #log_debug(" => skip nil value", depth: log_depth.to_i)
584
643
  next
585
644
  end
586
- #log_debug {" => #{expanded_value.inspect}"}
645
+ #log_debug(depth: log_depth.to_i) {" => #{expanded_value.inspect}"}
587
646
 
588
647
  # 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.
589
648
  if container.first == '@list' && container.length == 1 && !list?(expanded_value)
590
- #log_debug(" => ") { "convert #{expanded_value.inspect} to list"}
649
+ #log_debug(" => ", depth: log_depth.to_i) { "convert #{expanded_value.inspect} to list"}
591
650
  expanded_value = {'@list' => as_array(expanded_value)}
592
651
  end
593
- #log_debug {" => #{expanded_value.inspect}"}
652
+ #log_debug(depth: log_depth.to_i) {" => #{expanded_value.inspect}"}
594
653
 
595
654
  # convert expanded value to @graph if container specifies it
596
655
  if container.first == '@graph' && container.length == 1
597
- #log_debug(" => ") { "convert #{expanded_value.inspect} to list"}
656
+ #log_debug(" => ", depth: log_depth.to_i) { "convert #{expanded_value.inspect} to list"}
598
657
  expanded_value = as_array(expanded_value).map do |v|
599
658
  {'@graph' => as_array(v)}
600
659
  end
@@ -630,16 +689,24 @@ module JSON::LD
630
689
 
631
690
  # For each key in nests, recusively expand content
632
691
  nests.each do |key|
692
+ nest_context = context.term_definitions[key].context if context.term_definitions[key]
693
+ nest_context = if nest_context.nil?
694
+ context
695
+ else
696
+ log_debug("expand", depth: log_depth.to_i) {"nest_context: #{nest_context.inspect}"}
697
+ context.parse(nest_context, base: @options[:base],
698
+ override_protected: true)
699
+ end
633
700
  nested_values = as_array(input[key])
634
701
  nested_values.each do |nv|
635
702
  raise JsonLdError::InvalidNestValue, nv.inspect unless
636
- nv.is_a?(Hash) && nv.keys.none? {|k| context.expand_iri(k, vocab: true) == '@value'}
637
- expand_object(nv, active_property, context, output_object,
703
+ nv.is_a?(Hash) && nv.keys.none? {|k| nest_context.expand_iri(k, vocab: true, base: @options[:base]) == '@value'}
704
+ expand_object(nv, active_property, nest_context, output_object,
705
+ framing: framing,
638
706
  expanded_active_property: expanded_active_property,
639
- type_scoped_context: type_scoped_context,
640
707
  type_key: type_key,
641
- ordered: ordered,
642
- framing: framing)
708
+ type_scoped_context: type_scoped_context,
709
+ log_depth: log_depth.to_i + 1)
643
710
  end
644
711
  end
645
712
  end