json-ld 3.1.3 → 3.1.8

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