json-ld 3.1.3 → 3.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/json/ld.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  # frozen_string_literal: true
3
3
  $:.unshift(File.expand_path("../ld", __FILE__))
4
- require 'rdf' # @see http://rubygems.org/gems/rdf
4
+ require 'rdf' # @see https://rubygems.org/gems/rdf
5
5
  require 'multi_json'
6
6
  require 'set'
7
7
 
@@ -19,7 +19,7 @@ module JSON
19
19
  # end
20
20
  # end
21
21
  #
22
- # @see http://rubygems.org/gems/rdf
22
+ # @see https://rubygems.org/gems/rdf
23
23
  # @see http://www.w3.org/TR/REC-rdf-syntax/
24
24
  #
25
25
  # @author [Gregg Kellogg](http://greggkellogg.net/)
@@ -34,6 +34,8 @@ module JSON
34
34
  autoload :Normalize, 'json/ld/normalize'
35
35
  autoload :Reader, 'json/ld/reader'
36
36
  autoload :Resource, 'json/ld/resource'
37
+ autoload :StreamingReader, 'json/ld/streaming_reader'
38
+ autoload :StreamingWriter, 'json/ld/streaming_writer'
37
39
  autoload :VERSION, 'json/ld/version'
38
40
  autoload :Writer, 'json/ld/writer'
39
41
 
@@ -45,6 +47,7 @@ module JSON
45
47
  DEFAULT_CONTEXT = "http://schema.org"
46
48
 
47
49
  KEYWORDS = Set.new(%w(
50
+ @annotation
48
51
  @base
49
52
  @container
50
53
  @context
@@ -114,6 +117,7 @@ module JSON
114
117
  class CollidingKeywords < JsonLdError; @code = "colliding keywords"; end
115
118
  class ConflictingIndexes < JsonLdError; @code = "conflicting indexes"; end
116
119
  class CyclicIRIMapping < JsonLdError; @code = "cyclic IRI mapping"; end
120
+ class InvalidAnnotation < JsonLdError; @code = "invalid annotation"; end
117
121
  class InvalidBaseIRI < JsonLdError; @code = "invalid base IRI"; end
118
122
  class InvalidContainerMapping < JsonLdError; @code = "invalid container mapping"; end
119
123
  class InvalidContextEntry < JsonLdError; @code = "invalid context entry"; end
@@ -135,6 +139,7 @@ module JSON
135
139
  class InvalidNestValue < JsonLdError; @code = "invalid @nest value"; end
136
140
  class InvalidPrefixValue < JsonLdError; @code = "invalid @prefix value"; end
137
141
  class InvalidPropagateValue < JsonLdError; @code = "invalid @propagate value"; end
142
+ class InvalidEmbeddedNode < JsonLdError; @code = "invalid embedded node"; end
138
143
  class InvalidRemoteContext < JsonLdError; @code = "invalid remote context"; end
139
144
  class InvalidReverseProperty < JsonLdError; @code = "invalid reverse property"; end
140
145
  class InvalidReversePropertyMap < JsonLdError; @code = "invalid reverse property map"; end
@@ -143,6 +148,7 @@ module JSON
143
148
  class InvalidScopedContext < JsonLdError; @code = "invalid scoped context"; end
144
149
  class InvalidScriptElement < JsonLdError; @code = "invalid script element"; end
145
150
  class InvalidSetOrListObject < JsonLdError; @code = "invalid set or list object"; end
151
+ class InvalidStreamingKeyOrder < JsonLdError; @code = 'invalid streaming key order' end
146
152
  class InvalidTermDefinition < JsonLdError; @code = "invalid term definition"; end
147
153
  class InvalidBaseDirection < JsonLdError; @code = "invalid base direction"; end
148
154
  class InvalidTypedValue < JsonLdError; @code = "invalid typed value"; end
data/lib/json/ld/api.rb CHANGED
@@ -66,7 +66,7 @@ module JSON::LD
66
66
  # @param [String, #read, Hash, Array, JSON::LD::Context] context
67
67
  # An external context to use additionally to the context embedded in input when expanding the input.
68
68
  # @param [Hash{Symbol => Object}] options
69
- # @option options [String, #to_s] :base
69
+ # @option options [RDF::URI, String, #to_s] :base
70
70
  # The Base IRI to use when expanding the document. This overrides the value of `input` if it is a _IRI_. If not specified and `input` is not an _IRI_, the base IRI defaults to the current document IRI if in a browser context, or the empty string if there is no document context. If not specified, and a base IRI is found from `input`, options[:base] will be modified with this value.
71
71
  # @option options [Boolean] :compactArrays (true)
72
72
  # If set to `true`, the JSON-LD processor replaces arrays with just one element with that element during compaction. If set to `false`, all arrays will remain arrays even if they have just one element.
@@ -89,6 +89,8 @@ module JSON::LD
89
89
  # @option options [String] :processingMode
90
90
  # Processing mode, json-ld-1.0 or json-ld-1.1.
91
91
  # If `processingMode` is not specified, a mode of `json-ld-1.0` or `json-ld-1.1` is set, the context used for `expansion` or `compaction`.
92
+ # @option options [Boolean] rdfstar (false)
93
+ # support parsing JSON-LD* statement resources.
92
94
  # @option options [Boolean] :rename_bnodes (true)
93
95
  # Rename bnodes as part of expansion, or keep them the same.
94
96
  # @option options [Boolean] :unique_bnodes (false)
@@ -100,14 +102,17 @@ module JSON::LD
100
102
  # @yield [api]
101
103
  # @yieldparam [API]
102
104
  # @raise [JsonLdError]
103
- def initialize(input, context, rename_bnodes: true, unique_bnodes: false, **options, &block)
105
+ def initialize(input, context, **options, &block)
104
106
  @options = {
105
107
  compactArrays: true,
106
108
  ordered: false,
107
109
  extractAllScripts: false,
110
+ rename_bnodes: true,
111
+ unique_bnodes: false,
108
112
  }.merge(options)
109
- @namer = unique_bnodes ? BlankNodeUniqer.new : (rename_bnodes ? BlankNodeNamer.new("b") : BlankNodeMapper.new)
113
+ @namer = @options[:unique_bnodes] ? BlankNodeUniqer.new : (@options[:rename_bnodes] ? BlankNodeNamer.new("b") : BlankNodeMapper.new)
110
114
 
115
+ @options[:base] = RDF::URI(@options[:base]) if @options[:base] && !@options[:base].is_a?(RDF::URI)
111
116
  # For context via Link header
112
117
  _, context_ref = nil, nil
113
118
 
@@ -117,7 +122,7 @@ module JSON::LD
117
122
  remote_doc = self.class.loadRemoteDocument(input, **@options)
118
123
 
119
124
  context_ref = remote_doc.contextUrl
120
- @options[:base] = remote_doc.documentUrl if remote_doc.documentUrl && !@options[:no_default_base]
125
+ @options[:base] = RDF::URI(remote_doc.documentUrl) if remote_doc.documentUrl && !@options[:no_default_base]
121
126
 
122
127
  case remote_doc.document
123
128
  when String
@@ -130,7 +135,7 @@ module JSON::LD
130
135
 
131
136
  # If not provided, first use context from document, or from a Link header
132
137
  context ||= context_ref || {}
133
- @context = Context.parse(context || {}, **@options)
138
+ @context = Context.parse(context, **@options)
134
139
 
135
140
  if block_given?
136
141
  case block.arity
@@ -163,10 +168,9 @@ module JSON::LD
163
168
  # If a block is given, the result of evaluating the block is returned, otherwise, the expanded JSON-LD document
164
169
  # @see https://www.w3.org/TR/json-ld11-api/#expansion-algorithm
165
170
  def self.expand(input, framing: false, **options, &block)
166
- result, doc_base = nil
171
+ result = doc_base = nil
167
172
  API.new(input, options[:expandContext], **options) do
168
173
  result = self.expand(self.value, nil, self.context,
169
- ordered: @options[:ordered],
170
174
  framing: framing)
171
175
  doc_base = @options[:base]
172
176
  end
@@ -200,9 +204,9 @@ module JSON::LD
200
204
  # The JSON-LD object to copy and perform the compaction upon.
201
205
  # @param [String, #read, Hash, Array, JSON::LD::Context] context
202
206
  # The base context to use when compacting the input.
207
+ # @param [Boolean] expanded (false) Input is already expanded
203
208
  # @param [Hash{Symbol => Object}] options
204
209
  # @option options (see #initialize)
205
- # @option options [Boolean] :expanded Input is already expanded
206
210
  # @yield jsonld
207
211
  # @yieldparam [Hash] jsonld
208
212
  # The compacted JSON-LD document
@@ -218,21 +222,21 @@ module JSON::LD
218
222
  # 1) Perform the Expansion Algorithm on the JSON-LD input.
219
223
  # This removes any existing context to allow the given context to be cleanly applied.
220
224
  expanded_input = expanded ? input : API.expand(input, ordered: false, **options) do |res, base_iri|
221
- options[:base] ||= base_iri if options[:compactToRelative]
225
+ options[:base] ||= RDF::URI(base_iri) if base_iri && options[:compactToRelative]
222
226
  res
223
227
  end
224
228
 
225
229
  API.new(expanded_input, context, no_default_base: true, **options) do
226
230
  log_debug(".compact") {"expanded input: #{expanded_input.to_json(JSON_STATE) rescue 'malformed json'}"}
227
- result = compact(value, ordered: @options[:ordered])
231
+ result = compact(value)
228
232
 
229
233
  # xxx) Add the given context to the output
230
- ctx = self.context.serialize
234
+ ctx = self.context.serialize(provided_context: context)
231
235
  if result.is_a?(Array)
232
236
  kwgraph = self.context.compact_iri('@graph', vocab: true)
233
237
  result = result.empty? ? {} : {kwgraph => result}
234
238
  end
235
- result = ctx.merge(result) unless ctx.empty?
239
+ result = ctx.merge(result) unless ctx.fetch('@context', {}).empty?
236
240
  end
237
241
  block_given? ? yield(result) : result
238
242
  end
@@ -246,9 +250,9 @@ module JSON::LD
246
250
  # The JSON-LD object or array of JSON-LD objects to flatten or an IRI referencing the JSON-LD document to flatten.
247
251
  # @param [String, #read, Hash, Array, JSON::LD::EvaluationContext] context
248
252
  # An optional external context to use additionally to the context embedded in input when expanding the input.
253
+ # @param [Boolean] expanded (false) Input is already expanded
249
254
  # @param [Hash{Symbol => Object}] options
250
255
  # @option options (see #initialize)
251
- # @option options [Boolean] :expanded Input is already expanded
252
256
  # @yield jsonld
253
257
  # @yieldparam [Hash] jsonld
254
258
  # The flattened JSON-LD document
@@ -265,7 +269,7 @@ module JSON::LD
265
269
 
266
270
  # Expand input to simplify processing
267
271
  expanded_input = expanded ? input : API.expand(input, **options) do |result, base_iri|
268
- options[:base] ||= base_iri if options[:compactToRelative]
272
+ options[:base] ||= RDF::URI(base_iri) if base_iri && options[:compactToRelative]
269
273
  result
270
274
  end
271
275
 
@@ -273,6 +277,9 @@ module JSON::LD
273
277
  API.new(expanded_input, context, no_default_base: true, **options) do
274
278
  log_debug(".flatten") {"expanded input: #{value.to_json(JSON_STATE) rescue 'malformed json'}"}
275
279
 
280
+ # Rename blank nodes recusively. Note that this does not create new blank node identifiers where none exist, which is performed in the node map generation algorithm.
281
+ @value = rename_bnodes(@value) if @options[:rename_bnodes]
282
+
276
283
  # Initialize node map to a JSON object consisting of a single member whose key is @default and whose value is an empty JSON object.
277
284
  graph_maps = {'@default' => {}}
278
285
  create_node_map(value, graph_maps)
@@ -294,9 +301,11 @@ module JSON::LD
294
301
 
295
302
  if context && !flattened.empty?
296
303
  # Otherwise, return the result of compacting flattened according the Compaction algorithm passing context ensuring that the compaction result uses the @graph keyword (or its alias) at the top-level, even if the context is empty or if there is only one element to put in the @graph array. This ensures that the returned document has a deterministic structure.
297
- compacted = as_array(compact(flattened, ordered: @options[:ordered]))
304
+ compacted = as_array(compact(flattened))
298
305
  kwgraph = self.context.compact_iri('@graph')
299
- flattened = self.context.serialize.merge(kwgraph => compacted)
306
+ flattened = self.context.
307
+ serialize(provided_context: context).
308
+ merge(kwgraph => compacted)
300
309
  end
301
310
  end
302
311
 
@@ -312,6 +321,7 @@ module JSON::LD
312
321
  # The JSON-LD object to copy and perform the framing on.
313
322
  # @param [String, #read, Hash, Array] frame
314
323
  # The frame to use when re-arranging the data.
324
+ # @param [Boolean] expanded (false) Input is already expanded
315
325
  # @option options (see #initialize)
316
326
  # @option options ['@always', '@link', '@once', '@never'] :embed ('@once')
317
327
  # a flag specifying that objects should be directly embedded in the output, instead of being referred to by their IRI.
@@ -321,7 +331,6 @@ module JSON::LD
321
331
  # A flag specifying that all properties present in the input frame must either have a default value or be present in the JSON-LD input for the frame to match.
322
332
  # @option options [Boolean] :omitDefault (false)
323
333
  # a flag specifying that properties that are missing from the JSON-LD input should be omitted from the output.
324
- # @option options [Boolean] :expanded Input is already expanded
325
334
  # @option options [Boolean] :pruneBlankNodeIdentifiers (true) removes blank node identifiers that are only used once.
326
335
  # @option options [Boolean] :omitGraph does not use `@graph` at top level unless necessary to describe multiple objects, defaults to `true` if processingMode is 1.1, otherwise `false`.
327
336
  # @yield jsonld
@@ -335,7 +344,7 @@ module JSON::LD
335
344
  def self.frame(input, frame, expanded: false, **options)
336
345
  result = nil
337
346
  options = {
338
- base: (input if input.is_a?(String)),
347
+ base: (RDF::URI(input) if input.is_a?(String)),
339
348
  compactArrays: true,
340
349
  compactToRelative: true,
341
350
  embed: '@once',
@@ -369,7 +378,7 @@ module JSON::LD
369
378
 
370
379
  # Expand input to simplify processing
371
380
  expanded_input = expanded ? input : API.expand(input, ordered: false, **options) do |res, base_iri|
372
- options[:base] ||= base_iri if options[:compactToRelative]
381
+ options[:base] ||= RDF::URI(base_iri) if base_iri && options[:compactToRelative]
373
382
  res
374
383
  end
375
384
 
@@ -387,10 +396,13 @@ module JSON::LD
387
396
  end
388
397
 
389
398
  # Set omitGraph option, if not present, based on processingMode
390
- unless options.has_key?(:omitGraph)
399
+ unless options.key?(:omitGraph)
391
400
  options[:omitGraph] = context.processingMode('json-ld-1.1')
392
401
  end
393
402
 
403
+ # Rename blank nodes recusively. Note that this does not create new blank node identifiers where none exist, which is performed in the node map generation algorithm.
404
+ @value = rename_bnodes(@value)
405
+
394
406
  # Get framing nodes from expanded input, replacing Blank Node identifiers as necessary
395
407
  create_node_map(value, framing_state[:graphMap], active_graph: '@default')
396
408
 
@@ -411,7 +423,7 @@ module JSON::LD
411
423
  frame(framing_state, framing_state[:subjects].keys.opt_sort(ordered: @options[:ordered]), (expanded_frame.first || {}), parent: result, **options)
412
424
 
413
425
  # Default to based on processinMode
414
- if !options.has_key?(:pruneBlankNodeIdentifiers)
426
+ if !options.key?(:pruneBlankNodeIdentifiers)
415
427
  options[:pruneBlankNodeIdentifiers] = context.processingMode('json-ld-1.1')
416
428
  end
417
429
 
@@ -426,7 +438,7 @@ module JSON::LD
426
438
  log_debug(".frame") {"expanded result: #{result.to_json(JSON_STATE) rescue 'malformed json'}"}
427
439
 
428
440
  # Compact result
429
- compacted = compact(result, ordered: @options[:ordered])
441
+ compacted = compact(result)
430
442
 
431
443
  # @replace `@null` with nil, compacting arrays
432
444
  compacted = cleanup_null(compacted)
@@ -434,11 +446,14 @@ module JSON::LD
434
446
 
435
447
  # Add the given context to the output
436
448
  result = if !compacted.is_a?(Array)
437
- context.serialize.merge(compacted)
449
+ compacted
438
450
  else
439
451
  kwgraph = context.compact_iri('@graph')
440
- context.serialize.merge({kwgraph => compacted})
452
+ {kwgraph => compacted}
441
453
  end
454
+ # Only add context if one was provided
455
+ result = context.serialize(provided_context: frame).merge(result) if frame['@context']
456
+
442
457
  log_debug(".frame") {"after compact: #{result.to_json(JSON_STATE) rescue 'malformed json'}"}
443
458
  result
444
459
  end
@@ -451,10 +466,10 @@ module JSON::LD
451
466
  #
452
467
  # @param [String, #read, Hash, Array] input
453
468
  # The JSON-LD object to process when outputting statements.
469
+ # @param [Boolean] expanded (false) Input is already expanded
454
470
  # @option options (see #initialize)
455
471
  # @option options [Boolean] :produceGeneralizedRdf (false)
456
472
  # If true, output will include statements having blank node predicates, otherwise they are dropped.
457
- # @option options [Boolean] :expanded Input is already expanded
458
473
  # @raise [JsonLdError]
459
474
  # @yield statement
460
475
  # @yieldparam [RDF::Statement] statement
@@ -463,7 +478,7 @@ module JSON::LD
463
478
  unless block_given?
464
479
  results = []
465
480
  results.extend(RDF::Enumerable)
466
- self.toRdf(input, **options) do |stmt|
481
+ self.toRdf(input, expanded: expanded, **options) do |stmt|
467
482
  results << stmt
468
483
  end
469
484
  return results
@@ -473,16 +488,16 @@ module JSON::LD
473
488
  extractAllScripts: true,
474
489
  }.merge(options)
475
490
 
476
- # Expand input to simplify processing
477
- expanded_input = expanded ? input : API.expand(input, ordered: false, **options)
491
+ # Flatten input to simplify processing
492
+ flattened_input = API.flatten(input, nil, expanded: expanded, ordered: false, **options)
478
493
 
479
- API.new(expanded_input, nil, **options) do
494
+ API.new(flattened_input, nil, **options) do
480
495
  # 1) Perform the Expansion Algorithm on the JSON-LD input.
481
496
  # This removes any existing context to allow the given context to be cleanly applied.
482
- log_debug(".toRdf") {"expanded input: #{expanded_input.to_json(JSON_STATE) rescue 'malformed json'}"}
497
+ log_debug(".toRdf") {"flattened input: #{flattened_input.to_json(JSON_STATE) rescue 'malformed json'}"}
483
498
 
484
499
  # Recurse through input
485
- expanded_input.each do |node|
500
+ flattened_input.each do |node|
486
501
  item_to_rdf(node) do |statement|
487
502
  next if statement.predicate.node? && !options[:produceGeneralizedRdf]
488
503
 
@@ -521,8 +536,7 @@ module JSON::LD
521
536
  API.new(nil, nil, **options) do
522
537
  result = from_statements(input,
523
538
  useRdfType: useRdfType,
524
- useNativeTypes: useNativeTypes,
525
- ordered: @options[:ordered])
539
+ useNativeTypes: useNativeTypes)
526
540
  end
527
541
 
528
542
  block_given? ? yield(result) : result
@@ -532,16 +546,18 @@ module JSON::LD
532
546
  # Uses built-in or provided documentLoader to retrieve a parsed document.
533
547
  #
534
548
  # @param [RDF::URI, String] url
549
+ # @param [String, RDF::URI] base
550
+ # Location to use as documentUrl instead of `url`.
551
+ # @option options [Proc] :documentLoader
552
+ # The callback of the loader to be used to retrieve remote documents and contexts.
535
553
  # @param [Boolean] extractAllScripts
536
554
  # If set to `true`, when extracting JSON-LD script elements from HTML, unless a specific fragment identifier is targeted, extracts all encountered JSON-LD script elements using an array form, if necessary.
537
555
  # @param [String] profile
538
556
  # When the resulting `contentType` is `text/html` or `application/xhtml+xml`, this option determines the profile to use for selecting a JSON-LD script elements.
539
557
  # @param [String] requestProfile
540
558
  # One or more IRIs to use in the request as a profile parameter.
541
- # @param [Boolean] validate
559
+ # @param [Boolean] validate (false)
542
560
  # Allow only appropriate content types
543
- # @param [String, RDF::URI] base
544
- # Location to use as documentUrl instead of `url`.
545
561
  # @param [Hash<Symbol => Object>] options
546
562
  # @yield remote_document
547
563
  # @yieldparam [RemoteDocumentRemoteDocument, RDF::Util::File::RemoteDocument] remote_document
@@ -550,13 +566,14 @@ module JSON::LD
550
566
  # If a block is given, the result of evaluating the block is returned, otherwise, the retrieved remote document and context information unless block given
551
567
  # @raise [JsonLdError]
552
568
  def self.loadRemoteDocument(url,
569
+ base: nil,
570
+ documentLoader: nil,
553
571
  extractAllScripts: false,
554
572
  profile: nil,
555
573
  requestProfile: nil,
556
574
  validate: false,
557
- base: nil,
558
575
  **options)
559
- documentLoader = options.fetch(:documentLoader, self.method(:documentLoader))
576
+ documentLoader ||= self.method(:documentLoader)
560
577
  options = OPEN_OPTS.merge(options)
561
578
  if requestProfile
562
579
  # Add any request profile
@@ -12,16 +12,16 @@ module JSON::LD
12
12
  # This algorithm compacts a JSON-LD document, such that the given context is applied. This must result in shortening any applicable IRIs to terms or compact IRIs, any applicable keywords to keyword aliases, and any applicable JSON-LD values expressed in expanded form to simple values such as strings or numbers.
13
13
  #
14
14
  # @param [Array, Hash] element
15
- # @param [String] property (nil)
16
- # @param [Boolean] ordered (true)
15
+ # @param [String, RDF::URI] base (nil)
17
16
  # Ensure output objects have keys ordered properly
17
+ # @param [String] property (nil)
18
+ # Extra validatation
18
19
  # @return [Array, Hash]
19
- def compact(element, property: nil, ordered: false)
20
- #if property.nil?
21
- # log_debug("compact") {"element: #{element.inspect}, ec: #{context.inspect}"}
22
- #else
23
- # log_debug("compact") {"property: #{property.inspect}"}
24
- #end
20
+ def compact(element,
21
+ base: nil,
22
+ property: nil,
23
+ log_depth: nil)
24
+ log_debug("compact", depth: log_depth.to_i) {"element: #{element.inspect}, ec: #{context.inspect}"}
25
25
 
26
26
  # If the term definition for active property itself contains a context, use that for compacting values.
27
27
  input_context = self.context
@@ -29,17 +29,19 @@ module JSON::LD
29
29
  case element
30
30
  when Array
31
31
  #log_debug("") {"Array #{element.inspect}"}
32
- result = element.map {|item| compact(item, property: property, ordered: ordered)}.compact
32
+ result = element.map do |item|
33
+ compact(item, base: base, property: property, log_depth: log_depth.to_i + 1)
34
+ end.compact
33
35
 
34
36
  # If element has a single member and the active property has no
35
37
  # @container mapping to @list or @set, the compacted value is that
36
38
  # member; otherwise the compacted value is element
37
39
  if result.length == 1 &&
38
40
  !context.as_array?(property) && @options[:compactArrays]
39
- #log_debug("=> extract single element: #{result.first.inspect}")
41
+ log_debug("=> extract single element", depth: log_depth.to_i) {result.first.inspect}
40
42
  result.first
41
43
  else
42
- #log_debug("=> array result: #{result.inspect}")
44
+ log_debug("=> array result", depth: log_depth.to_i) {result.inspect}
43
45
  result
44
46
  end
45
47
  when Hash
@@ -50,24 +52,31 @@ module JSON::LD
50
52
 
51
53
  # Revert any previously type-scoped (non-preserved) context
52
54
  if context.previous_context && !element.key?('@value') && element.keys != %w(@id)
55
+ log_debug("revert ec", depth: log_depth.to_i) {"previous context: #{context.previous_context.inspect}"}
53
56
  self.context = context.previous_context
54
57
  end
55
58
 
56
59
  # Look up term definintions from property using the original type-scoped context, if it exists, but apply them to the now current previous context
57
60
  td = input_context.term_definitions[property] if property
58
- self.context = context.parse(td.context, override_protected: true) if td && td.context
61
+ if td && !td.context.nil?
62
+ self.context = context.parse(td.context,
63
+ override_protected: true)
64
+ log_debug("prop-scoped", depth: log_depth.to_i) {"context: #{self.context.inspect}"}
65
+ end
59
66
 
60
- if element.key?('@id') || element.key?('@value')
61
- result = context.compact_value(property, element, log_depth: @options[:log_depth])
67
+ if (element.key?('@id') || element.key?('@value')) && !element.key?('@annotation')
68
+ result = context.compact_value(property, element, base: @options[:base])
62
69
  if !result.is_a?(Hash) || context.coerce(property) == '@json'
63
- #log_debug("") {"=> scalar result: #{result.inspect}"}
70
+ log_debug("", depth: log_depth.to_i) {"=> scalar result: #{result.inspect}"}
64
71
  return result
65
72
  end
66
73
  end
67
74
 
68
75
  # If expanded property is @list and we're contained within a list container, recursively compact this item to an array
69
76
  if list?(element) && context.container(property).include?('@list')
70
- return compact(element['@list'], property: property, ordered: ordered)
77
+ return compact(element['@list'], base: base,
78
+ property: property,
79
+ log_depth: log_depth.to_i + 1)
71
80
  end
72
81
 
73
82
  inside_reverse = property == '@reverse'
@@ -80,15 +89,25 @@ module JSON::LD
80
89
  sort.
81
90
  each do |term|
82
91
  term_context = input_context.term_definitions[term].context if input_context.term_definitions[term]
83
- self.context = context.parse(term_context, propagate: false) if term_context
92
+ self.context = context.parse(term_context, propagate: false) unless term_context.nil?
93
+ log_debug("type-scoped", depth: log_depth.to_i) {"context: #{self.context.inspect}"}
84
94
  end
85
95
 
86
- element.keys.opt_sort(ordered: ordered).each do |expanded_property|
96
+ element.keys.opt_sort(ordered: @options[:ordered]).each do |expanded_property|
87
97
  expanded_value = element[expanded_property]
88
- #log_debug("") {"#{expanded_property}: #{expanded_value.inspect}"}
98
+ log_debug("", depth: log_depth.to_i) {"#{expanded_property}: #{expanded_value.inspect}"}
89
99
 
90
100
  if expanded_property == '@id'
91
- compacted_value = Array(expanded_value).map {|expanded_id| context.compact_iri(expanded_id)}
101
+ compacted_value = as_array(expanded_value).map do |expanded_id|
102
+ if node?(expanded_id) && @options[:rdfstar]
103
+ # This can only really happen for valid RDF*
104
+ compact(expanded_id, base: base,
105
+ property: '@id',
106
+ log_depth: log_depth.to_i + 1)
107
+ else
108
+ context.compact_iri(expanded_id, base: @options[:base])
109
+ end
110
+ end
92
111
 
93
112
  kw_alias = context.compact_iri('@id', vocab: true)
94
113
  as_array = compacted_value.length > 1
@@ -98,7 +117,9 @@ module JSON::LD
98
117
  end
99
118
 
100
119
  if expanded_property == '@type'
101
- compacted_value = Array(expanded_value).map {|expanded_type| input_context.compact_iri(expanded_type, vocab: true)}
120
+ compacted_value = Array(expanded_value).map do |expanded_type|
121
+ input_context.compact_iri(expanded_type, vocab: true)
122
+ end
102
123
 
103
124
  kw_alias = context.compact_iri('@type', vocab: true)
104
125
  as_array = compacted_value.length > 1 ||
@@ -110,8 +131,10 @@ module JSON::LD
110
131
  end
111
132
 
112
133
  if expanded_property == '@reverse'
113
- compacted_value = compact(expanded_value, property: '@reverse', ordered: ordered)
114
- #log_debug("@reverse") {"compacted_value: #{compacted_value.inspect}"}
134
+ compacted_value = compact(expanded_value, base: base,
135
+ property: '@reverse',
136
+ log_depth: log_depth.to_i + 1)
137
+ log_debug("@reverse", depth: log_depth.to_i) {"compacted_value: #{compacted_value.inspect}"}
115
138
  # handle double-reversed properties
116
139
  compacted_value.each do |prop, value|
117
140
  if context.reverse?(prop)
@@ -123,7 +146,7 @@ module JSON::LD
123
146
 
124
147
  unless compacted_value.empty?
125
148
  al = context.compact_iri('@reverse')
126
- #log_debug("") {"remainder: #{al} => #{compacted_value.inspect}"}
149
+ log_debug("", depth: log_depth.to_i) {"remainder: #{al} => #{compacted_value.inspect}"}
127
150
  result[al] = compacted_value
128
151
  end
129
152
  next
@@ -131,8 +154,10 @@ module JSON::LD
131
154
 
132
155
  if expanded_property == '@preserve'
133
156
  # Compact using `property`
134
- compacted_value = compact(expanded_value, property: property, ordered: ordered)
135
- #log_debug("@preserve") {"compacted_value: #{compacted_value.inspect}"}
157
+ compacted_value = compact(expanded_value, base: base,
158
+ property: property,
159
+ log_depth: log_depth.to_i + 1)
160
+ log_debug("@preserve", depth: log_depth.to_i) {"compacted_value: #{compacted_value.inspect}"}
136
161
 
137
162
  unless compacted_value.is_a?(Array) && compacted_value.empty?
138
163
  result['@preserve'] = compacted_value
@@ -141,14 +166,14 @@ module JSON::LD
141
166
  end
142
167
 
143
168
  if expanded_property == '@index' && context.container(property).include?('@index')
144
- #log_debug("@index") {"drop @index"}
169
+ log_debug("@index", depth: log_depth.to_i) {"drop @index"}
145
170
  next
146
171
  end
147
172
 
148
173
  # Otherwise, if expanded property is @direction, @index, @value, or @language:
149
174
  if EXPANDED_PROPERTY_DIRECTION_INDEX_LANGUAGE_VALUE.include?(expanded_property)
150
175
  al = context.compact_iri(expanded_property, vocab: true)
151
- #log_debug(expanded_property) {"#{al} => #{expanded_value.inspect}"}
176
+ log_debug(expanded_property, depth: log_depth.to_i) {"#{al} => #{expanded_value.inspect}"}
152
177
  result[al] = expanded_value
153
178
  next
154
179
  end
@@ -158,8 +183,7 @@ module JSON::LD
158
183
  context.compact_iri(expanded_property,
159
184
  value: expanded_value,
160
185
  vocab: true,
161
- reverse: inside_reverse,
162
- log_depth: @options[:log_depth])
186
+ reverse: inside_reverse)
163
187
 
164
188
  if nest_prop = context.nest(item_active_property)
165
189
  result[nest_prop] ||= {}
@@ -177,8 +201,7 @@ module JSON::LD
177
201
  context.compact_iri(expanded_property,
178
202
  value: expanded_item,
179
203
  vocab: true,
180
- reverse: inside_reverse,
181
- log_depth: @options[:log_depth])
204
+ reverse: inside_reverse)
182
205
 
183
206
 
184
207
  nest_result = if nest_prop = context.nest(item_active_property)
@@ -197,8 +220,10 @@ module JSON::LD
197
220
  else expanded_item
198
221
  end
199
222
 
200
- compacted_item = compact(value, property: item_active_property, ordered: ordered)
201
- #log_debug("") {" => compacted key: #{item_active_property.inspect} for #{compacted_item.inspect}"}
223
+ compacted_item = compact(value, base: base,
224
+ property: item_active_property,
225
+ log_depth: log_depth.to_i + 1)
226
+ log_debug("", depth: log_depth.to_i) {" => compacted key: #{item_active_property.inspect} for #{compacted_item.inspect}"}
202
227
 
203
228
  # handle @list
204
229
  if list?(expanded_item)
@@ -206,7 +231,7 @@ module JSON::LD
206
231
  unless container.include?('@list')
207
232
  al = context.compact_iri('@list', vocab: true)
208
233
  compacted_item = {al => compacted_item}
209
- if expanded_item.has_key?('@index')
234
+ if expanded_item.key?('@index')
210
235
  key = context.compact_iri('@index', vocab: true)
211
236
  compacted_item[key] = expanded_item['@index']
212
237
  end
@@ -225,9 +250,9 @@ module JSON::LD
225
250
  map_object = nest_result[item_active_property] ||= {}
226
251
  # If there is no @id, create a blank node identifier to use as an index
227
252
  map_key = if container.include?('@id') && expanded_item['@id']
228
- context.compact_iri(expanded_item['@id'])
253
+ context.compact_iri(expanded_item['@id'], base: @options[:base])
229
254
  elsif container.include?('@index') && expanded_item['@index']
230
- context.compact_iri(expanded_item['@index'])
255
+ context.compact_iri(expanded_item['@index'], vocab: true)
231
256
  else
232
257
  context.compact_iri('@none', vocab: true)
233
258
  end
@@ -251,7 +276,7 @@ module JSON::LD
251
276
  al = context.compact_iri('@id', vocab: true)
252
277
  compacted_item[al] = context.compact_iri(expanded_item['@id'], vocab: false)
253
278
  end
254
- if expanded_item.has_key?('@index')
279
+ if expanded_item.key?('@index')
255
280
  key = context.compact_iri('@index', vocab: true)
256
281
  compacted_item[key] = expanded_item['@index']
257
282
  end
@@ -299,7 +324,10 @@ module JSON::LD
299
324
 
300
325
  # if compacted_item contains a single entry who's key maps to @id, then recompact the item without @type
301
326
  if compacted_item.keys.length == 1 && expanded_item.keys.include?('@id')
302
- compacted_item = compact({'@id' => expanded_item['@id']}, property: item_active_property)
327
+ compacted_item = compact({'@id' => expanded_item['@id']},
328
+ base: base,
329
+ property: item_active_property,
330
+ log_depth: log_depth.to_i + 1)
303
331
  end
304
332
  compacted_item
305
333
  end
@@ -316,7 +344,7 @@ module JSON::LD
316
344
  result
317
345
  else
318
346
  # For other types, the compacted value is the element value
319
- #log_debug("compact") {element.class.to_s}
347
+ log_debug("compact", depth: log_depth.to_i) {element.class.to_s}
320
348
  element
321
349
  end
322
350