json-ld 3.1.4 → 3.1.9

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/)
@@ -47,6 +47,7 @@ module JSON
47
47
  DEFAULT_CONTEXT = "http://schema.org"
48
48
 
49
49
  KEYWORDS = Set.new(%w(
50
+ @annotation
50
51
  @base
51
52
  @container
52
53
  @context
@@ -116,6 +117,7 @@ module JSON
116
117
  class CollidingKeywords < JsonLdError; @code = "colliding keywords"; end
117
118
  class ConflictingIndexes < JsonLdError; @code = "conflicting indexes"; end
118
119
  class CyclicIRIMapping < JsonLdError; @code = "cyclic IRI mapping"; end
120
+ class InvalidAnnotation < JsonLdError; @code = "invalid annotation"; end
119
121
  class InvalidBaseIRI < JsonLdError; @code = "invalid base IRI"; end
120
122
  class InvalidContainerMapping < JsonLdError; @code = "invalid container mapping"; end
121
123
  class InvalidContextEntry < JsonLdError; @code = "invalid context entry"; end
@@ -137,6 +139,7 @@ module JSON
137
139
  class InvalidNestValue < JsonLdError; @code = "invalid @nest value"; end
138
140
  class InvalidPrefixValue < JsonLdError; @code = "invalid @prefix value"; end
139
141
  class InvalidPropagateValue < JsonLdError; @code = "invalid @propagate value"; end
142
+ class InvalidEmbeddedNode < JsonLdError; @code = "invalid embedded node"; end
140
143
  class InvalidRemoteContext < JsonLdError; @code = "invalid remote context"; end
141
144
  class InvalidReverseProperty < JsonLdError; @code = "invalid reverse property"; end
142
145
  class InvalidReversePropertyMap < JsonLdError; @code = "invalid reverse property map"; end
data/lib/json/ld/api.rb CHANGED
@@ -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-star 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,13 +102,15 @@ 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
 
111
115
  @options[:base] = RDF::URI(@options[:base]) if @options[:base] && !@options[:base].is_a?(RDF::URI)
112
116
  # For context via Link header
@@ -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
@@ -246,9 +250,11 @@ 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
256
+ # @option options [Boolean] :createAnnotations
257
+ # Unfold embedded nodes which can be represented using `@annotation`.
252
258
  # @yield jsonld
253
259
  # @yieldparam [Hash] jsonld
254
260
  # The flattened JSON-LD document
@@ -273,10 +279,20 @@ module JSON::LD
273
279
  API.new(expanded_input, context, no_default_base: true, **options) do
274
280
  log_debug(".flatten") {"expanded input: #{value.to_json(JSON_STATE) rescue 'malformed json'}"}
275
281
 
282
+ # 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.
283
+ @value = rename_bnodes(@value) if @options[:rename_bnodes]
284
+
276
285
  # 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
286
  graph_maps = {'@default' => {}}
278
287
  create_node_map(value, graph_maps)
279
288
 
289
+ # If create annotations flag is set, then update each node map in graph maps with the result of calling the create annotations algorithm.
290
+ if options[:createAnnotations]
291
+ graph_maps.values.each do |node_map|
292
+ create_annotations(node_map)
293
+ end
294
+ end
295
+
280
296
  default_graph = graph_maps['@default']
281
297
  graph_maps.keys.opt_sort(ordered: @options[:ordered]).each do |graph_name|
282
298
  next if graph_name == '@default'
@@ -295,7 +311,7 @@ module JSON::LD
295
311
  if context && !flattened.empty?
296
312
  # 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
313
  compacted = as_array(compact(flattened))
298
- kwgraph = self.context.compact_iri('@graph')
314
+ kwgraph = self.context.compact_iri('@graph', vocab: true)
299
315
  flattened = self.context.
300
316
  serialize(provided_context: context).
301
317
  merge(kwgraph => compacted)
@@ -314,6 +330,7 @@ module JSON::LD
314
330
  # The JSON-LD object to copy and perform the framing on.
315
331
  # @param [String, #read, Hash, Array] frame
316
332
  # The frame to use when re-arranging the data.
333
+ # @param [Boolean] expanded (false) Input is already expanded
317
334
  # @option options (see #initialize)
318
335
  # @option options ['@always', '@link', '@once', '@never'] :embed ('@once')
319
336
  # a flag specifying that objects should be directly embedded in the output, instead of being referred to by their IRI.
@@ -323,7 +340,6 @@ module JSON::LD
323
340
  # 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.
324
341
  # @option options [Boolean] :omitDefault (false)
325
342
  # a flag specifying that properties that are missing from the JSON-LD input should be omitted from the output.
326
- # @option options [Boolean] :expanded Input is already expanded
327
343
  # @option options [Boolean] :pruneBlankNodeIdentifiers (true) removes blank node identifiers that are only used once.
328
344
  # @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`.
329
345
  # @yield jsonld
@@ -389,10 +405,13 @@ module JSON::LD
389
405
  end
390
406
 
391
407
  # Set omitGraph option, if not present, based on processingMode
392
- unless options.has_key?(:omitGraph)
408
+ unless options.key?(:omitGraph)
393
409
  options[:omitGraph] = context.processingMode('json-ld-1.1')
394
410
  end
395
411
 
412
+ # 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.
413
+ @value = rename_bnodes(@value)
414
+
396
415
  # Get framing nodes from expanded input, replacing Blank Node identifiers as necessary
397
416
  create_node_map(value, framing_state[:graphMap], active_graph: '@default')
398
417
 
@@ -413,7 +432,7 @@ module JSON::LD
413
432
  frame(framing_state, framing_state[:subjects].keys.opt_sort(ordered: @options[:ordered]), (expanded_frame.first || {}), parent: result, **options)
414
433
 
415
434
  # Default to based on processinMode
416
- if !options.has_key?(:pruneBlankNodeIdentifiers)
435
+ if !options.key?(:pruneBlankNodeIdentifiers)
417
436
  options[:pruneBlankNodeIdentifiers] = context.processingMode('json-ld-1.1')
418
437
  end
419
438
 
@@ -438,7 +457,7 @@ module JSON::LD
438
457
  result = if !compacted.is_a?(Array)
439
458
  compacted
440
459
  else
441
- kwgraph = context.compact_iri('@graph')
460
+ kwgraph = context.compact_iri('@graph', vocab: true)
442
461
  {kwgraph => compacted}
443
462
  end
444
463
  # Only add context if one was provided
@@ -456,10 +475,10 @@ module JSON::LD
456
475
  #
457
476
  # @param [String, #read, Hash, Array] input
458
477
  # The JSON-LD object to process when outputting statements.
478
+ # @param [Boolean] expanded (false) Input is already expanded
459
479
  # @option options (see #initialize)
460
480
  # @option options [Boolean] :produceGeneralizedRdf (false)
461
481
  # If true, output will include statements having blank node predicates, otherwise they are dropped.
462
- # @option options [Boolean] :expanded Input is already expanded
463
482
  # @raise [JsonLdError]
464
483
  # @yield statement
465
484
  # @yieldparam [RDF::Statement] statement
@@ -468,7 +487,7 @@ module JSON::LD
468
487
  unless block_given?
469
488
  results = []
470
489
  results.extend(RDF::Enumerable)
471
- self.toRdf(input, **options) do |stmt|
490
+ self.toRdf(input, expanded: expanded, **options) do |stmt|
472
491
  results << stmt
473
492
  end
474
493
  return results
@@ -478,16 +497,16 @@ module JSON::LD
478
497
  extractAllScripts: true,
479
498
  }.merge(options)
480
499
 
481
- # Expand input to simplify processing
482
- expanded_input = expanded ? input : API.expand(input, ordered: false, **options)
500
+ # Flatten input to simplify processing
501
+ flattened_input = API.flatten(input, nil, expanded: expanded, ordered: false, **options)
483
502
 
484
- API.new(expanded_input, nil, **options) do
503
+ API.new(flattened_input, nil, **options) do
485
504
  # 1) Perform the Expansion Algorithm on the JSON-LD input.
486
505
  # This removes any existing context to allow the given context to be cleanly applied.
487
- log_debug(".toRdf") {"expanded input: #{expanded_input.to_json(JSON_STATE) rescue 'malformed json'}"}
506
+ log_debug(".toRdf") {"flattened input: #{flattened_input.to_json(JSON_STATE) rescue 'malformed json'}"}
488
507
 
489
508
  # Recurse through input
490
- expanded_input.each do |node|
509
+ flattened_input.each do |node|
491
510
  item_to_rdf(node) do |statement|
492
511
  next if statement.predicate.node? && !options[:produceGeneralizedRdf]
493
512
 
@@ -19,12 +19,9 @@ module JSON::LD
19
19
  # @return [Array, Hash]
20
20
  def compact(element,
21
21
  base: nil,
22
- property: nil)
23
- #if property.nil?
24
- # log_debug("compact") {"element: #{element.inspect}, ec: #{context.inspect}"}
25
- #else
26
- # log_debug("compact") {"property: #{property.inspect}"}
27
- #end
22
+ property: nil,
23
+ log_depth: nil)
24
+ log_debug("compact", depth: log_depth.to_i) {"element: #{element.inspect}, ec: #{context.inspect}"}
28
25
 
29
26
  # If the term definition for active property itself contains a context, use that for compacting values.
30
27
  input_context = self.context
@@ -33,7 +30,7 @@ module JSON::LD
33
30
  when Array
34
31
  #log_debug("") {"Array #{element.inspect}"}
35
32
  result = element.map do |item|
36
- compact(item, base: base, property: property)
33
+ compact(item, base: base, property: property, log_depth: log_depth.to_i + 1)
37
34
  end.compact
38
35
 
39
36
  # If element has a single member and the active property has no
@@ -41,10 +38,10 @@ module JSON::LD
41
38
  # member; otherwise the compacted value is element
42
39
  if result.length == 1 &&
43
40
  !context.as_array?(property) && @options[:compactArrays]
44
- #log_debug("=> extract single element: #{result.first.inspect}")
41
+ log_debug("=> extract single element", depth: log_depth.to_i) {result.first.inspect}
45
42
  result.first
46
43
  else
47
- #log_debug("=> array result: #{result.inspect}")
44
+ log_debug("=> array result", depth: log_depth.to_i) {result.inspect}
48
45
  result
49
46
  end
50
47
  when Hash
@@ -55,6 +52,7 @@ module JSON::LD
55
52
 
56
53
  # Revert any previously type-scoped (non-preserved) context
57
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}"}
58
56
  self.context = context.previous_context
59
57
  end
60
58
 
@@ -63,12 +61,13 @@ module JSON::LD
63
61
  if td && !td.context.nil?
64
62
  self.context = context.parse(td.context,
65
63
  override_protected: true)
64
+ log_debug("prop-scoped", depth: log_depth.to_i) {"context: #{self.context.inspect}"}
66
65
  end
67
66
 
68
- if element.key?('@id') || element.key?('@value')
67
+ if (element.key?('@id') || element.key?('@value')) && !element.key?('@annotation')
69
68
  result = context.compact_value(property, element, base: @options[:base])
70
69
  if !result.is_a?(Hash) || context.coerce(property) == '@json'
71
- #log_debug("") {"=> scalar result: #{result.inspect}"}
70
+ log_debug("", depth: log_depth.to_i) {"=> scalar result: #{result.inspect}"}
72
71
  return result
73
72
  end
74
73
  end
@@ -76,7 +75,8 @@ module JSON::LD
76
75
  # If expanded property is @list and we're contained within a list container, recursively compact this item to an array
77
76
  if list?(element) && context.container(property).include?('@list')
78
77
  return compact(element['@list'], base: base,
79
- property: property)
78
+ property: property,
79
+ log_depth: log_depth.to_i + 1)
80
80
  end
81
81
 
82
82
  inside_reverse = property == '@reverse'
@@ -90,15 +90,23 @@ module JSON::LD
90
90
  each do |term|
91
91
  term_context = input_context.term_definitions[term].context if input_context.term_definitions[term]
92
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}"}
93
94
  end
94
95
 
95
96
  element.keys.opt_sort(ordered: @options[:ordered]).each do |expanded_property|
96
97
  expanded_value = element[expanded_property]
97
- #log_debug("") {"#{expanded_property}: #{expanded_value.inspect}"}
98
+ log_debug("", depth: log_depth.to_i) {"#{expanded_property}: #{expanded_value.inspect}"}
98
99
 
99
100
  if expanded_property == '@id'
100
- compacted_value = Array(expanded_value).map do |expanded_id|
101
- context.compact_iri(expanded_id, base: @options[:base])
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-star
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
102
110
  end
103
111
 
104
112
  kw_alias = context.compact_iri('@id', vocab: true)
@@ -124,8 +132,9 @@ module JSON::LD
124
132
 
125
133
  if expanded_property == '@reverse'
126
134
  compacted_value = compact(expanded_value, base: base,
127
- property: '@reverse')
128
- #log_debug("@reverse") {"compacted_value: #{compacted_value.inspect}"}
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}"}
129
138
  # handle double-reversed properties
130
139
  compacted_value.each do |prop, value|
131
140
  if context.reverse?(prop)
@@ -136,8 +145,8 @@ module JSON::LD
136
145
  end
137
146
 
138
147
  unless compacted_value.empty?
139
- al = context.compact_iri('@reverse')
140
- #log_debug("") {"remainder: #{al} => #{compacted_value.inspect}"}
148
+ al = context.compact_iri('@reverse', vocab: true)
149
+ log_debug("", depth: log_depth.to_i) {"remainder: #{al} => #{compacted_value.inspect}"}
141
150
  result[al] = compacted_value
142
151
  end
143
152
  next
@@ -146,8 +155,9 @@ module JSON::LD
146
155
  if expanded_property == '@preserve'
147
156
  # Compact using `property`
148
157
  compacted_value = compact(expanded_value, base: base,
149
- property: property)
150
- #log_debug("@preserve") {"compacted_value: #{compacted_value.inspect}"}
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}"}
151
161
 
152
162
  unless compacted_value.is_a?(Array) && compacted_value.empty?
153
163
  result['@preserve'] = compacted_value
@@ -156,14 +166,14 @@ module JSON::LD
156
166
  end
157
167
 
158
168
  if expanded_property == '@index' && context.container(property).include?('@index')
159
- #log_debug("@index") {"drop @index"}
169
+ log_debug("@index", depth: log_depth.to_i) {"drop @index"}
160
170
  next
161
171
  end
162
172
 
163
173
  # Otherwise, if expanded property is @direction, @index, @value, or @language:
164
174
  if EXPANDED_PROPERTY_DIRECTION_INDEX_LANGUAGE_VALUE.include?(expanded_property)
165
175
  al = context.compact_iri(expanded_property, vocab: true)
166
- #log_debug(expanded_property) {"#{al} => #{expanded_value.inspect}"}
176
+ log_debug(expanded_property, depth: log_depth.to_i) {"#{al} => #{expanded_value.inspect}"}
167
177
  result[al] = expanded_value
168
178
  next
169
179
  end
@@ -211,8 +221,9 @@ module JSON::LD
211
221
  end
212
222
 
213
223
  compacted_item = compact(value, base: base,
214
- property: item_active_property)
215
- #log_debug("") {" => compacted key: #{item_active_property.inspect} for #{compacted_item.inspect}"}
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}"}
216
227
 
217
228
  # handle @list
218
229
  if list?(expanded_item)
@@ -220,7 +231,7 @@ module JSON::LD
220
231
  unless container.include?('@list')
221
232
  al = context.compact_iri('@list', vocab: true)
222
233
  compacted_item = {al => compacted_item}
223
- if expanded_item.has_key?('@index')
234
+ if expanded_item.key?('@index')
224
235
  key = context.compact_iri('@index', vocab: true)
225
236
  compacted_item[key] = expanded_item['@index']
226
237
  end
@@ -265,7 +276,7 @@ module JSON::LD
265
276
  al = context.compact_iri('@id', vocab: true)
266
277
  compacted_item[al] = context.compact_iri(expanded_item['@id'], vocab: false)
267
278
  end
268
- if expanded_item.has_key?('@index')
279
+ if expanded_item.key?('@index')
269
280
  key = context.compact_iri('@index', vocab: true)
270
281
  compacted_item[key] = expanded_item['@index']
271
282
  end
@@ -315,7 +326,8 @@ module JSON::LD
315
326
  if compacted_item.keys.length == 1 && expanded_item.keys.include?('@id')
316
327
  compacted_item = compact({'@id' => expanded_item['@id']},
317
328
  base: base,
318
- property: item_active_property)
329
+ property: item_active_property,
330
+ log_depth: log_depth.to_i + 1)
319
331
  end
320
332
  compacted_item
321
333
  end
@@ -332,7 +344,7 @@ module JSON::LD
332
344
  result
333
345
  else
334
346
  # For other types, the compacted value is the element value
335
- #log_debug("compact") {element.class.to_s}
347
+ log_debug("compact", depth: log_depth.to_i) {element.class.to_s}
336
348
  element
337
349
  end
338
350
 
@@ -46,7 +46,7 @@ module JSON::LD
46
46
  #
47
47
  # @param [Hash{String => String}] env
48
48
  # @return [Array(Integer, Hash, #each)] Status, Headers and Body
49
- # @see http://rack.rubyforge.org/doc/SPEC.html
49
+ # @see https://rubydoc.info/github/rack/rack/file/SPEC
50
50
  def call(env)
51
51
  response = app.call(env)
52
52
  body = response[2].respond_to?(:body) ? response[2].body : response[2]
@@ -104,16 +104,12 @@ module JSON::LD
104
104
  # @see #initialize
105
105
  # @see #parse
106
106
  # @param [String, #read, Array, Hash, Context] local_context
107
- # @param [String, #to_s] :base (nil)
107
+ # @param [String, #to_s] base (nil)
108
108
  # 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.
109
- # @param [Proc] :documentLoader (nil)
110
- # The callback of the loader to be used to retrieve remote documents and contexts. If specified, it must be used to retrieve remote documents and contexts; otherwise, if not specified, the processor's built-in loader must be used. See {API.documentLoader} for the method signature.
111
109
  # @param [Boolean] override_protected (false)
112
110
  # Protected terms may be cleared.
113
111
  # @param [Boolean] propagate (true)
114
112
  # If false, retains any previously defined term, which can be rolled back when the descending into a new node object changes.
115
- # @param [Boolean] validate (false)
116
- # Extra validatation
117
113
  # @raise [JsonLdError]
118
114
  # on a remote context load error, syntax error, or a reference to a term which is not defined.
119
115
  # @return [Context]
@@ -231,14 +227,12 @@ module JSON::LD
231
227
  #
232
228
  #
233
229
  # @param [String, #read, Array, Hash, Context] local_context
234
- # @param [String, #to_s] :base
230
+ # @param [String, #to_s] base
235
231
  # 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.
236
232
  # @param [Boolean] override_protected Protected terms may be cleared.
237
233
  # @param [Boolean] propagate (true)
238
234
  # If false, retains any previously defined term, which can be rolled back when the descending into a new node object changes.
239
235
  # @param [Array<String>] remote_contexts ([])
240
- # @param [Boolean] validate (false)
241
- # Extra validatation
242
236
  # @param [Boolean] validate_scoped (true).
243
237
  # Validate scoped context, loading if necessary.
244
238
  # If false, do not load scoped contexts.
@@ -320,7 +314,7 @@ module JSON::LD
320
314
  #context_opts.delete(:headers)
321
315
  JSON::LD::API.loadRemoteDocument(context.to_s, **context_opts) do |remote_doc|
322
316
  # 3.2.5) Dereference context. If the dereferenced document has no top-level JSON object with an @context member, an invalid remote context has been detected and processing is aborted; otherwise, set context to the value of that member.
323
- raise JsonLdError::InvalidRemoteContext, "#{context}" unless remote_doc.document.is_a?(Hash) && remote_doc.document.has_key?('@context')
317
+ raise JsonLdError::InvalidRemoteContext, "#{context}" unless remote_doc.document.is_a?(Hash) && remote_doc.document.key?('@context')
324
318
 
325
319
  # Parse stand-alone
326
320
  ctx = Context.new(unfrozen: true, **options).dup
@@ -358,7 +352,7 @@ module JSON::LD
358
352
  '@propagate' => :propagate=,
359
353
  '@vocab' => :vocab=,
360
354
  }.each do |key, setter|
361
- next unless context.has_key?(key)
355
+ next unless context.key?(key)
362
356
  if key == '@import'
363
357
  # Retrieve remote context and merge the remaining context object into the result.
364
358
  raise JsonLdError::InvalidContextEntry, "@import may only be used in 1.1 mode}" if result.processingMode("json-ld-1.0")
@@ -373,11 +367,11 @@ module JSON::LD
373
367
  # FIXME: should cache this, but ContextCache is for parsed contexts
374
368
  JSON::LD::API.loadRemoteDocument(import_loc, **context_opts) do |remote_doc|
375
369
  # Dereference import_loc. If the dereferenced document has no top-level JSON object with an @context member, an invalid remote context has been detected and processing is aborted; otherwise, set context to the value of that member.
376
- raise JsonLdError::InvalidRemoteContext, "#{import_loc}" unless remote_doc.document.is_a?(Hash) && remote_doc.document.has_key?('@context')
370
+ raise JsonLdError::InvalidRemoteContext, "#{import_loc}" unless remote_doc.document.is_a?(Hash) && remote_doc.document.key?('@context')
377
371
  import_context = remote_doc.document['@context']
378
372
  import_context.delete('@base')
379
373
  raise JsonLdError::InvalidRemoteContext, "#{import_context.to_json} must be an object" unless import_context.is_a?(Hash)
380
- raise JsonLdError::InvalidContextEntry, "#{import_context.to_json} must not include @import entry" if import_context.has_key?('@import')
374
+ raise JsonLdError::InvalidContextEntry, "#{import_context.to_json} must not include @import entry" if import_context.key?('@import')
381
375
  context.delete(key)
382
376
  context = import_context.merge(context)
383
377
  end
@@ -419,9 +413,7 @@ module JSON::LD
419
413
  # Merge in a context, creating a new context with updates from `context`
420
414
  #
421
415
  # @param [Context] context
422
- # @param [Boolean] protected mark resulting context as protected
423
416
  # @param [Boolean] override_protected Allow or disallow protected terms to be changed
424
- # @param [Boolean]
425
417
  # @return [Context]
426
418
  def merge(context, override_protected: false)
427
419
  ctx = Context.new(term_definitions: self.term_definitions, standard_prefixes: options[:standard_prefixes])
@@ -469,8 +461,6 @@ module JSON::LD
469
461
  # @param [String, RDF::URI] base for resolving document-relative IRIs
470
462
  # @param [Boolean] protected if true, causes all terms to be marked protected
471
463
  # @param [Boolean] override_protected Protected terms may be cleared.
472
- # @param [Boolean] propagate
473
- # Context is propagated across node objects.
474
464
  # @param [Array<String>] remote_contexts
475
465
  # @param [Boolean] validate_scoped (true).
476
466
  # Validate scoped context, loading if necessary.
@@ -552,7 +542,7 @@ module JSON::LD
552
542
  # Potentially note that the term is protected
553
543
  definition.protected = value.fetch('@protected', protected)
554
544
 
555
- if value.has_key?('@type')
545
+ if value.key?('@type')
556
546
  type = value['@type']
557
547
  # SPEC FIXME: @type may be nil
558
548
  type = case type
@@ -576,7 +566,7 @@ module JSON::LD
576
566
  definition.type_mapping = type
577
567
  end
578
568
 
579
- if value.has_key?('@reverse')
569
+ if value.key?('@reverse')
580
570
  raise JsonLdError::InvalidReverseProperty, "unexpected key in #{value.inspect} on term #{term.inspect}" if
581
571
  value.key?('@id') || value.key?('@nest')
582
572
  raise JsonLdError::InvalidIRIMapping, "expected value of @reverse to be a string: #{value['@reverse'].inspect} on term #{term.inspect}" unless
@@ -602,7 +592,7 @@ module JSON::LD
602
592
  warn "[DEPRECATION] Blank Node terms deprecated in JSON-LD 1.1." if @options[:validate] && processingMode('json-ld-1.1') && definition.id.to_s.start_with?("_:")
603
593
 
604
594
  # If value contains an @container member, set the container mapping of definition to its value; if its value is neither @set, @index, @type, @id, an absolute IRI nor null, an invalid reverse property error has been detected (reverse properties only support set- and index-containers) and processing is aborted.
605
- if value.has_key?('@container')
595
+ if value.key?('@container')
606
596
  container = value['@container']
607
597
  raise JsonLdError::InvalidReverseProperty,
608
598
  "unknown mapping for '@container' to #{container.inspect} on term #{term.inspect}" unless
@@ -610,9 +600,9 @@ module JSON::LD
610
600
  definition.container_mapping = check_container(container, local_context, defined, term)
611
601
  end
612
602
  definition.reverse_property = true
613
- elsif value.has_key?('@id') && value['@id'].nil?
603
+ elsif value.key?('@id') && value['@id'].nil?
614
604
  # Allowed to reserve a null term, which may be protected
615
- elsif value.has_key?('@id') && value['@id'] != term
605
+ elsif value.key?('@id') && value['@id'] != term
616
606
  raise JsonLdError::InvalidIRIMapping, "expected value of @id to be a string: #{value['@id'].inspect} on term #{term.inspect}" unless
617
607
  value['@id'].is_a?(String)
618
608
 
@@ -647,7 +637,7 @@ module JSON::LD
647
637
  elsif term[1..-1].include?(':')
648
638
  # If term is a compact IRI with a prefix that is a key in local context then a dependency has been found. Use this algorithm recursively passing active context, local context, the prefix as term, and defined.
649
639
  prefix, suffix = term.split(':', 2)
650
- create_term_definition(local_context, prefix, defined, protected: protected) if local_context.has_key?(prefix)
640
+ create_term_definition(local_context, prefix, defined, protected: protected) if local_context.key?(prefix)
651
641
 
652
642
  definition.id = if td = term_definitions[prefix]
653
643
  # If term's prefix has a term definition in active context, set the IRI mapping for definition to the result of concatenating the value associated with the prefix's IRI mapping and the term's suffix.
@@ -674,7 +664,7 @@ module JSON::LD
674
664
 
675
665
  @iri_to_term[definition.id] = term if simple_term && definition.id
676
666
 
677
- if value.has_key?('@container')
667
+ if value.key?('@container')
678
668
  #log_debug("") {"container_mapping: #{value['@container'].inspect}"}
679
669
  definition.container_mapping = check_container(value['@container'], local_context, defined, term)
680
670
 
@@ -689,14 +679,14 @@ module JSON::LD
689
679
  end
690
680
  end
691
681
 
692
- if value.has_key?('@index')
682
+ if value.key?('@index')
693
683
  # property-based indexing
694
684
  raise JsonLdError::InvalidTermDefinition, "@index without @index in @container: #{value['@index']} on term #{term.inspect}" unless definition.container_mapping.include?('@index')
695
685
  raise JsonLdError::InvalidTermDefinition, "@index must expand to an IRI: #{value['@index']} on term #{term.inspect}" unless value['@index'].is_a?(String) && !value['@index'].start_with?('@')
696
686
  definition.index = value['@index'].to_s
697
687
  end
698
688
 
699
- if value.has_key?('@context')
689
+ if value.key?('@context')
700
690
  begin
701
691
  new_ctx = self.parse(value['@context'],
702
692
  base: base,
@@ -714,7 +704,7 @@ module JSON::LD
714
704
  end
715
705
  end
716
706
 
717
- if value.has_key?('@language')
707
+ if value.key?('@language')
718
708
  language = value['@language']
719
709
  language = case value['@language']
720
710
  when String
@@ -732,14 +722,14 @@ module JSON::LD
732
722
  definition.language_mapping = language || false
733
723
  end
734
724
 
735
- if value.has_key?('@direction')
725
+ if value.key?('@direction')
736
726
  direction = value['@direction']
737
727
  raise JsonLdError::InvalidBaseDirection, "direction must be null, 'ltr', or 'rtl', was #{language.inspect}} on term #{term.inspect}" unless direction.nil? || %w(ltr rtl).include?(direction)
738
728
  #log_debug("") {"direction_mapping: #{direction.inspect}"}
739
729
  definition.direction_mapping = direction || false
740
730
  end
741
731
 
742
- if value.has_key?('@nest')
732
+ if value.key?('@nest')
743
733
  nest = value['@nest']
744
734
  raise JsonLdError::InvalidNestValue, "nest must be a string, was #{nest.inspect}} on term #{term.inspect}" unless nest.is_a?(String)
745
735
  raise JsonLdError::InvalidNestValue, "nest must not be a keyword other than @nest, was #{nest.inspect}} on term #{term.inspect}" if nest.match?(/^@[a-zA-Z]+$/) && nest != '@nest'
@@ -747,7 +737,7 @@ module JSON::LD
747
737
  definition.nest = nest
748
738
  end
749
739
 
750
- if value.has_key?('@prefix')
740
+ if value.key?('@prefix')
751
741
  raise JsonLdError::InvalidTermDefinition, "@prefix used on compact or relative IRI term #{term.inspect}" if term.match?(%r{:|/})
752
742
  case pfx = value['@prefix']
753
743
  when TrueClass, FalseClass
@@ -846,7 +836,7 @@ module JSON::LD
846
836
  # If contex has a @version member, it's value MUST be 1.1, otherwise an "invalid @version value" has been detected, and processing is aborted.
847
837
  # If processingMode has been set, and it is not "json-ld-1.1", a "processing mode conflict" has been detecting, and processing is aborted.
848
838
  #
849
- # @param [String, Number] expected
839
+ # @param [String, Number] value
850
840
  # @return [String]
851
841
  # @raise [JsonLdError::ProcessingModeConflict]
852
842
  def processingMode=(value = nil, **options)
@@ -1028,7 +1018,7 @@ module JSON::LD
1028
1018
 
1029
1019
  term_sym = term.empty? ? "" : term.to_sym
1030
1020
  iri_to_term.delete(term_definitions[term].id.to_s) if term_definitions[term].id.is_a?(String)
1031
- @options[:prefixes][term_sym] = value if @options.has_key?(:prefixes)
1021
+ @options[:prefixes][term_sym] = value if @options.key?(:prefixes)
1032
1022
  iri_to_term[value.to_s] = term
1033
1023
  term_definitions[term]
1034
1024
  end
@@ -1048,7 +1038,7 @@ module JSON::LD
1048
1038
  # @param [Term, #to_s] term in unexpanded form
1049
1039
  # @return [Array<'@index', '@language', '@index', '@set', '@type', '@id', '@graph'>]
1050
1040
  def container(term)
1051
- return [term] if term == '@list'
1041
+ return Set[term] if term == '@list'
1052
1042
  term = find_definition(term)
1053
1043
  term ? term.container_mapping : Set.new
1054
1044
  end
@@ -1144,7 +1134,7 @@ module JSON::LD
1144
1134
  # @return [Term] related term definition
1145
1135
  def reverse_term(term)
1146
1136
  # Direct lookup of term
1147
- term = term_definitions[term.to_s] if term_definitions.has_key?(term.to_s) && !term.is_a?(TermDefinition)
1137
+ term = term_definitions[term.to_s] if term_definitions.key?(term.to_s) && !term.is_a?(TermDefinition)
1148
1138
 
1149
1139
  # Lookup term, assuming term is an IRI
1150
1140
  unless term.is_a?(TermDefinition)
@@ -1192,7 +1182,7 @@ module JSON::LD
1192
1182
  defined = defined || {} # if we initialized in the keyword arg we would allocate {} at each invokation, even in the 2 (common) early returns above.
1193
1183
 
1194
1184
  # If local context is not null, it contains a key that equals value, and the value associated with the key that equals value in defined is not true, then invoke the Create Term Definition subalgorithm, passing active context, local context, value as term, and defined. This will ensure that a term definition is created for value in active context during Context Processing.
1195
- if local_context && local_context.has_key?(value) && !defined[value]
1185
+ if local_context && local_context.key?(value) && !defined[value]
1196
1186
  create_term_definition(local_context, value, defined)
1197
1187
  end
1198
1188
 
@@ -1222,7 +1212,7 @@ module JSON::LD
1222
1212
  end
1223
1213
 
1224
1214
  # If local context is not null, it contains a key that equals prefix, and the value associated with the key that equals prefix in defined is not true, invoke the Create Term Definition algorithm, passing active context, local context, prefix as term, and defined. This will ensure that a term definition is created for prefix in active context during Context Processing.
1225
- if local_context && local_context.has_key?(prefix) && !defined[prefix]
1215
+ if local_context && local_context.key?(prefix) && !defined[prefix]
1226
1216
  create_term_definition(local_context, prefix, defined)
1227
1217
  end
1228
1218
 
@@ -1297,7 +1287,7 @@ module JSON::LD
1297
1287
  return if iri.nil?
1298
1288
  iri = iri.to_s
1299
1289
 
1300
- if vocab && inverse_context.has_key?(iri)
1290
+ if vocab && inverse_context.key?(iri)
1301
1291
  default_language = if self.default_direction
1302
1292
  "#{self.default_language}_#{self.default_direction}".downcase
1303
1293
  else
@@ -1308,7 +1298,7 @@ module JSON::LD
1308
1298
  containers.concat(CONTAINERS_INDEX_SET) if index?(value) && !graph?(value)
1309
1299
 
1310
1300
  # If the value is a JSON Object with the key @preserve, use the value of @preserve.
1311
- value = value['@preserve'].first if value.is_a?(Hash) && value.has_key?('@preserve')
1301
+ value = value['@preserve'].first if value.is_a?(Hash) && value.key?('@preserve')
1312
1302
 
1313
1303
  if reverse
1314
1304
  tl, tl_value = "@type", "@reverse"
@@ -1322,11 +1312,11 @@ module JSON::LD
1322
1312
  list.each do |item|
1323
1313
  item_language, item_type = "@none", "@none"
1324
1314
  if value?(item)
1325
- if item.has_key?('@direction')
1315
+ if item.key?('@direction')
1326
1316
  item_language = "#{item['@language']}_#{item['@direction']}".downcase
1327
- elsif item.has_key?('@language')
1317
+ elsif item.key?('@language')
1328
1318
  item_language = item['@language'].downcase
1329
- elsif item.has_key?('@type')
1319
+ elsif item.key?('@type')
1330
1320
  item_type = item['@type']
1331
1321
  else
1332
1322
  item_language = "@null"
@@ -1354,14 +1344,14 @@ module JSON::LD
1354
1344
  elsif graph?(value)
1355
1345
  # Prefer @index and @id containers, then @graph, then @index
1356
1346
  containers.concat(CONTAINERS_GRAPH_INDEX_INDEX) if index?(value)
1357
- containers.concat(CONTAINERS_GRAPH) if value.has_key?('@id')
1347
+ containers.concat(CONTAINERS_GRAPH) if value.key?('@id')
1358
1348
 
1359
1349
  # Prefer an @graph container next
1360
1350
  containers.concat(CONTAINERS_GRAPH_SET)
1361
1351
 
1362
1352
  # Lastly, in 1.1, any graph can be indexed on @index or @id, so add if we haven't already
1363
1353
  containers.concat(CONTAINERS_GRAPH_INDEX) unless index?(value)
1364
- containers.concat(CONTAINERS_GRAPH) unless value.has_key?('@id')
1354
+ containers.concat(CONTAINERS_GRAPH) unless value.key?('@id')
1365
1355
  containers.concat(CONTAINERS_INDEX_SET) unless index?(value)
1366
1356
  containers << '@set'
1367
1357
 
@@ -1369,13 +1359,13 @@ module JSON::LD
1369
1359
  else
1370
1360
  if value?(value)
1371
1361
  # In 1.1, an language map can be used to index values using @none
1372
- if value.has_key?('@language') && !index?(value)
1362
+ if value.key?('@language') && !index?(value)
1373
1363
  tl_value = value['@language'].downcase
1374
1364
  tl_value += "_#{value['@direction']}" if value['@direction']
1375
1365
  containers.concat(CONTAINERS_LANGUAGE)
1376
- elsif value.has_key?('@direction') && !index?(value)
1366
+ elsif value.key?('@direction') && !index?(value)
1377
1367
  tl_value = "_#{value['@direction']}"
1378
- elsif value.has_key?('@type')
1368
+ elsif value.key?('@type')
1379
1369
  tl_value = value['@type']
1380
1370
  tl = '@type'
1381
1371
  end
@@ -1397,7 +1387,7 @@ module JSON::LD
1397
1387
  tl_value ||= '@null'
1398
1388
  preferred_values = []
1399
1389
  preferred_values << '@reverse' if tl_value == '@reverse'
1400
- if (tl_value == '@id' || tl_value == '@reverse') && value.is_a?(Hash) && value.has_key?('@id')
1390
+ if (tl_value == '@id' || tl_value == '@reverse') && value.is_a?(Hash) && value.key?('@id')
1401
1391
  t_iri = compact_iri(value['@id'], vocab: true, base: base)
1402
1392
  if (r_td = term_definitions[t_iri]) && r_td.id == value['@id']
1403
1393
  preferred_values.concat(CONTAINERS_VOCAB_ID)
@@ -1423,7 +1413,7 @@ module JSON::LD
1423
1413
  # At this point, there is no simple term that iri can be compacted to. If vocab is true and active context has a vocabulary mapping:
1424
1414
  if vocab && self.vocab && iri.start_with?(self.vocab) && iri.length > self.vocab.length
1425
1415
  suffix = iri[self.vocab.length..-1]
1426
- return suffix unless term_definitions.has_key?(suffix)
1416
+ return suffix unless term_definitions.key?(suffix)
1427
1417
  end
1428
1418
 
1429
1419
  # The iri could not be compacted using the active context's vocabulary mapping. Try to create a compact IRI, starting by initializing compact IRI to null. This variable will be used to tore the created compact IRI, if any.
@@ -1437,7 +1427,7 @@ module JSON::LD
1437
1427
 
1438
1428
  suffix = iri[td.id.length..-1]
1439
1429
  ciri = "#{term}:#{suffix}"
1440
- candidates << ciri unless value && term_definitions.has_key?(ciri)
1430
+ candidates << ciri unless value && term_definitions.key?(ciri)
1441
1431
  end
1442
1432
 
1443
1433
  return candidates.sort.first if !candidates.empty?
@@ -1465,6 +1455,8 @@ module JSON::LD
1465
1455
  if !vocab
1466
1456
  # transform iri to a relative IRI using the document's base IRI
1467
1457
  iri = remove_base(self.base || base, iri)
1458
+ # Make . relative if it has the form of a keyword.
1459
+ iri = "./#{iri}" if iri.match?(/^@[a-zA-Z]+$/)
1468
1460
  return iri
1469
1461
  else
1470
1462
  return iri
@@ -1533,16 +1525,16 @@ module JSON::LD
1533
1525
  res['@language'] = lang
1534
1526
  end
1535
1527
  res['@direction'] = dir
1536
- elsif useNativeTypes && RDF_LITERAL_NATIVE_TYPES.include?(value.datatype)
1528
+ elsif useNativeTypes && RDF_LITERAL_NATIVE_TYPES.include?(value.datatype) && value.valid?
1537
1529
  res['@type'] = uri(coerce(property)) if coerce(property)
1538
1530
  res['@value'] = value.object
1539
1531
  else
1540
- value.canonicalize! if value.datatype == RDF::XSD.double
1532
+ value.canonicalize! if value.valid? && value.datatype == RDF::XSD.double
1541
1533
  if coerce(property)
1542
1534
  res['@type'] = uri(coerce(property)).to_s
1543
- elsif value.has_datatype?
1535
+ elsif value.datatype?
1544
1536
  res['@type'] = uri(value.datatype).to_s
1545
- elsif value.has_language? || language(property)
1537
+ elsif value.language? || language(property)
1546
1538
  res['@language'] = (value.language || language(property)).to_s
1547
1539
  end
1548
1540
  res['@value'] = value.to_s
@@ -1590,15 +1582,15 @@ module JSON::LD
1590
1582
  direction = direction(property)
1591
1583
 
1592
1584
  result = case
1593
- when coerce(property) == '@id' && value.has_key?('@id') && (value.keys - %w(@id @index)).empty?
1585
+ when coerce(property) == '@id' && value.key?('@id') && (value.keys - %w(@id @index)).empty?
1594
1586
  # Compact an @id coercion
1595
1587
  #log_debug("") {" (@id & coerce)"}
1596
1588
  compact_iri(value['@id'], base: base)
1597
- when coerce(property) == '@vocab' && value.has_key?('@id') && (value.keys - %w(@id @index)).empty?
1589
+ when coerce(property) == '@vocab' && value.key?('@id') && (value.keys - %w(@id @index)).empty?
1598
1590
  # Compact an @id coercion
1599
1591
  #log_debug("") {" (@id & coerce & vocab)"}
1600
1592
  compact_iri(value['@id'], vocab: true)
1601
- when value.has_key?('@id')
1593
+ when value.key?('@id')
1602
1594
  #log_debug("") {" (@id)"}
1603
1595
  # return value as is
1604
1596
  value
@@ -1619,7 +1611,7 @@ module JSON::LD
1619
1611
  value
1620
1612
  end
1621
1613
 
1622
- if result.is_a?(Hash) && result.has_key?('@type') && value['@type'] != '@json'
1614
+ if result.is_a?(Hash) && result.key?('@type') && value['@type'] != '@json'
1623
1615
  # Compact values of @type
1624
1616
  c_type = if result['@type'].is_a?(Array)
1625
1617
  result['@type'].map {|t| compact_iri(t, vocab: true)}
@@ -1867,11 +1859,11 @@ module JSON::LD
1867
1859
  container_map = inverse_context[iri]
1868
1860
  #log_debug(" ") {"container_map: #{container_map.inspect}"}
1869
1861
  containers.each do |container|
1870
- next unless container_map.has_key?(container)
1862
+ next unless container_map.key?(container)
1871
1863
  tl_map = container_map[container]
1872
1864
  value_map = tl_map[type_language]
1873
1865
  preferred_values.each do |item|
1874
- next unless value_map.has_key?(item)
1866
+ next unless value_map.key?(item)
1875
1867
  #log_debug("=>") {value_map[item].inspect}
1876
1868
  return value_map[item]
1877
1869
  end
@@ -2064,7 +2056,7 @@ module JSON::LD
2064
2056
  # @param ["ltr", "rtl"] direction_mapping
2065
2057
  # Direction mapping of term, `false` is used if there is an explicit direction mapping for this term
2066
2058
  # @param [Boolean] reverse_property
2067
- # @param [Boolean] protected
2059
+ # @param [Boolean] protected mark resulting context as protected
2068
2060
  # @param [String] nest term used for nest properties
2069
2061
  # @param [Boolean] simple
2070
2062
  # This is a simple term definition, not an expanded term definition