json-ld 2.0.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +12 -1
- data/VERSION +1 -1
- data/lib/json/ld.rb +3 -1
- data/lib/json/ld/api.rb +23 -20
- data/lib/json/ld/compact.rb +27 -26
- data/lib/json/ld/context.rb +499 -428
- data/lib/json/ld/expand.rb +284 -281
- data/lib/json/ld/extensions.rb +2 -0
- data/lib/json/ld/flatten.rb +84 -88
- data/lib/json/ld/format.rb +3 -1
- data/lib/json/ld/frame.rb +154 -163
- data/lib/json/ld/from_rdf.rb +10 -6
- data/lib/json/ld/reader.rb +2 -0
- data/lib/json/ld/resource.rb +2 -0
- data/lib/json/ld/streaming_writer.rb +8 -6
- data/lib/json/ld/to_rdf.rb +11 -9
- data/lib/json/ld/utils.rb +2 -0
- data/lib/json/ld/version.rb +2 -0
- data/lib/json/ld/writer.rb +2 -0
- data/spec/context_spec.rb +73 -3
- data/spec/frame_spec.rb +97 -1
- metadata +6 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 693e380cfc75c04525a1144d742a4c98737df5bf
|
4
|
+
data.tar.gz: a6d0d85e8323532379bfeef2be95361b1a5c1a01
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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.
|
1
|
+
2.1.0
|
data/lib/json/ld.rb
CHANGED
data/lib/json/ld/api.rb
CHANGED
@@ -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
|
-
|
211
|
+
expanded_input = options[:expanded] ? input : API.expand(input, options)
|
208
212
|
|
209
|
-
API.new(
|
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 =
|
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 ('@
|
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 =
|
370
|
+
@context = @context.parse(frame['@context'])
|
370
371
|
# Compact result
|
371
|
-
compacted =
|
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
|
-
|
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
|
data/lib/json/ld/compact.rb
CHANGED
@@ -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
|
-
|
14
|
-
else
|
15
|
-
|
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 =
|
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
|
-
|
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 =
|
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 =
|
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 =
|
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
|
-
|
121
|
+
|
121
122
|
container = context.container(item_active_property)
|
122
123
|
value = list?(expanded_item) ? expanded_item['@list'] : expanded_item
|
123
|
-
compacted_item =
|
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
|
data/lib/json/ld/context.rb
CHANGED
@@ -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 [
|
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
|
-
|
42
|
-
|
43
|
-
|
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, "@
|
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, "@
|
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
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
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
|
-
|
287
|
-
|
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
|
-
|
290
|
-
|
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
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
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
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
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
|
-
|
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
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
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
|
-
#
|
573
|
-
|
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
|
-
# @
|
741
|
-
# @
|
742
|
-
# @
|
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
|
-
# @
|
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,
|
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
|
-
|
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
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
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
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
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
|
-
|
776
|
-
|
777
|
-
|
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
|
-
|
780
|
-
|
781
|
-
|
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
|
-
|
785
|
-
|
786
|
-
|
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
|
-
|
796
|
-
|
797
|
-
result = if
|
798
|
-
|
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
|
-
|
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
|
820
|
-
# @option options [Object] :value
|
879
|
+
# @param [Object] value
|
821
880
|
# Value, used to select among various maps for the same IRI
|
822
|
-
# @
|
881
|
+
# @param [Boolean] :vocab
|
823
882
|
# specifies whether the passed iri should be compacted using the active context's vocabulary mapping
|
824
|
-
# @
|
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,
|
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) {
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
tl, tl_value = "@
|
842
|
-
containers << '@
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
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
|
-
|
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
|
-
|
922
|
+
item_type = '@id'
|
885
923
|
end
|
886
|
-
|
887
|
-
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
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
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
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
|
-
|
942
|
+
tl_value = common_language
|
916
943
|
end
|
917
|
-
log_debug("") {"
|
918
|
-
|
919
|
-
|
920
|
-
|
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
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
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
|
-
|
932
|
-
|
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
|
-
|
935
|
-
|
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
|
-
|
939
|
-
|
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
|
-
|
942
|
-
|
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
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
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
|
-
|
952
|
-
#
|
953
|
-
|
954
|
-
|
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
|
-
|
963
|
-
|
964
|
-
|
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 !
|
969
|
-
#
|
970
|
-
|
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,
|
996
|
-
|
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
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
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
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1019
|
-
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
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
|
-
|
1041
|
-
res
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
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
|
-
|
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
|
-
|
1076
|
-
log_debug("compact_value") {"property: #{property.inspect}, value: #{value.inspect}"}
|
1130
|
+
num_members = value.keys.length
|
1077
1131
|
|
1078
|
-
|
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
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
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
|
-
|
1087
|
-
|
1088
|
-
|
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
|
-
|
1130
|
-
|
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 << "
|
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
|
-
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
|
1284
|
-
|
1285
|
-
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
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 |
|
1319
|
-
next unless iri.start_with?(
|
1320
|
-
rel = "../" * index + iri[
|
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
|