json-ld 2.0.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d460373a6fabcbbd48f31b6b2dc12d473882802d
4
- data.tar.gz: 58f8dd9acb20a4f46241100a178217dc31dba372
3
+ metadata.gz: 693e380cfc75c04525a1144d742a4c98737df5bf
4
+ data.tar.gz: a6d0d85e8323532379bfeef2be95361b1a5c1a01
5
5
  SHA512:
6
- metadata.gz: d1af7beafcff3c9008b04d8efeebae47a7786296decd2f1d231d5ba4ecedda764f495927d0dc52a22cdde6ac8186299ed569a673ca3f78c0ebfe0f2dadb689f5
7
- data.tar.gz: 168dbef959bf418880c42357e5bca85d9bb0688fce3037f22b0e6f6b98166a053377c912606e3a08ffdf6fcf4193b3dfd83dc272357607a694169c779e91a589
6
+ metadata.gz: d18d9ad16dc730a96037c4b5f8b1926910d75820a94529a1287fe82dcc1e251b1c73fce936ba3988c6672f5615e8361298971018d24525ce51dd1c2667062492
7
+ data.tar.gz: 34cfa1b4561de877b8eb94513a42c8c383f8ce037a03c67f9535cbd1a71ed4a9023e71a70a7ba8bb79b73c7c9e49827d664028af416a7f1b489f25a3dd821e8d
data/README.md CHANGED
@@ -240,6 +240,17 @@ Then, when performing something like expansion:
240
240
 
241
241
  JSON::LD::API.expand(input, documentLoader: load_document_local)
242
242
 
243
+
244
+ ## Preloading contexts
245
+ In many cases, for small documents, processing time can be dominated by loading and parsing remote contexts. In particular, a small schema.org example may need to download a large context and turn it into an internal representation, before the actual document can be expanded for processing. Using {JSON::LD::Context.add_preloaded}, an implementation can perform this loading up-front, and make it available to the processor.
246
+
247
+ ctx = JSON::LD::Context.new().parse('http://schema.org/')
248
+ JSON::LD::Context.add_preloaded('http://schema.org/', ctx)
249
+
250
+ On lookup, URIs with an `https` prefix are normalized to `http`.
251
+
252
+ A context may be serialized to Ruby to speed this process using `Context#to_rb`. When loaded, this generated file will add entries to the {JSON::LD::Context::PRELOADED}.
253
+
243
254
  ## RDF Reader and Writer
244
255
  {JSON::LD} also acts as a normal RDF reader and writer, using the standard RDF.rb reader/writer interfaces:
245
256
 
@@ -274,7 +285,7 @@ Note, the API method signatures differed in versions before 1.0, in that they al
274
285
  * {JSON::LD::Writer}
275
286
 
276
287
  ## Dependencies
277
- * [Ruby](http://ruby-lang.org/) (>= 2.0)
288
+ * [Ruby](http://ruby-lang.org/) (>= 2.2.2)
278
289
  * [RDF.rb](http://rubygems.org/gems/rdf) (>= 2.0)
279
290
  * [JSON](https://rubygems.org/gems/json) (>= 1.5)
280
291
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.0.1
1
+ 2.1.0
@@ -1,4 +1,6 @@
1
- $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..')))
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
3
+ $:.unshift(File.expand_path("../ld", __FILE__))
2
4
  require 'rdf' # @see http://rubygems.org/gems/rdf
3
5
  require 'multi_json'
4
6
 
@@ -1,3 +1,5 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
1
3
  require 'openssl'
2
4
  require 'json/ld/expand'
3
5
  require 'json/ld/compact'
@@ -70,12 +72,6 @@ module JSON::LD
70
72
  # If set to a value that is not `false`, the JSON-LD processor must modify the output of the Compaction Algorithm or the Expansion Algorithm by coalescing all properties associated with each subject via the Flattening Algorithm. The value of `flatten must` be either an _IRI_ value representing the name of the graph to flatten, or `true`. If the value is `true`, then the first graph encountered in the input document is selected and flattened.
71
73
  # @option options [String] :processingMode ("json-ld-1.0")
72
74
  # If set to "json-ld-1.0", the JSON-LD processor must produce exactly the same results as the algorithms defined in this specification. If set to another value, the JSON-LD processor is allowed to extend or modify the algorithms defined in this specification to enable application-specific optimizations. The definition of such optimizations is beyond the scope of this specification and thus not defined. Consequently, different implementations may implement different optimizations. Developers must not define modes beginning with json-ld as they are reserved for future versions of this specification.
73
- # @option options [String] :produceGeneralizedRdf (false)
74
- # Unless the produce generalized RDF flag is set to true, RDF triples containing a blank node predicate are excluded from output.
75
- # @option options [Boolean] :useNativeTypes (false)
76
- # If set to `true`, the JSON-LD processor will use native datatypes for expression xsd:integer, xsd:boolean, and xsd:double values, otherwise, it will use the expanded form.
77
- # @option options [Boolean] :useRdfType (false)
78
- # If set to `true`, the JSON-LD processor will treat `rdf:type` like a normal property instead of using `@type`.
79
75
  # @option options [Boolean] :rename_bnodes (true)
80
76
  # Rename bnodes as part of expansion, or keep them the same.
81
77
  # @option options [Boolean] :unique_bnodes (false)
@@ -141,6 +137,9 @@ module JSON::LD
141
137
  end
142
138
  end
143
139
  end
140
+
141
+ # This is used internally only
142
+ private :initialize
144
143
 
145
144
  ##
146
145
  # Expands the given input according to the steps in the Expansion Algorithm. The input must be copied, expanded and returned
@@ -152,6 +151,8 @@ module JSON::LD
152
151
  # The JSON-LD object to copy and perform the expansion upon.
153
152
  # @param [Hash{Symbol => Object}] options
154
153
  # See options in {JSON::LD::API#initialize}
154
+ # @option options [String, #read, Hash, Array, JSON::LD::Context] :expandContext
155
+ # A context that is used to initialize the active context when expanding a document.
155
156
  # @raise [JsonLdError]
156
157
  # @yield jsonld
157
158
  # @yieldparam [Array<Hash>] jsonld
@@ -163,7 +164,10 @@ module JSON::LD
163
164
  def self.expand(input, options = {})
164
165
  result = nil
165
166
  API.new(input, options[:expandContext], options) do |api|
166
- result = api.expand(api.value, nil, api.context)
167
+ result = api.expand(api.value, nil, api.context,
168
+ ordered: options.fetch(:ordered, true),
169
+ framing: options.fetch(:framing, false),
170
+ keep_free_floating_nodes: options.fetch(:keep_free_floating_nodes, false))
167
171
  end
168
172
 
169
173
  # If, after the algorithm outlined above is run, the resulting element is an
@@ -204,9 +208,9 @@ module JSON::LD
204
208
 
205
209
  # 1) Perform the Expansion Algorithm on the JSON-LD input.
206
210
  # This removes any existing context to allow the given context to be cleanly applied.
207
- expanded = options[:expanded] ? input : API.expand(input, options)
211
+ expanded_input = options[:expanded] ? input : API.expand(input, options)
208
212
 
209
- API.new(expanded, context, options) do
213
+ API.new(expanded_input, context, options) do
210
214
  log_debug(".compact") {"expanded input: #{expanded.to_json(JSON_STATE) rescue 'malformed json'}"}
211
215
  result = compact(value, nil)
212
216
 
@@ -270,7 +274,7 @@ module JSON::LD
270
274
 
271
275
  if context && !flattened.empty?
272
276
  # 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.
273
- compacted = log_depth {compact(flattened, nil)}
277
+ compacted = compact(flattened, nil)
274
278
  compacted = [compacted] unless compacted.is_a?(Array)
275
279
  kwgraph = self.context.compact_iri('@graph', quiet: true)
276
280
  flattened = self.context.serialize.merge(kwgraph => compacted)
@@ -294,7 +298,7 @@ module JSON::LD
294
298
  # @param [Hash{Symbol => Object}] options
295
299
  # See options in {JSON::LD::API#initialize}
296
300
  # Other options passed to {JSON::LD::API.expand}
297
- # @option options ['@last', '@always', '@never', '@link'] :embed ('@link')
301
+ # @option options ['@last', '@always', '@never', '@link'] :embed ('@last')
298
302
  # a flag specifying that objects should be directly embedded in the output,
299
303
  # instead of being referred to by their IRI.
300
304
  # @option options [Boolean] :explicit (false)
@@ -347,28 +351,25 @@ module JSON::LD
347
351
  expanded_input = options[:expanded] ? input : API.expand(input, options)
348
352
 
349
353
  # Expand frame to simplify processing
350
- expanded_frame = API.expand(frame, options)
354
+ expanded_frame = API.expand(frame, options.merge(framing: true, keep_free_floating_nodes: true))
351
355
 
352
356
  # Initialize input using frame as context
353
357
  API.new(expanded_input, nil, options) do
354
- #log_debug(".frame") {"context from frame: #{context.inspect}"}
355
358
  log_debug(".frame") {"expanded frame: #{expanded_frame.to_json(JSON_STATE) rescue 'malformed json'}"}
356
359
 
357
360
  # Get framing nodes from expanded input, replacing Blank Node identifiers as necessary
358
361
  old_logger, @options[:logger] = @options[:logger], []
359
- create_node_map(value, framing_state[:graphs], '@merged')
362
+ create_node_map(value, framing_state[:graphs], graph: '@merged')
360
363
  @options[:logger] = old_logger
361
364
  framing_state[:subjects] = framing_state[:graphs]['@merged']
362
- log_debug(".frame") {"subjects: #{framing_state[:subjects].to_json(JSON_STATE) rescue 'malformed json'}"}
363
365
 
364
366
  result = []
365
367
  frame(framing_state, framing_state[:subjects].keys.sort, (expanded_frame.first || {}), options.merge(parent: result))
366
- log_debug(".frame") {"after frame: #{result.to_json(JSON_STATE) rescue 'malformed json'}"}
367
368
 
368
369
  # Initalize context from frame
369
- @context = log_depth {@context.parse(frame['@context'])}
370
+ @context = @context.parse(frame['@context'])
370
371
  # Compact result
371
- compacted = log_depth {compact(result, nil)}
372
+ compacted = compact(result, nil)
372
373
  compacted = [compacted] unless compacted.is_a?(Array)
373
374
 
374
375
  # Add the given context to the output
@@ -449,6 +450,8 @@ module JSON::LD
449
450
  # @param [Array<RDF::Statement>] input
450
451
  # @param [Hash{Symbol => Object}] options
451
452
  # See options in {JSON::LD::API#initialize}
453
+ # @option options [Boolean] :useRdfType (false)
454
+ # If set to `true`, the JSON-LD processor will treat `rdf:type` like a normal property instead of using `@type`.
452
455
  # @yield jsonld
453
456
  # @yieldparam [Hash] jsonld
454
457
  # The JSON-LD document in expanded form
@@ -456,11 +459,11 @@ module JSON::LD
456
459
  # @return [Object, Hash]
457
460
  # If a block is given, the result of evaluating the block is returned, otherwise, the expanded JSON-LD document
458
461
  def self.fromRdf(input, options = {}, &block)
459
- options = {useNativeTypes: false}.merge!(options)
462
+ useRdfType = options.fetch(:useRdfType, false)
460
463
  result = nil
461
464
 
462
465
  API.new(nil, nil, options) do |api|
463
- result = api.from_statements(input)
466
+ result = api.from_statements(input, useRdfType: useRdfType)
464
467
  end
465
468
 
466
469
  block_given? ? yield(result) : result
@@ -1,3 +1,5 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
1
3
  module JSON::LD
2
4
  module Compact
3
5
  include Utils
@@ -9,24 +11,24 @@ module JSON::LD
9
11
  # @param [String] property (nil)
10
12
  # @return [Array, Hash]
11
13
  def compact(element, property = nil)
12
- if property.nil?
13
- log_debug("compact") {"element: #{element.inspect}, ec: #{context.inspect}"}
14
- else
15
- log_debug("compact") {"property: #{property.inspect}"}
16
- end
14
+ #if property.nil?
15
+ # log_debug("compact") {"element: #{element.inspect}, ec: #{context.inspect}"}
16
+ #else
17
+ # log_debug("compact") {"property: #{property.inspect}"}
18
+ #end
17
19
  case element
18
20
  when Array
19
- log_debug("") {"Array #{element.inspect}"}
20
- result = log_depth {element.map {|item| compact(item, property)}.compact}
21
+ #log_debug("") {"Array #{element.inspect}"}
22
+ result = element.map {|item| compact(item, property)}.compact
21
23
 
22
24
  # If element has a single member and the active property has no
23
25
  # @container mapping to @list or @set, the compacted value is that
24
26
  # member; otherwise the compacted value is element
25
27
  if result.length == 1 && context.container(property).nil? && @options[:compactArrays]
26
- log_debug("=> extract single element: #{result.first.inspect}")
28
+ #log_debug("=> extract single element: #{result.first.inspect}")
27
29
  result.first
28
30
  else
29
- log_debug("=> array result: #{result.inspect}")
31
+ #log_debug("=> array result: #{result.inspect}")
30
32
  result
31
33
  end
32
34
  when Hash
@@ -38,7 +40,7 @@ module JSON::LD
38
40
  if element.keys.any? {|k| %w(@id @value).include?(k)}
39
41
  result = context.compact_value(property, element, log_depth: @options[:log_depth])
40
42
  unless result.is_a?(Hash)
41
- log_debug("") {"=> scalar result: #{result.inspect}"}
43
+ #log_debug("") {"=> scalar result: #{result.inspect}"}
42
44
  return result
43
45
  end
44
46
  end
@@ -48,28 +50,28 @@ module JSON::LD
48
50
 
49
51
  element.each_key do |expanded_property|
50
52
  expanded_value = element[expanded_property]
51
- log_debug("") {"#{expanded_property}: #{expanded_value.inspect}"}
53
+ #log_debug("") {"#{expanded_property}: #{expanded_value.inspect}"}
52
54
 
53
55
  if %w(@id @type).include?(expanded_property)
54
56
  compacted_value = [expanded_value].flatten.compact.map do |expanded_type|
55
- log_depth {context.compact_iri(expanded_type, vocab: (expanded_property == '@type'), log_depth: @options[:log_depth])}
57
+ context.compact_iri(expanded_type, vocab: (expanded_property == '@type'), log_depth: @options[:log_depth])
56
58
  end
57
59
  compacted_value = compacted_value.first if compacted_value.length == 1
58
60
 
59
61
  al = context.compact_iri(expanded_property, vocab: true, quiet: true)
60
- log_debug(expanded_property) {"result[#{al}] = #{compacted_value.inspect}"}
62
+ #log_debug(expanded_property) {"result[#{al}] = #{compacted_value.inspect}"}
61
63
  result[al] = compacted_value
62
64
  next
63
65
  end
64
66
 
65
67
  if expanded_property == '@reverse'
66
- compacted_value = log_depth {compact(expanded_value, '@reverse')}
67
- log_debug("@reverse") {"compacted_value: #{compacted_value.inspect}"}
68
+ compacted_value = compact(expanded_value, '@reverse')
69
+ #log_debug("@reverse") {"compacted_value: #{compacted_value.inspect}"}
68
70
  compacted_value.each do |prop, value|
69
71
  if context.reverse?(prop)
70
72
  value = [value] if !value.is_a?(Array) &&
71
73
  (context.container(prop) == '@set' || !@options[:compactArrays])
72
- log_debug("") {"merge #{prop} => #{value.inspect}"}
74
+ #log_debug("") {"merge #{prop} => #{value.inspect}"}
73
75
  merge_compacted_value(result, prop, value)
74
76
  compacted_value.delete(prop)
75
77
  end
@@ -77,33 +79,32 @@ module JSON::LD
77
79
 
78
80
  unless compacted_value.empty?
79
81
  al = context.compact_iri('@reverse', quiet: true)
80
- log_debug("") {"remainder: #{al} => #{compacted_value.inspect}"}
82
+ #log_debug("") {"remainder: #{al} => #{compacted_value.inspect}"}
81
83
  result[al] = compacted_value
82
84
  end
83
85
  next
84
86
  end
85
87
 
86
88
  if expanded_property == '@index' && context.container(property) == '@index'
87
- log_debug("@index") {"drop @index"}
89
+ #log_debug("@index") {"drop @index"}
88
90
  next
89
91
  end
90
92
 
91
93
  # Otherwise, if expanded property is @index, @value, or @language:
92
94
  if %w(@index @value @language).include?(expanded_property)
93
95
  al = context.compact_iri(expanded_property, vocab: true, quiet: true)
94
- log_debug(expanded_property) {"#{al} => #{expanded_value.inspect}"}
96
+ #log_debug(expanded_property) {"#{al} => #{expanded_value.inspect}"}
95
97
  result[al] = expanded_value
96
98
  next
97
99
  end
98
100
 
99
101
  if expanded_value == []
100
- item_active_property = log_depth do
102
+ item_active_property =
101
103
  context.compact_iri(expanded_property,
102
104
  value: expanded_value,
103
105
  vocab: true,
104
106
  reverse: inside_reverse,
105
107
  log_depth: @options[:log_depth])
106
- end
107
108
 
108
109
  iap = result[item_active_property] ||= []
109
110
  result[item_active_property] = [iap] unless iap.is_a?(Array)
@@ -111,17 +112,17 @@ module JSON::LD
111
112
 
112
113
  # At this point, expanded value must be an array due to the Expansion algorithm.
113
114
  expanded_value.each do |expanded_item|
114
- item_active_property = log_depth do
115
+ item_active_property =
115
116
  context.compact_iri(expanded_property,
116
117
  value: expanded_item,
117
118
  vocab: true,
118
119
  reverse: inside_reverse,
119
120
  log_depth: @options[:log_depth])
120
- end
121
+
121
122
  container = context.container(item_active_property)
122
123
  value = list?(expanded_item) ? expanded_item['@list'] : expanded_item
123
- compacted_item = log_depth {compact(value, item_active_property)}
124
- log_debug("") {" => compacted key: #{item_active_property.inspect} for #{compacted_item.inspect}"}
124
+ compacted_item = compact(value, item_active_property)
125
+ #log_debug("") {" => compacted key: #{item_active_property.inspect} for #{compacted_item.inspect}"}
125
126
 
126
127
  if list?(expanded_item)
127
128
  compacted_item = [compacted_item] unless compacted_item.is_a?(Array)
@@ -159,7 +160,7 @@ module JSON::LD
159
160
  result.keys.kw_sort.inject({}) {|map, kk| map[kk] = result[kk]; map}
160
161
  else
161
162
  # For other types, the compacted value is the element value
162
- log_debug("compact") {element.class.to_s}
163
+ #log_debug("compact") {element.class.to_s}
163
164
  element
164
165
  end
165
166
  end
@@ -1,3 +1,5 @@
1
+ # -*- encoding: utf-8 -*-
2
+ # frozen_string_literal: true
1
3
  require 'json'
2
4
  require 'bigdecimal'
3
5
 
@@ -6,6 +8,23 @@ module JSON::LD
6
8
  include Utils
7
9
  include RDF::Util::Logger
8
10
 
11
+ ##
12
+ # Preloaded contexts.
13
+ # To avoid runtime context parsing and downloading, contexts may be pre-loaded by implementations.
14
+ # @return [Hash{Symbol => Context}]
15
+ PRELOADED = {}
16
+
17
+ class << self
18
+ ##
19
+ # Add preloaded context. In the block form, the context is lazy evaulated on first use.
20
+ # @param [String, RDF::URI] url
21
+ # @param [Context] context (nil)
22
+ # @yieldreturn [Context]
23
+ def add_preloaded(url, context = nil, &block)
24
+ PRELOADED[url.to_s.freeze] = context || block
25
+ end
26
+ end
27
+
9
28
  # Term Definitions specify how properties and values have to be interpreted as well as the current vocabulary mapping and the default language
10
29
  class TermDefinition
11
30
  # @return [RDF::URI] IRI map
@@ -17,7 +36,7 @@ module JSON::LD
17
36
  # @return [String] Type mapping
18
37
  attr_accessor :type_mapping
19
38
 
20
- # @return [String] Container mapping
39
+ # @return ['@set', '@list'] Container mapping
21
40
  attr_accessor :container_mapping
22
41
 
23
42
  # Language mapping of term, `false` is used if there is explicitly no language mapping for this term.
@@ -38,9 +57,27 @@ module JSON::LD
38
57
  # Create a new Term Mapping with an ID
39
58
  # @param [String] term
40
59
  # @param [String] id
41
- def initialize(term, id = nil)
42
- @term = term
43
- @id = id.to_s if id
60
+ # @param [String] type_mapping Type mapping
61
+ # @param ['@set', '@list'] container_mapping
62
+ # @param [String] language_mapping
63
+ # Language mapping of term, `false` is used if there is explicitly no language mapping for this term
64
+ # @param [Boolean] reverse_property
65
+ # @param [Boolean] simple
66
+ # This is a simple term definition, not an expanded term definition
67
+ def initialize(term,
68
+ id: nil,
69
+ type_mapping: nil,
70
+ container_mapping: nil,
71
+ language_mapping: nil,
72
+ reverse_property: false,
73
+ simple: false)
74
+ @term = term
75
+ @id = id.to_s if id
76
+ @type_mapping = type_mapping.to_s if type_mapping
77
+ @container_mapping = container_mapping if container_mapping
78
+ @language_mapping = language_mapping if language_mapping
79
+ @reverse_property = reverse_property if reverse_property
80
+ @simple = simple if simple
44
81
  end
45
82
 
46
83
  ##
@@ -81,6 +118,19 @@ module JSON::LD
81
118
  end
82
119
  end
83
120
 
121
+ ##
122
+ # Turn this into a source for a new instantiation
123
+ # @return [String]
124
+ def to_rb
125
+ defn = [%(TermDefinition.new\(#{term.inspect})]
126
+ %w(id type_mapping container_mapping language_mapping reverse_property simple).each do |acc|
127
+ v = instance_variable_get("@#{acc}".to_sym)
128
+ v = v.to_s if v.is_a?(RDF::Term)
129
+ defn << "#{acc}: #{v.inspect}" if v
130
+ end
131
+ defn.join(', ') + ")"
132
+ end
133
+
84
134
  def inspect
85
135
  v = %w([TD)
86
136
  v << "id=#{@id}"
@@ -173,12 +223,13 @@ module JSON::LD
173
223
  (options[:prefixes] || {}).each_pair do |k, v|
174
224
  next if k.nil?
175
225
  @iri_to_term[v.to_s] = k
176
- @term_definitions[k.to_s] = TermDefinition.new(k, v.to_s)
226
+ @term_definitions[k.to_s] = TermDefinition.new(k, id: v.to_s)
177
227
  @term_definitions[k.to_s].simple = true
178
228
  end
179
229
 
180
230
  self.vocab = options[:vocab] if options[:vocab]
181
231
  self.default_language = options[:language] if options[:language]
232
+ @term_definitions = options[:term_definitions] if options[:term_definitions]
182
233
 
183
234
  #log_debug("init") {"iri_to_term: #{iri_to_term.inspect}"}
184
235
 
@@ -224,14 +275,14 @@ module JSON::LD
224
275
  @vocab = case value
225
276
  when /_:/
226
277
  value
227
- when String
228
- v = as_resource(value)
229
- raise JsonLdError::InvalidVocabMapping, "@value must be an absolute IRI: #{value.inspect}" if v.uri? && v.relative? && @options[:validate]
278
+ when String, RDF::URI
279
+ v = as_resource(value.to_s)
280
+ raise JsonLdError::InvalidVocabMapping, "@vocab must be an absolute IRI: #{value.inspect}" if v.uri? && v.relative? && @options[:validate]
230
281
  v
231
282
  when nil
232
283
  nil
233
284
  else
234
- raise JsonLdError::InvalidVocabMapping, "@value must be a string: #{value.inspect}"
285
+ raise JsonLdError::InvalidVocabMapping, "@vocab must be an absolute IRI: #{value.inspect}"
235
286
  end
236
287
  end
237
288
 
@@ -255,41 +306,55 @@ module JSON::LD
255
306
  local_context = [local_context] unless local_context.is_a?(Array)
256
307
 
257
308
  local_context.each do |context|
258
- log_depth do
259
- case context
260
- when nil
261
- # 3.1 If niil, set to a new empty context
262
- result = Context.new(options)
263
- when Context
264
- log_debug("parse") {"context: #{context.inspect}"}
265
- result = context.dup
266
- when IO, StringIO
267
- log_debug("parse") {"io: #{context}"}
268
- # Load context document, if it is an open file
269
- begin
270
- ctx = JSON.load(context)
271
- raise JSON::LD::JsonLdError::InvalidRemoteContext, "Context missing @context key" if @options[:validate] && ctx['@context'].nil?
272
- result = result.dup.parse(ctx["@context"] ? ctx["@context"].dup : {})
273
- result.provided_context = ctx["@context"] if [context] == local_context
274
- result
275
- rescue JSON::ParserError => e
276
- log_debug("parse") {"Failed to parse @context from remote document at #{context}: #{e.message}"}
277
- raise JSON::LD::JsonLdError::InvalidRemoteContext, "Failed to parse remote context at #{context}: #{e.message}" if @options[:validate]
278
- self.dup
279
- end
280
- when String, RDF::URI
281
- log_debug("parse") {"remote: #{context}, base: #{result.context_base || result.base}"}
282
- # Load context document, if it is a string
283
- # 3.2.1) Set context to the result of resolving value against the base IRI which is established as specified in section 5.1 Establishing a Base URI of [RFC3986]. Only the basic algorithm in section 5.2 of [RFC3986] is used; neither Syntax-Based Normalization nor Scheme-Based Normalization are performed. Characters additionally allowed in IRI references are treated in the same way that unreserved characters are treated in URI references, per section 6.5 of [RFC3987].
284
- context = RDF::URI(result.context_base || result.base).join(context)
309
+ case context
310
+ when nil
311
+ # 3.1 If niil, set to a new empty context
312
+ result = Context.new(options)
313
+ when Context
314
+ #log_debug("parse") {"context: #{context.inspect}"}
315
+ result = context.dup
316
+ when IO, StringIO
317
+ #log_debug("parse") {"io: #{context}"}
318
+ # Load context document, if it is an open file
319
+ begin
320
+ ctx = JSON.load(context)
321
+ raise JSON::LD::JsonLdError::InvalidRemoteContext, "Context missing @context key" if @options[:validate] && ctx['@context'].nil?
322
+ result = result.dup.parse(ctx["@context"] ? ctx["@context"].dup : {})
323
+ result.provided_context = ctx["@context"] if [context] == local_context
324
+ result
325
+ rescue JSON::ParserError => e
326
+ #log_debug("parse") {"Failed to parse @context from remote document at #{context}: #{e.message}"}
327
+ raise JSON::LD::JsonLdError::InvalidRemoteContext, "Failed to parse remote context at #{context}: #{e.message}" if @options[:validate]
328
+ self.dup
329
+ end
330
+ when String, RDF::URI
331
+ #log_debug("parse") {"remote: #{context}, base: #{result.context_base || result.base}"}
285
332
 
286
- raise JsonLdError::RecursiveContextInclusion, "#{context}" if remote_contexts.include?(context.to_s)
287
- remote_contexts << context.to_s
333
+ # 3.2.1) Set context to the result of resolving value against the base IRI which is established as specified in section 5.1 Establishing a Base URI of [RFC3986]. Only the basic algorithm in section 5.2 of [RFC3986] is used; neither Syntax-Based Normalization nor Scheme-Based Normalization are performed. Characters additionally allowed in IRI references are treated in the same way that unreserved characters are treated in URI references, per section 6.5 of [RFC3987].
334
+ context = RDF::URI(result.context_base || result.base).join(context)
335
+ context_canon = RDF::URI(context).canonicalize
336
+ context_canon.dup.scheme = 'http'.dup if context_canon.scheme == 'https'
288
337
 
289
- context_no_base = result.dup
290
- context_no_base.base = nil
291
- context_no_base.context_base = context.to_s
338
+ raise JsonLdError::RecursiveContextInclusion, "#{context}" if remote_contexts.include?(context.to_s)
339
+ remote_contexts << context.to_s
292
340
 
341
+ context_no_base = result.dup
342
+ context_no_base.base = nil
343
+ context_no_base.context_base = context.to_s
344
+
345
+ if PRELOADED[context_canon.to_s]
346
+ # If we have a cached context, merge it into the current context (result) and use as the new context
347
+ #log_debug("parse") {"=> cached_context: #{context_canon.to_s.inspect}"}
348
+
349
+ # If this is a Proc, then replace the entry with the result of running the Proc
350
+ if PRELOADED[context_canon.to_s].respond_to?(:call)
351
+ #log_debug("parse") {"=> (call)"}
352
+ PRELOADED[context_canon.to_s] = PRELOADED[context_canon.to_s].call
353
+ end
354
+ context = context_no_base.merge!(PRELOADED[context_canon.to_s])
355
+ else
356
+
357
+ # Load context document, if it is a string
293
358
  begin
294
359
  context_opts = @options.dup
295
360
  context_opts.delete(:headers)
@@ -306,48 +371,46 @@ module JSON::LD
306
371
  end
307
372
  end
308
373
  rescue JsonLdError::LoadingDocumentFailed => e
309
- log_debug("parse") {"Failed to retrieve @context from remote document at #{context_no_base.context_base.inspect}: #{e.message}"}
374
+ #log_debug("parse") {"Failed to retrieve @context from remote document at #{context_no_base.context_base.inspect}: #{e.message}"}
310
375
  raise JsonLdError::LoadingRemoteContextFailed, "#{context_no_base.context_base}", e.backtrace
311
376
  rescue JsonLdError
312
377
  raise
313
378
  rescue Exception => e
314
- log_debug("parse") {"Failed to retrieve @context from remote document at #{context_no_base.context_base.inspect}: #{e.message}"}
379
+ #log_debug("parse") {"Failed to retrieve @context from remote document at #{context_no_base.context_base.inspect}: #{e.message}"}
315
380
  raise JsonLdError::LoadingRemoteContextFailed, "#{context_no_base.context_base}", e.backtrace
316
381
  end
317
382
 
318
383
  # 3.2.6) Set context to the result of recursively calling this algorithm, passing context no base for active context, context for local context, and remote contexts.
319
384
  context = context_no_base.parse(context, remote_contexts.dup)
320
385
  context.provided_context = result.provided_context
321
- context.base ||= result.base
322
- result = context
323
- log_debug("parse") {"=> provided_context: #{context.inspect}"}
324
- when Hash
325
- # If context has a @vocab member: if its value is not a valid absolute IRI or null trigger an INVALID_VOCAB_MAPPING error; otherwise set the active context's vocabulary mapping to its value and remove the @vocab member from context.
326
- context = context.dup # keep from modifying a hash passed as a param
327
- {
328
- '@base' => :base=,
329
- '@language' => :default_language=,
330
- '@vocab' => :vocab=
331
- }.each do |key, setter|
332
- v = context.fetch(key, false)
333
- unless v == false
334
- context.delete(key)
335
- log_debug("parse") {"Set #{key} to #{v.inspect}"}
336
- result.send(setter, v)
337
- end
386
+ end
387
+ context.base ||= result.base
388
+ result = context
389
+ #log_debug("parse") {"=> provided_context: #{context.inspect}"}
390
+ when Hash
391
+ # If context has a @vocab member: if its value is not a valid absolute IRI or null trigger an INVALID_VOCAB_MAPPING error; otherwise set the active context's vocabulary mapping to its value and remove the @vocab member from context.
392
+ context = context.dup # keep from modifying a hash passed as a param
393
+ {
394
+ '@base' => :base=,
395
+ '@language' => :default_language=,
396
+ '@vocab' => :vocab=
397
+ }.each do |key, setter|
398
+ v = context.fetch(key, false)
399
+ unless v == false
400
+ context.delete(key)
401
+ #log_debug("parse") {"Set #{key} to #{v.inspect}"}
402
+ result.send(setter, v)
338
403
  end
404
+ end
339
405
 
340
- defined = {}
341
- # For each key-value pair in context invoke the Create Term Definition subalgorithm, passing result for active context, context for local context, key, and defined
342
- log_depth do
343
- context.each_key do |key|
344
- result.create_term_definition(context, key, defined)
345
- end
346
- end
347
- else
348
- # 3.3) If context is not a JSON object, an invalid local context error has been detected and processing is aborted.
349
- raise JsonLdError::InvalidLocalContext, context.inspect
406
+ defined = {}
407
+ # For each key-value pair in context invoke the Create Term Definition subalgorithm, passing result for active context, context for local context, key, and defined
408
+ context.each_key do |key|
409
+ result.create_term_definition(context, key, defined)
350
410
  end
411
+ else
412
+ # 3.3) If context is not a JSON object, an invalid local context error has been detected and processing is aborted.
413
+ raise JsonLdError::InvalidLocalContext, context.inspect
351
414
  end
352
415
  end
353
416
  result
@@ -395,7 +458,7 @@ module JSON::LD
395
458
  # @see http://json-ld.org/spec/latest/json-ld-api/index.html#create-term-definition
396
459
  def create_term_definition(local_context, term, defined)
397
460
  # Expand a string value, unless it matches a keyword
398
- log_debug("create_term_definition") {"term = #{term.inspect}"}
461
+ #log_debug("create_term_definition") {"term = #{term.inspect}"}
399
462
 
400
463
  # If defined contains the key term, then the associated value must be true, indicating that the term definition has already been created, so return. Otherwise, a cyclical term definition has been detected, which is an error.
401
464
  case defined[term]
@@ -425,12 +488,12 @@ module JSON::LD
425
488
  case value
426
489
  when nil, {'@id' => nil}
427
490
  # If value equals null or value is a JSON object containing the key-value pair (@id-null), then set the term definition in active context to null, set the value associated with defined's key term to true, and return.
428
- log_debug("") {"=> nil"}
491
+ #log_debug("") {"=> nil"}
429
492
  term_definitions[term] = TermDefinition.new(term)
430
493
  defined[term] = true
431
494
  return
432
495
  when Hash
433
- log_debug("") {"Hash[#{term.inspect}] = #{value.inspect}"}
496
+ #log_debug("") {"Hash[#{term.inspect}] = #{value.inspect}"}
434
497
  definition = TermDefinition.new(term)
435
498
  definition.simple = simple_term
436
499
 
@@ -452,7 +515,7 @@ module JSON::LD
452
515
  unless %w(@id @vocab).include?(type) || type.is_a?(RDF::URI) && type.absolute?
453
516
  raise JsonLdError::InvalidTypeMapping, "unknown mapping for '@type': #{type.inspect} on term #{term.inspect}"
454
517
  end
455
- log_debug("") {"type_mapping: #{type.inspect}"}
518
+ #log_debug("") {"type_mapping: #{type.inspect}"}
456
519
  definition.type_mapping = type
457
520
  end
458
521
 
@@ -492,7 +555,7 @@ module JSON::LD
492
555
  elsif term.include?(':')
493
556
  # 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.
494
557
  prefix, suffix = term.split(':')
495
- log_depth {create_term_definition(local_context, prefix, defined)} if local_context.has_key?(prefix)
558
+ create_term_definition(local_context, prefix, defined) if local_context.has_key?(prefix)
496
559
 
497
560
  definition.id = if td = term_definitions[prefix]
498
561
  # 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.
@@ -501,12 +564,12 @@ module JSON::LD
501
564
  # Otherwise, term is an absolute IRI. Set the IRI mapping for definition to term
502
565
  term
503
566
  end
504
- log_debug("") {"=> #{definition.id}"}
567
+ #log_debug("") {"=> #{definition.id}"}
505
568
  else
506
569
  # Otherwise, active context must have a vocabulary mapping, otherwise an invalid value has been detected, which is an error. Set the IRI mapping for definition to the result of concatenating the value associated with the vocabulary mapping and term.
507
570
  raise JsonLdError::InvalidIRIMapping, "relative term definition without vocab: #{term} on term #{term.inspect}" unless vocab
508
571
  definition.id = vocab + term
509
- log_debug("") {"=> #{definition.id}"}
572
+ #log_debug("") {"=> #{definition.id}"}
510
573
  end
511
574
 
512
575
  @iri_to_term[definition.id] = term if simple_term && definition.id
@@ -514,7 +577,7 @@ module JSON::LD
514
577
  if value.has_key?('@container')
515
578
  container = value['@container']
516
579
  raise JsonLdError::InvalidContainerMapping, "unknown mapping for '@container' to #{container.inspect} on term #{term.inspect}" unless %w(@list @set @language @index).include?(container)
517
- log_debug("") {"container_mapping: #{container.inspect}"}
580
+ #log_debug("") {"container_mapping: #{container.inspect}"}
518
581
  definition.container_mapping = container
519
582
  end
520
583
 
@@ -522,7 +585,7 @@ module JSON::LD
522
585
  language = value['@language']
523
586
  raise JsonLdError::InvalidLanguageMapping, "language must be null or a string, was #{language.inspect}} on term #{term.inspect}" unless language.nil? || (language || "").is_a?(String)
524
587
  language = language.downcase if language.is_a?(String)
525
- log_debug("") {"language_mapping: #{language.inspect}"}
588
+ #log_debug("") {"language_mapping: #{language.inspect}"}
526
589
  definition.language_mapping = language || false
527
590
  end
528
591
 
@@ -542,38 +605,36 @@ module JSON::LD
542
605
  # @param [Hash{Symbol => Object}] options ({})
543
606
  # @return [Hash]
544
607
  def serialize(options = {})
545
- log_depth(options) do
546
- # FIXME: not setting provided_context now
547
- use_context = case provided_context
548
- when String, RDF::URI
549
- log_debug "serlialize: reuse context: #{provided_context.inspect}"
550
- provided_context.to_s
551
- when Hash, Array
552
- log_debug "serlialize: reuse context: #{provided_context.inspect}"
553
- provided_context
554
- else
555
- log_debug("serlialize: generate context")
556
- log_debug("") {"=> context: #{inspect}"}
557
- ctx = {}
558
- ctx['@base'] = base.to_s if base && base != doc_base
559
- ctx['@language'] = default_language.to_s if default_language
560
- ctx['@vocab'] = vocab.to_s if vocab
561
-
562
- # Term Definitions
563
- term_definitions.keys.sort.each do |term|
564
- defn = term_definitions[term].to_context_definition(self)
565
- ctx[term] = defn if defn
566
- end
567
-
568
- log_debug("") {"start_doc: context=#{ctx.inspect}"}
569
- ctx
608
+ # FIXME: not setting provided_context now
609
+ use_context = case provided_context
610
+ when String, RDF::URI
611
+ #log_debug "serlialize: reuse context: #{provided_context.inspect}"
612
+ provided_context.to_s
613
+ when Hash, Array
614
+ #log_debug "serlialize: reuse context: #{provided_context.inspect}"
615
+ provided_context
616
+ else
617
+ #log_debug("serlialize: generate context")
618
+ #log_debug("") {"=> context: #{inspect}"}
619
+ ctx = {}
620
+ ctx['@base'] = base.to_s if base && base != doc_base
621
+ ctx['@language'] = default_language.to_s if default_language
622
+ ctx['@vocab'] = vocab.to_s if vocab
623
+
624
+ # Term Definitions
625
+ term_definitions.keys.sort.each do |term|
626
+ defn = term_definitions[term].to_context_definition(self)
627
+ ctx[term] = defn if defn
570
628
  end
571
629
 
572
- # Return hash with @context, or empty
573
- r = {}
574
- r['@context'] = use_context unless use_context.nil? || use_context.empty?
575
- r
630
+ #log_debug("") {"start_doc: context=#{ctx.inspect}"}
631
+ ctx
576
632
  end
633
+
634
+ # Return hash with @context, or empty
635
+ r = {}
636
+ r['@context'] = use_context unless use_context.nil? || use_context.empty?
637
+ r
577
638
  end
578
639
 
579
640
  ##
@@ -620,14 +681,14 @@ module JSON::LD
620
681
  next if vocab && subject.to_s.start_with?(vocab)
621
682
 
622
683
  # otherwise, create a term definition
623
- td = term_definitions[term] = TermDefinition.new(term, subject.to_s)
684
+ td = term_definitions[term] = TermDefinition.new(term, id: subject.to_s)
624
685
  else
625
686
  prop_ranges = ranges.fetch(subject, [])
626
687
  # If any range is empty or member of range includes rdfs:Literal or schema:Text
627
688
  next if vocab && prop_ranges.empty? ||
628
689
  prop_ranges.include?(RDF::SCHEMA.Text) ||
629
690
  prop_ranges.include?(RDF::RDFS.Literal)
630
- td = term_definitions[term] = TermDefinition.new(term, subject.to_s)
691
+ td = term_definitions[term] = TermDefinition.new(term, id: subject.to_s)
631
692
 
632
693
  # Set context typing based on first element in range
633
694
  case r = prop_ranges.first
@@ -658,9 +719,9 @@ module JSON::LD
658
719
  #
659
720
  # @return [TermDefinition]
660
721
  def set_mapping(term, value)
661
- log_debug("") {"map #{term.inspect} to #{value.inspect}"}
722
+ #log_debug("") {"map #{term.inspect} to #{value.inspect}"}
662
723
  term = term.to_s
663
- term_definitions[term] = TermDefinition.new(term, value)
724
+ term_definitions[term] = TermDefinition.new(term, id: value)
664
725
  term_definitions[term].simple = true
665
726
 
666
727
  term_sym = term.empty? ? "" : term.to_sym
@@ -724,7 +785,7 @@ module JSON::LD
724
785
  td = term_definitions.values.detect {|t| t.id == term.to_s}
725
786
 
726
787
  # Otherwise create a temporary term definition
727
- term = td || TermDefinition.new(term.to_s, expand_iri(term, vocab:true))
788
+ term = td || TermDefinition.new(term.to_s, id: expand_iri(term, vocab:true))
728
789
  end
729
790
 
730
791
  # Now, return a term, which reverses this term
@@ -737,244 +798,240 @@ module JSON::LD
737
798
  # @param [String] value
738
799
  # A keyword, term, prefix:suffix or possibly relative IRI
739
800
  # @param [Hash{Symbol => Object}] options
740
- # @option options [Boolean] documentRelative (false)
741
- # @option options [Boolean] vocab (false)
742
- # @option options [Hash] local_context
801
+ # @param [Boolean] documentRelative (false)
802
+ # @param [Boolean] vocab (false)
803
+ # @param [RDF::URI] base
804
+ # @param [Hash] local_context
743
805
  # Used during Context Processing.
744
- # @option options [Hash] defined
806
+ # @param [Hash] defined
745
807
  # Used during Context Processing.
808
+ # @param [Boolean] quiet (false)
809
+ # @param [Hash] options ({})
746
810
  # @return [RDF::URI, String]
747
811
  # IRI or String, if it's a keyword
748
812
  # @raise [JSON::LD::JsonLdError::InvalidIRIMapping] if the value cannot be expanded
749
813
  # @see http://json-ld.org/spec/latest/json-ld-api/#iri-expansion
750
- def expand_iri(value, options = {})
814
+ def expand_iri(value, documentRelative: false, vocab: false, local_context: nil, defined: {}, quiet: false, **options)
751
815
  return value unless value.is_a?(String)
752
816
 
753
817
  return value if KEYWORDS.include?(value)
754
- log_depth(options) do
755
- log_debug("expand_iri") {"value: #{value.inspect}"} unless options[:quiet]
756
- local_context = options[:local_context]
757
- defined = options.fetch(:defined, {})
758
-
759
- # 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.
760
- if local_context && local_context.has_key?(value) && !defined[value]
761
- log_depth {create_term_definition(local_context, value, defined)}
762
- end
818
+ #log_debug("expand_iri") {"value: #{value.inspect}"} unless quiet
763
819
 
764
- # If vocab is true and the active context has a term definition for value, return the associated IRI mapping.
765
- if options[:vocab] && (v_td = term_definitions[value])
766
- log_debug("") {"match with #{v_td.id}"} unless options[:quiet]
767
- return v_td.id
768
- end
820
+ # 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.
821
+ if local_context && local_context.has_key?(value) && !defined[value]
822
+ create_term_definition(local_context, value, defined)
823
+ end
769
824
 
770
- # If value contains a colon (:), it is either an absolute IRI or a compact IRI:
771
- if value.include?(':')
772
- prefix, suffix = value.split(':', 2)
773
- log_debug("") {"prefix: #{prefix.inspect}, suffix: #{suffix.inspect}, vocab: #{vocab.inspect}"} unless options[:quiet]
825
+ # If vocab is true and the active context has a term definition for value, return the associated IRI mapping.
826
+ if vocab && (v_td = term_definitions[value])
827
+ #log_debug("") {"match with #{v_td.id}"} unless quiet
828
+ return v_td.id
829
+ end
774
830
 
775
- # If prefix is underscore (_) or suffix begins with double-forward-slash (//), return value as it is already an absolute IRI or a blank node identifier.
776
- return RDF::Node.new(namer.get_sym(suffix)) if prefix == '_'
777
- return RDF::URI(value) if suffix[0,2] == '//'
831
+ # If value contains a colon (:), it is either an absolute IRI or a compact IRI:
832
+ if value.include?(':')
833
+ prefix, suffix = value.split(':', 2)
834
+ #log_debug("") {"prefix: #{prefix.inspect}, suffix: #{suffix.inspect}, vocab: #{self.vocab.inspect}"} unless quiet
778
835
 
779
- # 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.
780
- if local_context && local_context.has_key?(prefix) && !defined[prefix]
781
- create_term_definition(local_context, prefix, defined)
782
- end
836
+ # If prefix is underscore (_) or suffix begins with double-forward-slash (//), return value as it is already an absolute IRI or a blank node identifier.
837
+ return RDF::Node.new(namer.get_sym(suffix)) if prefix == '_'
838
+ return RDF::URI(value) if suffix[0,2] == '//'
783
839
 
784
- # If active context contains a term definition for prefix, return the result of concatenating the IRI mapping associated with prefix and suffix.
785
- result = if (td = term_definitions[prefix])
786
- result = td.id + suffix
787
- else
788
- # (Otherwise) Return value as it is already an absolute IRI.
789
- RDF::URI(value)
790
- end
791
-
792
- log_debug("") {"=> #{result.inspect}"} unless options[:quiet]
793
- return result
840
+ # 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.
841
+ if local_context && local_context.has_key?(prefix) && !defined[prefix]
842
+ create_term_definition(local_context, prefix, defined)
794
843
  end
795
- log_debug("") {"=> #{result.inspect}"} unless options[:quiet]
796
-
797
- result = if options[:vocab] && vocab
798
- # If vocab is true, and active context has a vocabulary mapping, return the result of concatenating the vocabulary mapping with value.
799
- vocab + value
800
- elsif options[:documentRelative] && base = options.fetch(:base, self.base)
801
- # Otherwise, if document relative is true, set value to the result of resolving value against the base IRI. Only the basic algorithm in section 5.2 of [RFC3986] is used; neither Syntax-Based Normalization nor Scheme-Based Normalization are performed. Characters additionally allowed in IRI references are treated in the same way that unreserved characters are treated in URI references, per section 6.5 of [RFC3987].
802
- value = RDF::URI(value)
803
- value.absolute? ? value : RDF::URI(base).join(value)
804
- elsif local_context && RDF::URI(value).relative?
805
- # If local context is not null and value is not an absolute IRI, an invalid IRI mapping error has been detected and processing is aborted.
806
- raise JSON::LD::JsonLdError::InvalidIRIMapping, "not an absolute IRI: #{value}"
844
+
845
+ # If active context contains a term definition for prefix, return the result of concatenating the IRI mapping associated with prefix and suffix.
846
+ result = if (td = term_definitions[prefix])
847
+ result = td.id + suffix
807
848
  else
849
+ # (Otherwise) Return value as it is already an absolute IRI.
808
850
  RDF::URI(value)
809
851
  end
810
- log_debug("") {"=> #{result}"} unless options[:quiet]
811
- result
852
+
853
+ #log_debug("") {"=> #{result.inspect}"} unless quiet
854
+ return result
855
+ end
856
+ #log_debug("") {"=> #{result.inspect}"} unless quiet
857
+
858
+ result = if vocab && self.vocab
859
+ # If vocab is true, and active context has a vocabulary mapping, return the result of concatenating the vocabulary mapping with value.
860
+ self.vocab + value
861
+ elsif documentRelative && (base ||= self.base)
862
+ # Otherwise, if document relative is true, set value to the result of resolving value against the base IRI. Only the basic algorithm in section 5.2 of [RFC3986] is used; neither Syntax-Based Normalization nor Scheme-Based Normalization are performed. Characters additionally allowed in IRI references are treated in the same way that unreserved characters are treated in URI references, per section 6.5 of [RFC3987].
863
+ value = RDF::URI(value)
864
+ value.absolute? ? value : RDF::URI(base).join(value)
865
+ elsif local_context && RDF::URI(value).relative?
866
+ # If local context is not null and value is not an absolute IRI, an invalid IRI mapping error has been detected and processing is aborted.
867
+ raise JSON::LD::JsonLdError::InvalidIRIMapping, "not an absolute IRI: #{value}"
868
+ else
869
+ RDF::URI(value)
812
870
  end
871
+ #log_debug("") {"=> #{result}"} unless quiet
872
+ result
813
873
  end
814
874
 
815
875
  ##
816
876
  # Compacts an absolute IRI to the shortest matching term or compact IRI
817
877
  #
818
878
  # @param [RDF::URI] iri
819
- # @param [Hash{Symbol => Object}] options ({})
820
- # @option options [Object] :value
879
+ # @param [Object] value
821
880
  # Value, used to select among various maps for the same IRI
822
- # @option options [Boolean] :vocab
881
+ # @param [Boolean] :vocab
823
882
  # specifies whether the passed iri should be compacted using the active context's vocabulary mapping
824
- # @option options [Boolean] :reverse
883
+ # @param [Boolean] reverse
825
884
  # specifies whether a reverse property is being compacted
885
+ # @param [Boolean] quiet (false)
886
+ # @param [Hash{Symbol => Object}] options ({})
826
887
  #
827
888
  # @return [String] compacted form of IRI
828
889
  # @see http://json-ld.org/spec/latest/json-ld-api/#iri-compaction
829
- def compact_iri(iri, options = {})
890
+ def compact_iri(iri, value: nil, vocab: nil, reverse: false, quiet: false, **options)
830
891
  return if iri.nil?
831
892
  iri = iri.to_s
832
- log_debug("compact_iri(#{iri.inspect}", options) {options.inspect} unless options[:quiet]
833
- log_depth(options) do
834
-
835
- value = options.fetch(:value, nil)
836
-
837
- if options[:vocab] && inverse_context.has_key?(iri)
838
- log_debug("") {"vocab and key in inverse context"} unless options[:quiet]
839
- default_language = self.default_language || @none
840
- containers = []
841
- tl, tl_value = "@language", "@null"
842
- containers << '@index' if index?(value)
843
- if options[:reverse]
844
- tl, tl_value = "@type", "@reverse"
845
- containers << '@set'
846
- elsif list?(value)
847
- log_debug("") {"list(#{value.inspect})"} unless options[:quiet]
848
- # if value is a list object, then set type/language and type/language value to the most specific values that work for all items in the list as follows:
849
- containers << "@list" unless index?(value)
850
- list = value['@list']
851
- common_type = nil
852
- common_language = default_language if list.empty?
853
- list.each do |item|
854
- item_language, item_type = "@none", "@none"
855
- if value?(item)
856
- if item.has_key?('@language')
857
- item_language = item['@language']
858
- elsif item.has_key?('@type')
859
- item_type = item['@type']
860
- else
861
- item_language = "@null"
862
- end
893
+ #log_debug("compact_iri(#{iri.inspect}", options) {[value, vocab, reverse].inspect} unless quiet
894
+
895
+ if vocab && inverse_context.has_key?(iri)
896
+ #log_debug("") {"vocab and key in inverse context"} unless quiet
897
+ default_language = self.default_language || @none
898
+ containers = []
899
+ tl, tl_value = "@language", "@null"
900
+ containers << '@index' if index?(value)
901
+ if reverse
902
+ tl, tl_value = "@type", "@reverse"
903
+ containers << '@set'
904
+ elsif list?(value)
905
+ #log_debug("") {"list(#{value.inspect})"} unless quiet
906
+ # if value is a list object, then set type/language and type/language value to the most specific values that work for all items in the list as follows:
907
+ containers << "@list" unless index?(value)
908
+ list = value['@list']
909
+ common_type = nil
910
+ common_language = default_language if list.empty?
911
+ list.each do |item|
912
+ item_language, item_type = "@none", "@none"
913
+ if value?(item)
914
+ if item.has_key?('@language')
915
+ item_language = item['@language']
916
+ elsif item.has_key?('@type')
917
+ item_type = item['@type']
863
918
  else
864
- item_type = '@id'
919
+ item_language = "@null"
865
920
  end
866
- common_language ||= item_language
867
- if item_language != common_language && value?(item)
868
- log_debug("") {"-- #{item_language} conflicts with #{common_language}, use @none"} unless options[:quiet]
869
- common_language = '@none'
870
- end
871
- common_type ||= item_type
872
- if item_type != common_type
873
- common_type = '@none'
874
- log_debug("") {"#{item_type} conflicts with #{common_type}, use @none"} unless options[:quiet]
875
- end
876
- end
877
-
878
- common_language ||= '@none'
879
- common_type ||= '@none'
880
- log_debug("") {"common type: #{common_type}, common language: #{common_language}"} unless options[:quiet]
881
- if common_type != '@none'
882
- tl, tl_value = '@type', common_type
883
921
  else
884
- tl_value = common_language
922
+ item_type = '@id'
885
923
  end
886
- log_debug("") {"list: containers: #{containers.inspect}, type/language: #{tl.inspect}, type/language value: #{tl_value.inspect}"} unless options[:quiet]
887
- else
888
- if value?(value)
889
- if value.has_key?('@language') && !index?(value)
890
- tl_value = value['@language']
891
- containers << '@language'
892
- elsif value.has_key?('@type')
893
- tl_value = value['@type']
894
- tl = '@type'
895
- end
896
- else
897
- tl, tl_value = '@type', '@id'
924
+ common_language ||= item_language
925
+ if item_language != common_language && value?(item)
926
+ #log_debug("") {"-- #{item_language} conflicts with #{common_language}, use @none"} unless quiet
927
+ common_language = '@none'
928
+ end
929
+ common_type ||= item_type
930
+ if item_type != common_type
931
+ common_type = '@none'
932
+ #log_debug("") {"#{item_type} conflicts with #{common_type}, use @none"} unless quiet
898
933
  end
899
- containers << '@set'
900
- log_debug("") {"value: containers: #{containers.inspect}, type/language: #{tl.inspect}, type/language value: #{tl_value.inspect}"} unless options[:quiet]
901
934
  end
902
935
 
903
- containers << '@none'
904
- tl_value ||= '@null'
905
- preferred_values = []
906
- preferred_values << '@reverse' if tl_value == '@reverse'
907
- if %w(@id @reverse).include?(tl_value) && value.is_a?(Hash) && value.has_key?('@id')
908
- t_iri = compact_iri(value['@id'], vocab: true, document_relative: true)
909
- if (r_td = term_definitions[t_iri]) && r_td.id == value['@id']
910
- preferred_values.concat(%w(@vocab @id @none))
911
- else
912
- preferred_values.concat(%w(@id @vocab @none))
913
- end
936
+ common_language ||= '@none'
937
+ common_type ||= '@none'
938
+ #log_debug("") {"common type: #{common_type}, common language: #{common_language}"} unless quiet
939
+ if common_type != '@none'
940
+ tl, tl_value = '@type', common_type
914
941
  else
915
- preferred_values.concat([tl_value, '@none'])
942
+ tl_value = common_language
916
943
  end
917
- log_debug("") {"preferred_values: #{preferred_values.inspect}"} unless options[:quiet]
918
- if p_term = select_term(iri, containers, tl, preferred_values)
919
- log_debug("") {"=> term: #{p_term.inspect}"} unless options[:quiet]
920
- return p_term
944
+ #log_debug("") {"list: containers: #{containers.inspect}, type/language: #{tl.inspect}, type/language value: #{tl_value.inspect}"} unless quiet
945
+ else
946
+ if value?(value)
947
+ if value.has_key?('@language') && !index?(value)
948
+ tl_value = value['@language']
949
+ containers << '@language'
950
+ elsif value.has_key?('@type')
951
+ tl_value = value['@type']
952
+ tl = '@type'
953
+ end
954
+ else
955
+ tl, tl_value = '@type', '@id'
921
956
  end
957
+ containers << '@set'
958
+ #log_debug("") {"value: containers: #{containers.inspect}, type/language: #{tl.inspect}, type/language value: #{tl_value.inspect}"} unless quiet
922
959
  end
923
960
 
924
- # 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:
925
- if options[:vocab] && vocab && iri.start_with?(vocab) && iri.length > vocab.length
926
- suffix = iri[vocab.length..-1]
927
- log_debug("") {"=> vocab suffix: #{suffix.inspect}"} unless options[:quiet]
928
- return suffix unless term_definitions.has_key?(suffix)
961
+ containers << '@none'
962
+ tl_value ||= '@null'
963
+ preferred_values = []
964
+ preferred_values << '@reverse' if tl_value == '@reverse'
965
+ if %w(@id @reverse).include?(tl_value) && value.is_a?(Hash) && value.has_key?('@id')
966
+ t_iri = compact_iri(value['@id'], vocab: true, document_relative: true)
967
+ if (r_td = term_definitions[t_iri]) && r_td.id == value['@id']
968
+ preferred_values.concat(%w(@vocab @id @none))
969
+ else
970
+ preferred_values.concat(%w(@id @vocab @none))
971
+ end
972
+ else
973
+ preferred_values.concat([tl_value, '@none'])
974
+ end
975
+ #log_debug("") {"preferred_values: #{preferred_values.inspect}"} unless quiet
976
+ if p_term = select_term(iri, containers, tl, preferred_values)
977
+ #log_debug("") {"=> term: #{p_term.inspect}"} unless quiet
978
+ return p_term
929
979
  end
980
+ end
930
981
 
931
- # 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.
932
- candidates = []
982
+ # 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:
983
+ if vocab && self.vocab && iri.start_with?(self.vocab) && iri.length > self.vocab.length
984
+ suffix = iri[self.vocab.length..-1]
985
+ #log_debug("") {"=> vocab suffix: #{suffix.inspect}"} unless quiet
986
+ return suffix unless term_definitions.has_key?(suffix)
987
+ end
933
988
 
934
- term_definitions.each do |term, td|
935
- next if term.include?(":")
936
- next if td.nil? || td.id.nil? || td.id == iri || !iri.start_with?(td.id)
989
+ # 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.
990
+ candidates = []
937
991
 
938
- # Also skip term if it was not a simple term and the :simple_compact_iris flag is true
939
- next if @options[:simple_compact_iris] && !td.simple?
992
+ term_definitions.each do |term, td|
993
+ next if term.include?(":")
994
+ next if td.nil? || td.id.nil? || td.id == iri || !iri.start_with?(td.id)
940
995
 
941
- suffix = iri[td.id.length..-1]
942
- ciri = "#{term}:#{suffix}"
943
- candidates << ciri unless value && term_definitions.has_key?(ciri)
944
- end
996
+ # Also skip term if it was not a simple term and the :simple_compact_iris flag is true
997
+ next if @options[:simple_compact_iris] && !td.simple?
945
998
 
946
- if !candidates.empty?
947
- log_debug("") {"=> compact iri: #{candidates.term_sort.first.inspect}"} unless options[:quiet]
948
- return candidates.term_sort.first
949
- end
999
+ suffix = iri[td.id.length..-1]
1000
+ ciri = "#{term}:#{suffix}"
1001
+ candidates << ciri unless value && term_definitions.has_key?(ciri)
1002
+ end
950
1003
 
951
- # If we still don't have any terms and we're using standard_prefixes,
952
- # try those, and add to mapping
953
- if @options[:standard_prefixes]
954
- candidates = RDF::Vocabulary.
955
- select {|v| iri.start_with?(v.to_uri.to_s) && iri != v.to_uri.to_s}.
956
- map do |v|
957
- prefix = v.__name__.to_s.split('::').last.downcase
958
- set_mapping(prefix, v.to_uri.to_s)
959
- iri.sub(v.to_uri.to_s, "#{prefix}:").sub(/:$/, '')
960
- end
1004
+ if !candidates.empty?
1005
+ #log_debug("") {"=> compact iri: #{candidates.term_sort.first.inspect}"} unless quiet
1006
+ return candidates.term_sort.first
1007
+ end
961
1008
 
962
- if !candidates.empty?
963
- log_debug("") {"=> standard prefies: #{candidates.term_sort.first.inspect}"} unless options[:quiet]
964
- return candidates.term_sort.first
1009
+ # If we still don't have any terms and we're using standard_prefixes,
1010
+ # try those, and add to mapping
1011
+ if @options[:standard_prefixes]
1012
+ candidates = RDF::Vocabulary.
1013
+ select {|v| iri.start_with?(v.to_uri.to_s) && iri != v.to_uri.to_s}.
1014
+ map do |v|
1015
+ prefix = v.__name__.to_s.split('::').last.downcase
1016
+ set_mapping(prefix, v.to_uri.to_s)
1017
+ iri.sub(v.to_uri.to_s, "#{prefix}:").sub(/:$/, '')
965
1018
  end
966
- end
967
1019
 
968
- if !options[:vocab]
969
- # transform iri to a relative IRI using the document's base IRI
970
- iri = remove_base(iri)
971
- log_debug("") {"=> relative iri: #{iri.inspect}"} unless options[:quiet]
972
- return iri
973
- else
974
- log_debug("") {"=> absolute iri: #{iri.inspect}"} unless options[:quiet]
975
- return iri
1020
+ if !candidates.empty?
1021
+ #log_debug("") {"=> standard prefies: #{candidates.term_sort.first.inspect}"} unless quiet
1022
+ return candidates.term_sort.first
976
1023
  end
977
1024
  end
1025
+
1026
+ if !vocab
1027
+ # transform iri to a relative IRI using the document's base IRI
1028
+ iri = remove_base(iri)
1029
+ #log_debug("") {"=> relative iri: #{iri.inspect}"} unless quiet
1030
+ return iri
1031
+ else
1032
+ #log_debug("") {"=> absolute iri: #{iri.inspect}"} unless quiet
1033
+ return iri
1034
+ end
978
1035
  end
979
1036
 
980
1037
  ##
@@ -986,75 +1043,72 @@ module JSON::LD
986
1043
  # Associated property used to find coercion rules
987
1044
  # @param [Hash, String] value
988
1045
  # Value (literal or IRI) to be expanded
1046
+ # @param [Boolean] useNativeTypes (false) use native representations
989
1047
  # @param [Hash{Symbol => Object}] options
990
- # @option options [Boolean] :useNativeTypes (false) use native representations
991
1048
  #
992
1049
  # @return [Hash] Object representation of value
993
1050
  # @raise [RDF::ReaderError] if the iri cannot be expanded
994
1051
  # @see http://json-ld.org/spec/latest/json-ld-api/#value-expansion
995
- def expand_value(property, value, options = {})
996
- options = {useNativeTypes: false}.merge!(options)
997
- log_depth(options) do
998
- log_debug("expand_value") {"property: #{property.inspect}, value: #{value.inspect}"}
999
-
1000
- # If the active property has a type mapping in active context that is @id, return a new JSON object containing a single key-value pair where the key is @id and the value is the result of using the IRI Expansion algorithm, passing active context, value, and true for document relative.
1001
- if (td = term_definitions.fetch(property, TermDefinition.new(property))) && td.type_mapping == '@id'
1002
- log_debug("") {"as relative IRI: #{value.inspect}"}
1003
- return {'@id' => expand_iri(value, documentRelative: true).to_s}
1004
- end
1052
+ def expand_value(property, value, useNativeTypes: false, **options)
1053
+ #log_debug("expand_value") {"property: #{property.inspect}, value: #{value.inspect}"}
1005
1054
 
1006
- # If active property has a type mapping in active context that is @vocab, return a new JSON object containing a single key-value pair where the key is @id and the value is the result of using the IRI Expansion algorithm, passing active context, value, true for vocab, and true for document relative.
1007
- if td.type_mapping == '@vocab'
1008
- log_debug("") {"as vocab IRI: #{value.inspect}"}
1009
- return {'@id' => expand_iri(value, vocab: true, documentRelative: true).to_s}
1010
- end
1055
+ # If the active property has a type mapping in active context that is @id, return a new JSON object containing a single key-value pair where the key is @id and the value is the result of using the IRI Expansion algorithm, passing active context, value, and true for document relative.
1056
+ if (td = term_definitions.fetch(property, TermDefinition.new(property))) && td.type_mapping == '@id'
1057
+ #log_debug("") {"as relative IRI: #{value.inspect}"}
1058
+ return {'@id' => expand_iri(value, documentRelative: true).to_s}
1059
+ end
1011
1060
 
1012
- value = RDF::Literal(value) if
1013
- value.is_a?(Date) ||
1014
- value.is_a?(DateTime) ||
1015
- value.is_a?(Time)
1016
-
1017
- result = case value
1018
- when RDF::URI, RDF::Node
1019
- log_debug("URI | BNode") { value.to_s }
1020
- {'@id' => value.to_s}
1021
- when RDF::Literal
1022
- log_debug("Literal") {"datatype: #{value.datatype.inspect}"}
1023
- res = {}
1024
- if options[:useNativeTypes] && [RDF::XSD.boolean, RDF::XSD.integer, RDF::XSD.double].include?(value.datatype)
1025
- res['@value'] = value.object
1026
- res['@type'] = uri(coerce(property)) if coerce(property)
1027
- else
1028
- value.canonicalize! if value.datatype == RDF::XSD.double
1029
- res['@value'] = value.to_s
1030
- if coerce(property)
1031
- res['@type'] = uri(coerce(property)).to_s
1032
- elsif value.has_datatype?
1033
- res['@type'] = uri(value.datatype).to_s
1034
- elsif value.has_language? || language(property)
1035
- res['@language'] = (value.language || language(property)).to_s
1036
- end
1037
- end
1038
- res
1061
+ # If active property has a type mapping in active context that is @vocab, return a new JSON object containing a single key-value pair where the key is @id and the value is the result of using the IRI Expansion algorithm, passing active context, value, true for vocab, and true for document relative.
1062
+ if td.type_mapping == '@vocab'
1063
+ #log_debug("") {"as vocab IRI: #{value.inspect}"}
1064
+ return {'@id' => expand_iri(value, vocab: true, documentRelative: true).to_s}
1065
+ end
1066
+
1067
+ value = RDF::Literal(value) if
1068
+ value.is_a?(Date) ||
1069
+ value.is_a?(DateTime) ||
1070
+ value.is_a?(Time)
1071
+
1072
+ result = case value
1073
+ when RDF::URI, RDF::Node
1074
+ #log_debug("URI | BNode") { value.to_s }
1075
+ {'@id' => value.to_s}
1076
+ when RDF::Literal
1077
+ #log_debug("Literal") {"datatype: #{value.datatype.inspect}"}
1078
+ res = {}
1079
+ if useNativeTypes && [RDF::XSD.boolean, RDF::XSD.integer, RDF::XSD.double].include?(value.datatype)
1080
+ res['@value'] = value.object
1081
+ res['@type'] = uri(coerce(property)) if coerce(property)
1039
1082
  else
1040
- # Otherwise, initialize result to a JSON object with an @value member whose value is set to value.
1041
- res = {'@value' => value}
1042
-
1043
- if td.type_mapping
1044
- res['@type'] = td.type_mapping.to_s
1045
- elsif value.is_a?(String)
1046
- if td.language_mapping
1047
- res['@language'] = td.language_mapping
1048
- elsif default_language && td.language_mapping.nil?
1049
- res['@language'] = default_language
1050
- end
1083
+ value.canonicalize! if value.datatype == RDF::XSD.double
1084
+ res['@value'] = value.to_s
1085
+ if coerce(property)
1086
+ res['@type'] = uri(coerce(property)).to_s
1087
+ elsif value.has_datatype?
1088
+ res['@type'] = uri(value.datatype).to_s
1089
+ elsif value.has_language? || language(property)
1090
+ res['@language'] = (value.language || language(property)).to_s
1051
1091
  end
1052
- res
1053
1092
  end
1054
-
1055
- log_debug("") {"=> #{result.inspect}"}
1056
- result
1093
+ res
1094
+ else
1095
+ # Otherwise, initialize result to a JSON object with an @value member whose value is set to value.
1096
+ res = {'@value' => value}
1097
+
1098
+ if td.type_mapping
1099
+ res['@type'] = td.type_mapping.to_s
1100
+ elsif value.is_a?(String)
1101
+ if td.language_mapping
1102
+ res['@language'] = td.language_mapping
1103
+ elsif default_language && td.language_mapping.nil?
1104
+ res['@language'] = default_language
1105
+ end
1106
+ end
1107
+ res
1057
1108
  end
1109
+
1110
+ #log_debug("") {"=> #{result.inspect}"}
1111
+ result
1058
1112
  end
1059
1113
 
1060
1114
  ##
@@ -1071,71 +1125,90 @@ module JSON::LD
1071
1125
  # @see http://json-ld.org/spec/latest/json-ld-api/#value-compaction
1072
1126
  # FIXME: revisit the specification version of this.
1073
1127
  def compact_value(property, value, options = {})
1128
+ #log_debug("compact_value") {"property: #{property.inspect}, value: #{value.inspect}"}
1074
1129
 
1075
- log_depth(options) do
1076
- log_debug("compact_value") {"property: #{property.inspect}, value: #{value.inspect}"}
1130
+ num_members = value.keys.length
1077
1131
 
1078
- num_members = value.keys.length
1132
+ num_members -= 1 if index?(value) && container(property) == '@index'
1133
+ if num_members > 2
1134
+ #log_debug("") {"can't compact value with # members > 2"}
1135
+ return value
1136
+ end
1079
1137
 
1080
- num_members -= 1 if index?(value) && container(property) == '@index'
1081
- if num_members > 2
1082
- log_debug("") {"can't compact value with # members > 2"}
1083
- return value
1138
+ result = case
1139
+ when coerce(property) == '@id' && value.has_key?('@id') && num_members == 1
1140
+ # Compact an @id coercion
1141
+ #log_debug("") {" (@id & coerce)"}
1142
+ compact_iri(value['@id'])
1143
+ when coerce(property) == '@vocab' && value.has_key?('@id') && num_members == 1
1144
+ # Compact an @id coercion
1145
+ #log_debug("") {" (@id & coerce & vocab)"}
1146
+ compact_iri(value['@id'], vocab: true)
1147
+ when value.has_key?('@id')
1148
+ #log_debug("") {" (@id)"}
1149
+ # return value as is
1150
+ value
1151
+ when value['@type'] && expand_iri(value['@type'], vocab: true) == coerce(property)
1152
+ # Compact common datatype
1153
+ #log_debug("") {" (@type & coerce) == #{coerce(property)}"}
1154
+ value['@value']
1155
+ when value['@language'] && (value['@language'] == language(property))
1156
+ # Compact language
1157
+ #log_debug("") {" (@language) == #{language(property).inspect}"}
1158
+ value['@value']
1159
+ when num_members == 1 && !value['@value'].is_a?(String)
1160
+ #log_debug("") {" (native)"}
1161
+ value['@value']
1162
+ when num_members == 1 && default_language.nil? || language(property) == false
1163
+ #log_debug("") {" (!@language)"}
1164
+ value['@value']
1165
+ else
1166
+ # Otherwise, use original value
1167
+ #log_debug("") {" (no change)"}
1168
+ value
1169
+ end
1170
+
1171
+ # If the result is an object, tranform keys using any term keyword aliases
1172
+ if result.is_a?(Hash) && result.keys.any? {|k| self.alias(k) != k}
1173
+ #log_debug("") {" (map to key aliases)"}
1174
+ new_element = {}
1175
+ result.each do |k, v|
1176
+ new_element[self.alias(k)] = v
1084
1177
  end
1178
+ result = new_element
1179
+ end
1085
1180
 
1086
- result = case
1087
- when coerce(property) == '@id' && value.has_key?('@id') && num_members == 1
1088
- # Compact an @id coercion
1089
- log_debug("") {" (@id & coerce)"}
1090
- compact_iri(value['@id'])
1091
- when coerce(property) == '@vocab' && value.has_key?('@id') && num_members == 1
1092
- # Compact an @id coercion
1093
- log_debug("") {" (@id & coerce & vocab)"}
1094
- compact_iri(value['@id'], vocab: true)
1095
- when value.has_key?('@id')
1096
- log_debug("") {" (@id)"}
1097
- # return value as is
1098
- value
1099
- when value['@type'] && expand_iri(value['@type'], vocab: true) == coerce(property)
1100
- # Compact common datatype
1101
- log_debug("") {" (@type & coerce) == #{coerce(property)}"}
1102
- value['@value']
1103
- when value['@language'] && (value['@language'] == language(property))
1104
- # Compact language
1105
- log_debug("") {" (@language) == #{language(property).inspect}"}
1106
- value['@value']
1107
- when num_members == 1 && !value['@value'].is_a?(String)
1108
- log_debug("") {" (native)"}
1109
- value['@value']
1110
- when num_members == 1 && default_language.nil? || language(property) == false
1111
- log_debug("") {" (!@language)"}
1112
- value['@value']
1113
- else
1114
- # Otherwise, use original value
1115
- log_debug("") {" (no change)"}
1116
- value
1117
- end
1118
-
1119
- # If the result is an object, tranform keys using any term keyword aliases
1120
- if result.is_a?(Hash) && result.keys.any? {|k| self.alias(k) != k}
1121
- log_debug("") {" (map to key aliases)"}
1122
- new_element = {}
1123
- result.each do |k, v|
1124
- new_element[self.alias(k)] = v
1125
- end
1126
- result = new_element
1127
- end
1181
+ #log_debug("") {"=> #{result.inspect}"}
1182
+ result
1183
+ end
1128
1184
 
1129
- log_debug("") {"=> #{result.inspect}"}
1130
- result
1185
+ ##
1186
+ # Turn this into a source for a new instantiation
1187
+ # @return [String]
1188
+ def to_rb
1189
+ defn = []
1190
+
1191
+ defn << "base: #{self.base.to_s.inspect}" if self.base
1192
+ defn << "language: #{self.default_language.inspect}" if self.default_language
1193
+ defn << "vocab: #{self.vocab.to_s.inspect}" if self.vocab
1194
+ term_defs = term_definitions.map do |term, td|
1195
+ " " + term.inspect + " => " + td.to_rb
1131
1196
  end
1197
+ defn << "term_definitions: {\n#{term_defs.join(",\n") }\n }" unless term_defs.empty?
1198
+ %(# -*- encoding: utf-8 -*-
1199
+ # frozen_string_literal: true
1200
+ # This file generated automatically from #{context_base}
1201
+ require 'json/ld'
1202
+ class JSON::LD::Context
1203
+ ).gsub(/^ /, '') +
1204
+ " add_preloaded(#{RDF::URI(context_base).canonicalize.to_s.inspect}) do\n new(" + defn.join(", ") + ")\n end\nend\n"
1132
1205
  end
1133
1206
 
1134
1207
  def inspect
1135
1208
  v = %w([Context)
1136
1209
  v << "base=#{base}" if base
1137
1210
  v << "vocab=#{vocab}" if vocab
1138
- v << "def_language=#{default_language}" if default_language
1211
+ v << "default_language=#{default_language}" if default_language
1139
1212
  v << "term_definitions[#{term_definitions.length}]=#{term_definitions}"
1140
1213
  v.join(" ") + "]"
1141
1214
  end
@@ -1195,7 +1268,7 @@ module JSON::LD
1195
1268
  case value.to_s
1196
1269
  when /^_:(.*)$/
1197
1270
  # Map BlankNodes if a namer is given
1198
- log_debug "uri(bnode)#{value}: #{$1}"
1271
+ #log_debug "uri(bnode)#{value}: #{$1}"
1199
1272
  bnode(namer.get_sym($1))
1200
1273
  else
1201
1274
  value = RDF::URI.new(value)
@@ -1274,28 +1347,26 @@ module JSON::LD
1274
1347
  # for the type mapping or language mapping
1275
1348
  # @return [String]
1276
1349
  def select_term(iri, containers, type_language, preferred_values)
1277
- log_depth do
1278
- log_debug("select_term") {
1279
- "iri: #{iri.inspect}, " +
1280
- "containers: #{containers.inspect}, " +
1281
- "type_language: #{type_language.inspect}, " +
1282
- "preferred_values: #{preferred_values.inspect}"
1283
- }
1284
- container_map = inverse_context[iri]
1285
- log_debug(" ") {"container_map: #{container_map.inspect}"}
1286
- containers.each do |container|
1287
- next unless container_map.has_key?(container)
1288
- tl_map = container_map[container]
1289
- value_map = tl_map[type_language]
1290
- preferred_values.each do |item|
1291
- next unless value_map.has_key?(item)
1292
- log_debug("=>") {value_map[item].inspect}
1293
- return value_map[item]
1294
- end
1350
+ #log_debug("select_term") {
1351
+ # "iri: #{iri.inspect}, " +
1352
+ # "containers: #{containers.inspect}, " +
1353
+ # "type_language: #{type_language.inspect}, " +
1354
+ # "preferred_values: #{preferred_values.inspect}"
1355
+ #}
1356
+ container_map = inverse_context[iri]
1357
+ #log_debug(" ") {"container_map: #{container_map.inspect}"}
1358
+ containers.each do |container|
1359
+ next unless container_map.has_key?(container)
1360
+ tl_map = container_map[container]
1361
+ value_map = tl_map[type_language]
1362
+ preferred_values.each do |item|
1363
+ next unless value_map.has_key?(item)
1364
+ #log_debug("=>") {value_map[item].inspect}
1365
+ return value_map[item]
1295
1366
  end
1296
- log_debug("=>") {"nil"}
1297
- nil
1298
1367
  end
1368
+ #log_debug("=>") {"nil"}
1369
+ nil
1299
1370
  end
1300
1371
 
1301
1372
  ##
@@ -1315,9 +1386,9 @@ module JSON::LD
1315
1386
  b = base.to_s
1316
1387
  return iri[b.length..-1] if iri.start_with?(b) && %w(? #).include?(iri[b.length, 1])
1317
1388
 
1318
- @base_and_parents.each_with_index do |b, index|
1319
- next unless iri.start_with?(b)
1320
- rel = "../" * index + iri[b.length..-1]
1389
+ @base_and_parents.each_with_index do |bb, index|
1390
+ next unless iri.start_with?(bb)
1391
+ rel = "../" * index + iri[bb.length..-1]
1321
1392
  return rel.empty? ? "./" : rel
1322
1393
  end
1323
1394
  iri