json-ld 3.1.3 → 3.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -1
- data/VERSION +1 -1
- data/lib/json/ld.rb +3 -0
- data/lib/json/ld/api.rb +31 -24
- data/lib/json/ld/compact.rb +37 -21
- data/lib/json/ld/context.rb +532 -473
- data/lib/json/ld/expand.rb +131 -68
- data/lib/json/ld/from_rdf.rb +7 -8
- data/lib/json/ld/reader.rb +19 -10
- data/lib/json/ld/streaming_reader.rb +578 -0
- data/spec/context_spec.rb +4 -42
- data/spec/reader_spec.rb +33 -34
- data/spec/streaming_reader_spec.rb +237 -0
- data/spec/suite_helper.rb +14 -8
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4755155aaabbcf15c3f29429eb085ac5e54c0785df8a84ebb42d8dbbb840c9b3
|
4
|
+
data.tar.gz: e8f5a6fc936ee33473f8f54ae9ee1587816b3146f4c94e179eebb2ebaa85feea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8ebbbad6fde10f4ce69d8485292a580d2d9dc9cf1ffafcd878efb03a3c47bdfcd2188f1f50ec3a4d96b1a3cf7718d5926118f0283204f7420765e691d5782b78
|
7
|
+
data.tar.gz: bb86cdff64bceb60c574e5a72448394c9882817990b4a734e29ab1f6bc2f177fabf2fb0437bfa5c312ca87a1f7be10123cae1bc45db8722fb1029701cb38f16c
|
data/README.md
CHANGED
@@ -19,6 +19,12 @@ JSON::LD can now be used to create a _context_ from an RDFS/OWL definition, and
|
|
19
19
|
|
20
20
|
Install with `gem install json-ld`
|
21
21
|
|
22
|
+
### JSON-LD Streaming Profile
|
23
|
+
This gem implements an optimized streaming reader used for generating RDF from large dataset dumps formatted as JSON-LD. Such documents must correspond to the [JSON-LD Streaming Profile](https://w3c.github.io/json-ld-streaming/):
|
24
|
+
|
25
|
+
* Keys in JSON objects must be ordered with any of `@context`, and/or `@type` coming before any other keys, in that order. This includes aliases of those keys. It is strongly encouraged that `@id` be present, and come immediately after.
|
26
|
+
* JSON-LD documents can be signaled or requested in [streaming document form](https://w3c.github.io/json-ld-streaming/#dfn-streaming-document-form). The profile URI identifying the [streaming document form](https://w3c.github.io/json-ld-streaming/#dfn-streaming-document-form) is `http://www.w3.org/ns/json-ld#streaming`.
|
27
|
+
|
22
28
|
### MultiJson parser
|
23
29
|
The [MultiJson](https://rubygems.org/gems/multi_json) gem is used for parsing JSON; this defaults to the native JSON parser, but will use a more performant parser if one is available. A specific parser can be specified by adding the `:adapter` option to any API call. See [MultiJson](https://rubygems.org/gems/multi_json) for more information.
|
24
30
|
|
@@ -230,7 +236,7 @@ In some cases, the built-in document loader {JSON::LD::API.documentLoader} is in
|
|
230
236
|
|
231
237
|
All entries into the {JSON::LD::API} accept a `:documentLoader` option, which can be used to provide an alternative method to use when loading remote documents. For example:
|
232
238
|
```ruby
|
233
|
-
|
239
|
+
load_document_local = Proc.new do |url, **options, &block|
|
234
240
|
if RDF::URI(url, canonicalize: true) == RDF::URI('http://schema.org/')
|
235
241
|
remote_document = JSON::LD::API::RemoteDocument.new(url, File.read("etc/schema.org.jsonld"))
|
236
242
|
return block_given? ? yield(remote_document) : remote_document
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.1.
|
1
|
+
3.1.4
|
data/lib/json/ld.rb
CHANGED
@@ -34,6 +34,8 @@ module JSON
|
|
34
34
|
autoload :Normalize, 'json/ld/normalize'
|
35
35
|
autoload :Reader, 'json/ld/reader'
|
36
36
|
autoload :Resource, 'json/ld/resource'
|
37
|
+
autoload :StreamingReader, 'json/ld/streaming_reader'
|
38
|
+
autoload :StreamingWriter, 'json/ld/streaming_writer'
|
37
39
|
autoload :VERSION, 'json/ld/version'
|
38
40
|
autoload :Writer, 'json/ld/writer'
|
39
41
|
|
@@ -143,6 +145,7 @@ module JSON
|
|
143
145
|
class InvalidScopedContext < JsonLdError; @code = "invalid scoped context"; end
|
144
146
|
class InvalidScriptElement < JsonLdError; @code = "invalid script element"; end
|
145
147
|
class InvalidSetOrListObject < JsonLdError; @code = "invalid set or list object"; end
|
148
|
+
class InvalidStreamingKeyOrder < JsonLdError; @code = 'invalid streaming key order' end
|
146
149
|
class InvalidTermDefinition < JsonLdError; @code = "invalid term definition"; end
|
147
150
|
class InvalidBaseDirection < JsonLdError; @code = "invalid base direction"; end
|
148
151
|
class InvalidTypedValue < JsonLdError; @code = "invalid typed value"; end
|
data/lib/json/ld/api.rb
CHANGED
@@ -66,7 +66,7 @@ module JSON::LD
|
|
66
66
|
# @param [String, #read, Hash, Array, JSON::LD::Context] context
|
67
67
|
# An external context to use additionally to the context embedded in input when expanding the input.
|
68
68
|
# @param [Hash{Symbol => Object}] options
|
69
|
-
# @option options [String, #to_s] :base
|
69
|
+
# @option options [RDF::URI, String, #to_s] :base
|
70
70
|
# The Base IRI to use when expanding the document. This overrides the value of `input` if it is a _IRI_. If not specified and `input` is not an _IRI_, the base IRI defaults to the current document IRI if in a browser context, or the empty string if there is no document context. If not specified, and a base IRI is found from `input`, options[:base] will be modified with this value.
|
71
71
|
# @option options [Boolean] :compactArrays (true)
|
72
72
|
# If set to `true`, the JSON-LD processor replaces arrays with just one element with that element during compaction. If set to `false`, all arrays will remain arrays even if they have just one element.
|
@@ -108,6 +108,7 @@ module JSON::LD
|
|
108
108
|
}.merge(options)
|
109
109
|
@namer = unique_bnodes ? BlankNodeUniqer.new : (rename_bnodes ? BlankNodeNamer.new("b") : BlankNodeMapper.new)
|
110
110
|
|
111
|
+
@options[:base] = RDF::URI(@options[:base]) if @options[:base] && !@options[:base].is_a?(RDF::URI)
|
111
112
|
# For context via Link header
|
112
113
|
_, context_ref = nil, nil
|
113
114
|
|
@@ -117,7 +118,7 @@ module JSON::LD
|
|
117
118
|
remote_doc = self.class.loadRemoteDocument(input, **@options)
|
118
119
|
|
119
120
|
context_ref = remote_doc.contextUrl
|
120
|
-
@options[:base] = remote_doc.documentUrl if remote_doc.documentUrl && !@options[:no_default_base]
|
121
|
+
@options[:base] = RDF::URI(remote_doc.documentUrl) if remote_doc.documentUrl && !@options[:no_default_base]
|
121
122
|
|
122
123
|
case remote_doc.document
|
123
124
|
when String
|
@@ -130,7 +131,7 @@ module JSON::LD
|
|
130
131
|
|
131
132
|
# If not provided, first use context from document, or from a Link header
|
132
133
|
context ||= context_ref || {}
|
133
|
-
@context = Context.parse(context
|
134
|
+
@context = Context.parse(context, **@options)
|
134
135
|
|
135
136
|
if block_given?
|
136
137
|
case block.arity
|
@@ -163,10 +164,9 @@ module JSON::LD
|
|
163
164
|
# If a block is given, the result of evaluating the block is returned, otherwise, the expanded JSON-LD document
|
164
165
|
# @see https://www.w3.org/TR/json-ld11-api/#expansion-algorithm
|
165
166
|
def self.expand(input, framing: false, **options, &block)
|
166
|
-
result
|
167
|
+
result = doc_base = nil
|
167
168
|
API.new(input, options[:expandContext], **options) do
|
168
169
|
result = self.expand(self.value, nil, self.context,
|
169
|
-
ordered: @options[:ordered],
|
170
170
|
framing: framing)
|
171
171
|
doc_base = @options[:base]
|
172
172
|
end
|
@@ -218,21 +218,21 @@ module JSON::LD
|
|
218
218
|
# 1) Perform the Expansion Algorithm on the JSON-LD input.
|
219
219
|
# This removes any existing context to allow the given context to be cleanly applied.
|
220
220
|
expanded_input = expanded ? input : API.expand(input, ordered: false, **options) do |res, base_iri|
|
221
|
-
options[:base] ||= base_iri if options[:compactToRelative]
|
221
|
+
options[:base] ||= RDF::URI(base_iri) if base_iri && options[:compactToRelative]
|
222
222
|
res
|
223
223
|
end
|
224
224
|
|
225
225
|
API.new(expanded_input, context, no_default_base: true, **options) do
|
226
226
|
log_debug(".compact") {"expanded input: #{expanded_input.to_json(JSON_STATE) rescue 'malformed json'}"}
|
227
|
-
result = compact(value
|
227
|
+
result = compact(value)
|
228
228
|
|
229
229
|
# xxx) Add the given context to the output
|
230
|
-
ctx = self.context.serialize
|
230
|
+
ctx = self.context.serialize(provided_context: context)
|
231
231
|
if result.is_a?(Array)
|
232
232
|
kwgraph = self.context.compact_iri('@graph', vocab: true)
|
233
233
|
result = result.empty? ? {} : {kwgraph => result}
|
234
234
|
end
|
235
|
-
result = ctx.merge(result) unless ctx.empty?
|
235
|
+
result = ctx.merge(result) unless ctx.fetch('@context', {}).empty?
|
236
236
|
end
|
237
237
|
block_given? ? yield(result) : result
|
238
238
|
end
|
@@ -265,7 +265,7 @@ module JSON::LD
|
|
265
265
|
|
266
266
|
# Expand input to simplify processing
|
267
267
|
expanded_input = expanded ? input : API.expand(input, **options) do |result, base_iri|
|
268
|
-
options[:base] ||= base_iri if options[:compactToRelative]
|
268
|
+
options[:base] ||= RDF::URI(base_iri) if base_iri && options[:compactToRelative]
|
269
269
|
result
|
270
270
|
end
|
271
271
|
|
@@ -294,9 +294,11 @@ module JSON::LD
|
|
294
294
|
|
295
295
|
if context && !flattened.empty?
|
296
296
|
# Otherwise, return the result of compacting flattened according the Compaction algorithm passing context ensuring that the compaction result uses the @graph keyword (or its alias) at the top-level, even if the context is empty or if there is only one element to put in the @graph array. This ensures that the returned document has a deterministic structure.
|
297
|
-
compacted = as_array(compact(flattened
|
297
|
+
compacted = as_array(compact(flattened))
|
298
298
|
kwgraph = self.context.compact_iri('@graph')
|
299
|
-
flattened = self.context.
|
299
|
+
flattened = self.context.
|
300
|
+
serialize(provided_context: context).
|
301
|
+
merge(kwgraph => compacted)
|
300
302
|
end
|
301
303
|
end
|
302
304
|
|
@@ -335,7 +337,7 @@ module JSON::LD
|
|
335
337
|
def self.frame(input, frame, expanded: false, **options)
|
336
338
|
result = nil
|
337
339
|
options = {
|
338
|
-
base: (input if input.is_a?(String)),
|
340
|
+
base: (RDF::URI(input) if input.is_a?(String)),
|
339
341
|
compactArrays: true,
|
340
342
|
compactToRelative: true,
|
341
343
|
embed: '@once',
|
@@ -369,7 +371,7 @@ module JSON::LD
|
|
369
371
|
|
370
372
|
# Expand input to simplify processing
|
371
373
|
expanded_input = expanded ? input : API.expand(input, ordered: false, **options) do |res, base_iri|
|
372
|
-
options[:base] ||= base_iri if options[:compactToRelative]
|
374
|
+
options[:base] ||= RDF::URI(base_iri) if base_iri && options[:compactToRelative]
|
373
375
|
res
|
374
376
|
end
|
375
377
|
|
@@ -426,7 +428,7 @@ module JSON::LD
|
|
426
428
|
log_debug(".frame") {"expanded result: #{result.to_json(JSON_STATE) rescue 'malformed json'}"}
|
427
429
|
|
428
430
|
# Compact result
|
429
|
-
compacted = compact(result
|
431
|
+
compacted = compact(result)
|
430
432
|
|
431
433
|
# @replace `@null` with nil, compacting arrays
|
432
434
|
compacted = cleanup_null(compacted)
|
@@ -434,11 +436,14 @@ module JSON::LD
|
|
434
436
|
|
435
437
|
# Add the given context to the output
|
436
438
|
result = if !compacted.is_a?(Array)
|
437
|
-
|
439
|
+
compacted
|
438
440
|
else
|
439
441
|
kwgraph = context.compact_iri('@graph')
|
440
|
-
|
442
|
+
{kwgraph => compacted}
|
441
443
|
end
|
444
|
+
# Only add context if one was provided
|
445
|
+
result = context.serialize(provided_context: frame).merge(result) if frame['@context']
|
446
|
+
|
442
447
|
log_debug(".frame") {"after compact: #{result.to_json(JSON_STATE) rescue 'malformed json'}"}
|
443
448
|
result
|
444
449
|
end
|
@@ -521,8 +526,7 @@ module JSON::LD
|
|
521
526
|
API.new(nil, nil, **options) do
|
522
527
|
result = from_statements(input,
|
523
528
|
useRdfType: useRdfType,
|
524
|
-
useNativeTypes: useNativeTypes
|
525
|
-
ordered: @options[:ordered])
|
529
|
+
useNativeTypes: useNativeTypes)
|
526
530
|
end
|
527
531
|
|
528
532
|
block_given? ? yield(result) : result
|
@@ -532,16 +536,18 @@ module JSON::LD
|
|
532
536
|
# Uses built-in or provided documentLoader to retrieve a parsed document.
|
533
537
|
#
|
534
538
|
# @param [RDF::URI, String] url
|
539
|
+
# @param [String, RDF::URI] base
|
540
|
+
# Location to use as documentUrl instead of `url`.
|
541
|
+
# @option options [Proc] :documentLoader
|
542
|
+
# The callback of the loader to be used to retrieve remote documents and contexts.
|
535
543
|
# @param [Boolean] extractAllScripts
|
536
544
|
# If set to `true`, when extracting JSON-LD script elements from HTML, unless a specific fragment identifier is targeted, extracts all encountered JSON-LD script elements using an array form, if necessary.
|
537
545
|
# @param [String] profile
|
538
546
|
# When the resulting `contentType` is `text/html` or `application/xhtml+xml`, this option determines the profile to use for selecting a JSON-LD script elements.
|
539
547
|
# @param [String] requestProfile
|
540
548
|
# One or more IRIs to use in the request as a profile parameter.
|
541
|
-
# @param [Boolean] validate
|
549
|
+
# @param [Boolean] validate (false)
|
542
550
|
# Allow only appropriate content types
|
543
|
-
# @param [String, RDF::URI] base
|
544
|
-
# Location to use as documentUrl instead of `url`.
|
545
551
|
# @param [Hash<Symbol => Object>] options
|
546
552
|
# @yield remote_document
|
547
553
|
# @yieldparam [RemoteDocumentRemoteDocument, RDF::Util::File::RemoteDocument] remote_document
|
@@ -550,13 +556,14 @@ module JSON::LD
|
|
550
556
|
# If a block is given, the result of evaluating the block is returned, otherwise, the retrieved remote document and context information unless block given
|
551
557
|
# @raise [JsonLdError]
|
552
558
|
def self.loadRemoteDocument(url,
|
559
|
+
base: nil,
|
560
|
+
documentLoader: nil,
|
553
561
|
extractAllScripts: false,
|
554
562
|
profile: nil,
|
555
563
|
requestProfile: nil,
|
556
564
|
validate: false,
|
557
|
-
base: nil,
|
558
565
|
**options)
|
559
|
-
documentLoader
|
566
|
+
documentLoader ||= self.method(:documentLoader)
|
560
567
|
options = OPEN_OPTS.merge(options)
|
561
568
|
if requestProfile
|
562
569
|
# Add any request profile
|
data/lib/json/ld/compact.rb
CHANGED
@@ -12,11 +12,14 @@ module JSON::LD
|
|
12
12
|
# This algorithm compacts a JSON-LD document, such that the given context is applied. This must result in shortening any applicable IRIs to terms or compact IRIs, any applicable keywords to keyword aliases, and any applicable JSON-LD values expressed in expanded form to simple values such as strings or numbers.
|
13
13
|
#
|
14
14
|
# @param [Array, Hash] element
|
15
|
-
# @param [String]
|
16
|
-
# @param [Boolean] ordered (true)
|
15
|
+
# @param [String, RDF::URI] base (nil)
|
17
16
|
# Ensure output objects have keys ordered properly
|
17
|
+
# @param [String] property (nil)
|
18
|
+
# Extra validatation
|
18
19
|
# @return [Array, Hash]
|
19
|
-
def compact(element,
|
20
|
+
def compact(element,
|
21
|
+
base: nil,
|
22
|
+
property: nil)
|
20
23
|
#if property.nil?
|
21
24
|
# log_debug("compact") {"element: #{element.inspect}, ec: #{context.inspect}"}
|
22
25
|
#else
|
@@ -29,7 +32,9 @@ module JSON::LD
|
|
29
32
|
case element
|
30
33
|
when Array
|
31
34
|
#log_debug("") {"Array #{element.inspect}"}
|
32
|
-
result = element.map
|
35
|
+
result = element.map do |item|
|
36
|
+
compact(item, base: base, property: property)
|
37
|
+
end.compact
|
33
38
|
|
34
39
|
# If element has a single member and the active property has no
|
35
40
|
# @container mapping to @list or @set, the compacted value is that
|
@@ -55,10 +60,13 @@ module JSON::LD
|
|
55
60
|
|
56
61
|
# Look up term definintions from property using the original type-scoped context, if it exists, but apply them to the now current previous context
|
57
62
|
td = input_context.term_definitions[property] if property
|
58
|
-
|
63
|
+
if td && !td.context.nil?
|
64
|
+
self.context = context.parse(td.context,
|
65
|
+
override_protected: true)
|
66
|
+
end
|
59
67
|
|
60
68
|
if element.key?('@id') || element.key?('@value')
|
61
|
-
result = context.compact_value(property, element,
|
69
|
+
result = context.compact_value(property, element, base: @options[:base])
|
62
70
|
if !result.is_a?(Hash) || context.coerce(property) == '@json'
|
63
71
|
#log_debug("") {"=> scalar result: #{result.inspect}"}
|
64
72
|
return result
|
@@ -67,7 +75,8 @@ module JSON::LD
|
|
67
75
|
|
68
76
|
# If expanded property is @list and we're contained within a list container, recursively compact this item to an array
|
69
77
|
if list?(element) && context.container(property).include?('@list')
|
70
|
-
return compact(element['@list'],
|
78
|
+
return compact(element['@list'], base: base,
|
79
|
+
property: property)
|
71
80
|
end
|
72
81
|
|
73
82
|
inside_reverse = property == '@reverse'
|
@@ -80,15 +89,17 @@ module JSON::LD
|
|
80
89
|
sort.
|
81
90
|
each do |term|
|
82
91
|
term_context = input_context.term_definitions[term].context if input_context.term_definitions[term]
|
83
|
-
self.context = context.parse(term_context, propagate: false)
|
92
|
+
self.context = context.parse(term_context, propagate: false) unless term_context.nil?
|
84
93
|
end
|
85
94
|
|
86
|
-
element.keys.opt_sort(ordered: ordered).each do |expanded_property|
|
95
|
+
element.keys.opt_sort(ordered: @options[:ordered]).each do |expanded_property|
|
87
96
|
expanded_value = element[expanded_property]
|
88
97
|
#log_debug("") {"#{expanded_property}: #{expanded_value.inspect}"}
|
89
98
|
|
90
99
|
if expanded_property == '@id'
|
91
|
-
compacted_value = Array(expanded_value).map
|
100
|
+
compacted_value = Array(expanded_value).map do |expanded_id|
|
101
|
+
context.compact_iri(expanded_id, base: @options[:base])
|
102
|
+
end
|
92
103
|
|
93
104
|
kw_alias = context.compact_iri('@id', vocab: true)
|
94
105
|
as_array = compacted_value.length > 1
|
@@ -98,7 +109,9 @@ module JSON::LD
|
|
98
109
|
end
|
99
110
|
|
100
111
|
if expanded_property == '@type'
|
101
|
-
compacted_value = Array(expanded_value).map
|
112
|
+
compacted_value = Array(expanded_value).map do |expanded_type|
|
113
|
+
input_context.compact_iri(expanded_type, vocab: true)
|
114
|
+
end
|
102
115
|
|
103
116
|
kw_alias = context.compact_iri('@type', vocab: true)
|
104
117
|
as_array = compacted_value.length > 1 ||
|
@@ -110,7 +123,8 @@ module JSON::LD
|
|
110
123
|
end
|
111
124
|
|
112
125
|
if expanded_property == '@reverse'
|
113
|
-
compacted_value = compact(expanded_value,
|
126
|
+
compacted_value = compact(expanded_value, base: base,
|
127
|
+
property: '@reverse')
|
114
128
|
#log_debug("@reverse") {"compacted_value: #{compacted_value.inspect}"}
|
115
129
|
# handle double-reversed properties
|
116
130
|
compacted_value.each do |prop, value|
|
@@ -131,7 +145,8 @@ module JSON::LD
|
|
131
145
|
|
132
146
|
if expanded_property == '@preserve'
|
133
147
|
# Compact using `property`
|
134
|
-
compacted_value = compact(expanded_value,
|
148
|
+
compacted_value = compact(expanded_value, base: base,
|
149
|
+
property: property)
|
135
150
|
#log_debug("@preserve") {"compacted_value: #{compacted_value.inspect}"}
|
136
151
|
|
137
152
|
unless compacted_value.is_a?(Array) && compacted_value.empty?
|
@@ -158,8 +173,7 @@ module JSON::LD
|
|
158
173
|
context.compact_iri(expanded_property,
|
159
174
|
value: expanded_value,
|
160
175
|
vocab: true,
|
161
|
-
reverse: inside_reverse
|
162
|
-
log_depth: @options[:log_depth])
|
176
|
+
reverse: inside_reverse)
|
163
177
|
|
164
178
|
if nest_prop = context.nest(item_active_property)
|
165
179
|
result[nest_prop] ||= {}
|
@@ -177,8 +191,7 @@ module JSON::LD
|
|
177
191
|
context.compact_iri(expanded_property,
|
178
192
|
value: expanded_item,
|
179
193
|
vocab: true,
|
180
|
-
reverse: inside_reverse
|
181
|
-
log_depth: @options[:log_depth])
|
194
|
+
reverse: inside_reverse)
|
182
195
|
|
183
196
|
|
184
197
|
nest_result = if nest_prop = context.nest(item_active_property)
|
@@ -197,7 +210,8 @@ module JSON::LD
|
|
197
210
|
else expanded_item
|
198
211
|
end
|
199
212
|
|
200
|
-
compacted_item = compact(value,
|
213
|
+
compacted_item = compact(value, base: base,
|
214
|
+
property: item_active_property)
|
201
215
|
#log_debug("") {" => compacted key: #{item_active_property.inspect} for #{compacted_item.inspect}"}
|
202
216
|
|
203
217
|
# handle @list
|
@@ -225,9 +239,9 @@ module JSON::LD
|
|
225
239
|
map_object = nest_result[item_active_property] ||= {}
|
226
240
|
# If there is no @id, create a blank node identifier to use as an index
|
227
241
|
map_key = if container.include?('@id') && expanded_item['@id']
|
228
|
-
context.compact_iri(expanded_item['@id'])
|
242
|
+
context.compact_iri(expanded_item['@id'], base: @options[:base])
|
229
243
|
elsif container.include?('@index') && expanded_item['@index']
|
230
|
-
context.compact_iri(expanded_item['@index'])
|
244
|
+
context.compact_iri(expanded_item['@index'], vocab: true)
|
231
245
|
else
|
232
246
|
context.compact_iri('@none', vocab: true)
|
233
247
|
end
|
@@ -299,7 +313,9 @@ module JSON::LD
|
|
299
313
|
|
300
314
|
# if compacted_item contains a single entry who's key maps to @id, then recompact the item without @type
|
301
315
|
if compacted_item.keys.length == 1 && expanded_item.keys.include?('@id')
|
302
|
-
compacted_item = compact({'@id' => expanded_item['@id']},
|
316
|
+
compacted_item = compact({'@id' => expanded_item['@id']},
|
317
|
+
base: base,
|
318
|
+
property: item_active_property)
|
303
319
|
end
|
304
320
|
compacted_item
|
305
321
|
end
|
data/lib/json/ld/context.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
require 'json'
|
4
4
|
require 'bigdecimal'
|
5
5
|
require 'set'
|
6
|
+
require 'rdf/util/cache'
|
7
|
+
|
6
8
|
begin
|
7
9
|
# Attempt to load this to avoid unnecessary context fetches
|
8
10
|
require 'json-ld-preloaded'
|
@@ -21,10 +23,13 @@ module JSON::LD
|
|
21
23
|
# @return [Hash{Symbol => Context}]
|
22
24
|
PRELOADED = {}
|
23
25
|
|
26
|
+
# Initial contexts, defined on first access
|
27
|
+
INITIAL_CONTEXTS = {}
|
28
|
+
|
24
29
|
##
|
25
30
|
# Defines the maximum number of interned URI references that can be held
|
26
31
|
# cached in memory at any one time.
|
27
|
-
CACHE_SIZE =
|
32
|
+
CACHE_SIZE = 100 # unlimited by default
|
28
33
|
|
29
34
|
class << self
|
30
35
|
##
|
@@ -45,242 +50,11 @@ module JSON::LD
|
|
45
50
|
end
|
46
51
|
end
|
47
52
|
|
48
|
-
# Term Definitions specify how properties and values have to be interpreted as well as the current vocabulary mapping and the default language
|
49
|
-
class TermDefinition
|
50
|
-
# @return [RDF::URI] IRI map
|
51
|
-
attr_accessor :id
|
52
|
-
|
53
|
-
# @return [String] term name
|
54
|
-
attr_accessor :term
|
55
|
-
|
56
|
-
# @return [String] Type mapping
|
57
|
-
attr_accessor :type_mapping
|
58
|
-
|
59
|
-
# Base container mapping, without @set
|
60
|
-
# @return [Array<'@index', '@language', '@index', '@set', '@type', '@id', '@graph'>] Container mapping
|
61
|
-
attr_reader :container_mapping
|
62
|
-
|
63
|
-
# @return [String] Term used for nest properties
|
64
|
-
attr_accessor :nest
|
65
|
-
|
66
|
-
# Language mapping of term, `false` is used if there is an explicit language mapping for this term.
|
67
|
-
# @return [String] Language mapping
|
68
|
-
attr_accessor :language_mapping
|
69
|
-
|
70
|
-
# Direction of term, `false` is used if there is explicit direction mapping mapping for this term.
|
71
|
-
# @return ["ltr", "rtl"] direction_mapping
|
72
|
-
attr_accessor :direction_mapping
|
73
|
-
|
74
|
-
# @return [Boolean] Reverse Property
|
75
|
-
attr_accessor :reverse_property
|
76
|
-
|
77
|
-
# This is a simple term definition, not an expanded term definition
|
78
|
-
# @return [Boolean]
|
79
|
-
attr_accessor :simple
|
80
|
-
|
81
|
-
# Property used for data indexing; defaults to @index
|
82
|
-
# @return [Boolean]
|
83
|
-
attr_accessor :index
|
84
|
-
|
85
|
-
# Indicate that term may be used as a prefix
|
86
|
-
attr_writer :prefix
|
87
|
-
|
88
|
-
# Term-specific context
|
89
|
-
# @return [Hash{String => Object}]
|
90
|
-
attr_accessor :context
|
91
|
-
|
92
|
-
# Term is protected.
|
93
|
-
# @return [Boolean]
|
94
|
-
attr_writer :protected
|
95
|
-
|
96
|
-
# This is a simple term definition, not an expanded term definition
|
97
|
-
# @return [Boolean] simple
|
98
|
-
def simple?; simple; end
|
99
|
-
|
100
|
-
# This is an appropriate term to use as the prefix of a compact IRI
|
101
|
-
# @return [Boolean] simple
|
102
|
-
def prefix?; @prefix; end
|
103
|
-
|
104
|
-
# Create a new Term Mapping with an ID
|
105
|
-
# @param [String] term
|
106
|
-
# @param [String] id
|
107
|
-
# @param [String] type_mapping Type mapping
|
108
|
-
# @param [Set<'@index', '@language', '@index', '@set', '@type', '@id', '@graph'>] container_mapping
|
109
|
-
# @param [String] language_mapping
|
110
|
-
# Language mapping of term, `false` is used if there is an explicit language mapping for this term
|
111
|
-
# @param ["ltr", "rtl"] direction_mapping
|
112
|
-
# Direction mapping of term, `false` is used if there is an explicit direction mapping for this term
|
113
|
-
# @param [Boolean] reverse_property
|
114
|
-
# @param [Boolean] protected
|
115
|
-
# @param [String] nest term used for nest properties
|
116
|
-
# @param [Boolean] simple
|
117
|
-
# This is a simple term definition, not an expanded term definition
|
118
|
-
# @param [Boolean] prefix
|
119
|
-
# Term may be used as a prefix
|
120
|
-
def initialize(term,
|
121
|
-
id: nil,
|
122
|
-
index: nil,
|
123
|
-
type_mapping: nil,
|
124
|
-
container_mapping: nil,
|
125
|
-
language_mapping: nil,
|
126
|
-
direction_mapping: nil,
|
127
|
-
reverse_property: false,
|
128
|
-
nest: nil,
|
129
|
-
protected: nil,
|
130
|
-
simple: false,
|
131
|
-
prefix: nil,
|
132
|
-
context: nil)
|
133
|
-
@term = term
|
134
|
-
@id = id.to_s unless id.nil?
|
135
|
-
@index = index.to_s unless index.nil?
|
136
|
-
@type_mapping = type_mapping.to_s unless type_mapping.nil?
|
137
|
-
self.container_mapping = container_mapping
|
138
|
-
@language_mapping = language_mapping unless language_mapping.nil?
|
139
|
-
@direction_mapping = direction_mapping unless direction_mapping.nil?
|
140
|
-
@reverse_property = reverse_property
|
141
|
-
@protected = protected
|
142
|
-
@nest = nest unless nest.nil?
|
143
|
-
@simple = simple
|
144
|
-
@prefix = prefix unless prefix.nil?
|
145
|
-
@context = context unless context.nil?
|
146
|
-
end
|
147
|
-
|
148
|
-
# Term is protected.
|
149
|
-
# @return [Boolean]
|
150
|
-
def protected?; !!@protected; end
|
151
|
-
|
152
|
-
# Set container mapping, from an array which may include @set
|
153
|
-
def container_mapping=(mapping)
|
154
|
-
mapping = case mapping
|
155
|
-
when Set then mapping
|
156
|
-
when Array then Set.new(mapping)
|
157
|
-
when String then Set[mapping]
|
158
|
-
when nil then Set.new
|
159
|
-
else
|
160
|
-
raise "Shouldn't happen with #{mapping.inspect}"
|
161
|
-
end
|
162
|
-
if @as_set = mapping.include?('@set')
|
163
|
-
mapping = mapping.dup
|
164
|
-
mapping.delete('@set')
|
165
|
-
end
|
166
|
-
@container_mapping = mapping
|
167
|
-
@index ||= '@index' if mapping.include?('@index')
|
168
|
-
end
|
169
|
-
|
170
|
-
##
|
171
|
-
# Output Hash or String definition for this definition considering @language and @vocab
|
172
|
-
#
|
173
|
-
# @param [Context] context
|
174
|
-
# @return [String, Hash{String => Array[String], String}]
|
175
|
-
def to_context_definition(context)
|
176
|
-
cid = if context.vocab && id.start_with?(context.vocab)
|
177
|
-
# Nothing to return unless it's the same as the vocab
|
178
|
-
id == context.vocab ? context.vocab : id.to_s[context.vocab.length..-1]
|
179
|
-
else
|
180
|
-
# Find a term to act as a prefix
|
181
|
-
iri, prefix = context.iri_to_term.detect {|i,p| id.to_s.start_with?(i.to_s)}
|
182
|
-
iri && iri != id ? "#{prefix}:#{id.to_s[iri.length..-1]}" : id
|
183
|
-
end
|
184
|
-
|
185
|
-
if simple?
|
186
|
-
cid.to_s unless cid == term && context.vocab
|
187
|
-
else
|
188
|
-
defn = {}
|
189
|
-
defn[reverse_property ? '@reverse' : '@id'] = cid.to_s unless cid == term && !reverse_property
|
190
|
-
if type_mapping
|
191
|
-
defn['@type'] = if KEYWORDS.include?(type_mapping)
|
192
|
-
type_mapping
|
193
|
-
else
|
194
|
-
context.compact_iri(type_mapping, vocab: true)
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
cm = Array(container_mapping)
|
199
|
-
cm << "@set" if as_set? && !cm.include?("@set")
|
200
|
-
cm = cm.first if cm.length == 1
|
201
|
-
defn['@container'] = cm unless cm.empty?
|
202
|
-
# Language set as false to be output as null
|
203
|
-
defn['@language'] = (@language_mapping ? @language_mapping : nil) unless @language_mapping.nil?
|
204
|
-
defn['@context'] = @context if @context
|
205
|
-
defn['@nest'] = @nest if @nest
|
206
|
-
defn['@index'] = @index if @index
|
207
|
-
defn['@prefix'] = @prefix unless @prefix.nil?
|
208
|
-
defn
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
##
|
213
|
-
# Turn this into a source for a new instantiation
|
214
|
-
# FIXME: context serialization
|
215
|
-
# @return [String]
|
216
|
-
def to_rb
|
217
|
-
defn = [%(TermDefinition.new\(#{term.inspect})]
|
218
|
-
%w(id index type_mapping container_mapping language_mapping direction_mapping reverse_property nest simple prefix context protected).each do |acc|
|
219
|
-
v = instance_variable_get("@#{acc}".to_sym)
|
220
|
-
v = v.to_s if v.is_a?(RDF::Term)
|
221
|
-
if acc == 'container_mapping'
|
222
|
-
v = v.to_a
|
223
|
-
v << '@set' if as_set?
|
224
|
-
v = v.first if v.length <= 1
|
225
|
-
end
|
226
|
-
defn << "#{acc}: #{v.inspect}" if v
|
227
|
-
end
|
228
|
-
defn.join(', ') + ")"
|
229
|
-
end
|
230
|
-
|
231
|
-
# If container mapping was defined along with @set
|
232
|
-
# @return [Boolean]
|
233
|
-
def as_set?; @as_set || false; end
|
234
|
-
|
235
|
-
# Check if term definitions are identical, modulo @protected
|
236
|
-
# @return [Boolean]
|
237
|
-
def ==(other)
|
238
|
-
other.is_a?(TermDefinition) &&
|
239
|
-
id == other.id &&
|
240
|
-
term == other.term &&
|
241
|
-
type_mapping == other.type_mapping &&
|
242
|
-
container_mapping == other.container_mapping &&
|
243
|
-
nest == other.nest &&
|
244
|
-
language_mapping == other.language_mapping &&
|
245
|
-
direction_mapping == other.direction_mapping &&
|
246
|
-
reverse_property == other.reverse_property &&
|
247
|
-
simple == other.simple &&
|
248
|
-
index == other.index &&
|
249
|
-
context == other.context &&
|
250
|
-
prefix? == other.prefix? &&
|
251
|
-
as_set? == other.as_set?
|
252
|
-
end
|
253
|
-
|
254
|
-
def inspect
|
255
|
-
v = %w([TD)
|
256
|
-
v << "id=#{@id}"
|
257
|
-
v << "index=#{index.inspect}" unless index.nil?
|
258
|
-
v << "term=#{@term}"
|
259
|
-
v << "rev" if reverse_property
|
260
|
-
v << "container=#{container_mapping}" if container_mapping
|
261
|
-
v << "as_set=#{as_set?.inspect}"
|
262
|
-
v << "lang=#{language_mapping.inspect}" unless language_mapping.nil?
|
263
|
-
v << "dir=#{direction_mapping.inspect}" unless direction_mapping.nil?
|
264
|
-
v << "type=#{type_mapping}" unless type_mapping.nil?
|
265
|
-
v << "nest=#{nest.inspect}" unless nest.nil?
|
266
|
-
v << "simple=true" if @simple
|
267
|
-
v << "protected=true" if @protected
|
268
|
-
v << "prefix=#{@prefix.inspect}" unless @prefix.nil?
|
269
|
-
v << "has-context" unless context.nil?
|
270
|
-
v.join(" ") + "]"
|
271
|
-
end
|
272
|
-
end
|
273
|
-
|
274
53
|
# The base.
|
275
54
|
#
|
276
55
|
# @return [RDF::URI] Current base IRI, used for expanding relative IRIs.
|
277
56
|
attr_reader :base
|
278
57
|
|
279
|
-
# The base.
|
280
|
-
#
|
281
|
-
# @return [RDF::URI] Document base IRI, to initialize `base`.
|
282
|
-
attr_reader :doc_base
|
283
|
-
|
284
58
|
# @return [RDF::URI] base IRI of the context, if loaded remotely.
|
285
59
|
attr_accessor :context_base
|
286
60
|
|
@@ -321,9 +95,6 @@ module JSON::LD
|
|
321
95
|
# @return [Hash{Symbol => Object}] Global options used in generating IRIs
|
322
96
|
attr_accessor :options
|
323
97
|
|
324
|
-
# @return [Context] A context provided to us that we can use without re-serializing XXX
|
325
|
-
attr_accessor :provided_context
|
326
|
-
|
327
98
|
# @return [BlankNodeNamer]
|
328
99
|
attr_accessor :namer
|
329
100
|
|
@@ -333,11 +104,33 @@ module JSON::LD
|
|
333
104
|
# @see #initialize
|
334
105
|
# @see #parse
|
335
106
|
# @param [String, #read, Array, Hash, Context] local_context
|
107
|
+
# @param [String, #to_s] :base (nil)
|
108
|
+
# The Base IRI to use when expanding the document. This overrides the value of `input` if it is a _IRI_. If not specified and `input` is not an _IRI_, the base IRI defaults to the current document IRI if in a browser context, or the empty string if there is no document context.
|
109
|
+
# @param [Proc] :documentLoader (nil)
|
110
|
+
# The callback of the loader to be used to retrieve remote documents and contexts. If specified, it must be used to retrieve remote documents and contexts; otherwise, if not specified, the processor's built-in loader must be used. See {API.documentLoader} for the method signature.
|
111
|
+
# @param [Boolean] override_protected (false)
|
112
|
+
# Protected terms may be cleared.
|
113
|
+
# @param [Boolean] propagate (true)
|
114
|
+
# If false, retains any previously defined term, which can be rolled back when the descending into a new node object changes.
|
115
|
+
# @param [Boolean] validate (false)
|
116
|
+
# Extra validatation
|
336
117
|
# @raise [JsonLdError]
|
337
118
|
# on a remote context load error, syntax error, or a reference to a term which is not defined.
|
338
119
|
# @return [Context]
|
339
|
-
def self.parse(local_context,
|
340
|
-
|
120
|
+
def self.parse(local_context,
|
121
|
+
base: nil,
|
122
|
+
override_protected: false,
|
123
|
+
propagate: true,
|
124
|
+
**options)
|
125
|
+
c = self.new(**options)
|
126
|
+
if local_context.respond_to?(:empty?) && local_context.empty?
|
127
|
+
c
|
128
|
+
else
|
129
|
+
c.parse(local_context,
|
130
|
+
base: base,
|
131
|
+
override_protected: override_protected,
|
132
|
+
propagate: propagate)
|
133
|
+
end
|
341
134
|
end
|
342
135
|
|
343
136
|
##
|
@@ -346,17 +139,50 @@ module JSON::LD
|
|
346
139
|
# @return [RDF::Util::Cache]
|
347
140
|
# @private
|
348
141
|
def self.cache
|
349
|
-
require 'rdf/util/cache' unless defined?(::RDF::Util::Cache)
|
350
142
|
@cache ||= RDF::Util::Cache.new(CACHE_SIZE)
|
351
143
|
end
|
352
144
|
|
145
|
+
##
|
146
|
+
# Class-level cache inverse contexts.
|
147
|
+
#
|
148
|
+
# @return [RDF::Util::Cache]
|
149
|
+
# @private
|
150
|
+
def self.inverse_cache
|
151
|
+
@inverse_cache ||= RDF::Util::Cache.new(CACHE_SIZE)
|
152
|
+
end
|
153
|
+
|
154
|
+
##
|
155
|
+
# @private
|
156
|
+
# Allow caching of well-known contexts
|
157
|
+
def self.new(**options)
|
158
|
+
if (options.keys - [
|
159
|
+
:compactArrays,
|
160
|
+
:documentLoader,
|
161
|
+
:extractAllScripts,
|
162
|
+
:ordered,
|
163
|
+
:processingMode,
|
164
|
+
:validate
|
165
|
+
]).empty?
|
166
|
+
# allow caching
|
167
|
+
key = options.hash
|
168
|
+
INITIAL_CONTEXTS[key] ||= begin
|
169
|
+
context = JSON::LD::Context.allocate
|
170
|
+
context.send(:initialize, **options)
|
171
|
+
context.freeze
|
172
|
+
context.term_definitions.freeze
|
173
|
+
context
|
174
|
+
end
|
175
|
+
else
|
176
|
+
# Don't try to cache
|
177
|
+
context = JSON::LD::Context.allocate
|
178
|
+
context.send(:initialize, **options)
|
179
|
+
context
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
353
183
|
##
|
354
184
|
# Create new evaluation context
|
355
185
|
# @param [Hash] options
|
356
|
-
# @option options [String, #to_s] :base
|
357
|
-
# The Base IRI to use when expanding the document. This overrides the value of `input` if it is a _IRI_. If not specified and `input` is not an _IRI_, the base IRI defaults to the current document IRI if in a browser context, or the empty string if there is no document context.
|
358
|
-
# @option options [Proc] :documentLoader
|
359
|
-
# The callback of the loader to be used to retrieve remote documents and contexts. If specified, it must be used to retrieve remote documents and contexts; otherwise, if not specified, the processor's built-in loader must be used. See {API.documentLoader} for the method signature.
|
360
186
|
# @option options [Hash{Symbol => String}] :prefixes
|
361
187
|
# See `RDF::Reader#initialize`
|
362
188
|
# @option options [String, #to_s] :vocab
|
@@ -367,11 +193,9 @@ module JSON::LD
|
|
367
193
|
# @yieldparam [Context]
|
368
194
|
# @return [Context]
|
369
195
|
def initialize(**options)
|
370
|
-
if options[:
|
371
|
-
@
|
372
|
-
@doc_base.canonicalize! if options[:canonicalize]
|
196
|
+
if options[:processingMode] == 'json-ld-1.0'
|
197
|
+
@processingMode = 'json-ld-1.0'
|
373
198
|
end
|
374
|
-
self.processingMode = options[:processingMode] if options.has_key?(:processingMode)
|
375
199
|
@term_definitions = {}
|
376
200
|
@iri_to_term = {
|
377
201
|
RDF.to_uri.to_s => "rdf",
|
@@ -397,135 +221,6 @@ module JSON::LD
|
|
397
221
|
yield(self) if block_given?
|
398
222
|
end
|
399
223
|
|
400
|
-
##
|
401
|
-
# Initial context, without mappings, vocab or default language
|
402
|
-
#
|
403
|
-
# @return [Boolean]
|
404
|
-
def empty?
|
405
|
-
@term_definitions.empty? && self.vocab.nil? && self.default_language.nil?
|
406
|
-
end
|
407
|
-
|
408
|
-
# @param [String] value must be an absolute IRI
|
409
|
-
def base=(value, **options)
|
410
|
-
if value
|
411
|
-
raise JsonLdError::InvalidBaseIRI, "@base must be a string: #{value.inspect}" unless value.is_a?(String) || value.is_a?(RDF::URI)
|
412
|
-
value = RDF::URI(value).dup
|
413
|
-
value = @base.join(value) if @base && value.relative?
|
414
|
-
@base = value
|
415
|
-
@base.canonicalize! if @options[:canonicalize]
|
416
|
-
raise JsonLdError::InvalidBaseIRI, "@base must be an absolute IRI: #{value.inspect}" unless @base.absolute? || !@options[:validate]
|
417
|
-
@base
|
418
|
-
else
|
419
|
-
@base = nil
|
420
|
-
end
|
421
|
-
|
422
|
-
end
|
423
|
-
|
424
|
-
# @param [String] value
|
425
|
-
def default_language=(value, **options)
|
426
|
-
@default_language = case value
|
427
|
-
when String
|
428
|
-
# Warn on an invalid language tag, unless :validate is true, in which case it's an error
|
429
|
-
if value !~ /^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/
|
430
|
-
warn "@language must be valid BCP47: #{value.inspect}"
|
431
|
-
end
|
432
|
-
options[:lowercaseLanguage] ? value.downcase : value
|
433
|
-
when nil
|
434
|
-
nil
|
435
|
-
else
|
436
|
-
raise JsonLdError::InvalidDefaultLanguage, "@language must be a string: #{value.inspect}"
|
437
|
-
end
|
438
|
-
end
|
439
|
-
|
440
|
-
# @param [String] value
|
441
|
-
def default_direction=(value, **options)
|
442
|
-
@default_direction = if value
|
443
|
-
raise JsonLdError::InvalidBaseDirection, "@direction must be one or 'ltr', or 'rtl': #{value.inspect}" unless %w(ltr rtl).include?(value)
|
444
|
-
value
|
445
|
-
else
|
446
|
-
nil
|
447
|
-
end
|
448
|
-
end
|
449
|
-
|
450
|
-
##
|
451
|
-
# Retrieve, or check processing mode.
|
452
|
-
#
|
453
|
-
# * With no arguments, retrieves the current set processingMode.
|
454
|
-
# * With an argument, verifies that the processingMode is at least that provided, either as an integer, or a string of the form "json-ld-1.x"
|
455
|
-
# * If expecting 1.1, and not set, it has the side-effect of setting mode to json-ld-1.1.
|
456
|
-
#
|
457
|
-
# @param [String, Number] expected (nil)
|
458
|
-
# @return [String]
|
459
|
-
def processingMode(expected = nil)
|
460
|
-
case expected
|
461
|
-
when 1.0, 'json-ld-1.0'
|
462
|
-
@processingMode == 'json-ld-1.0'
|
463
|
-
when 1.1, 'json-ld-1.1'
|
464
|
-
@processingMode ||= 'json-ld-1.1'
|
465
|
-
@processingMode == 'json-ld-1.1'
|
466
|
-
when nil
|
467
|
-
@processingMode
|
468
|
-
else
|
469
|
-
false
|
470
|
-
end
|
471
|
-
end
|
472
|
-
|
473
|
-
##
|
474
|
-
# Set processing mode.
|
475
|
-
#
|
476
|
-
# * With an argument, verifies that the processingMode is at least that provided, either as an integer, or a string of the form "json-ld-1.x"
|
477
|
-
#
|
478
|
-
# If contex has a @version member, it's value MUST be 1.1, otherwise an "invalid @version value" has been detected, and processing is aborted.
|
479
|
-
# If processingMode has been set, and it is not "json-ld-1.1", a "processing mode conflict" has been detecting, and processing is aborted.
|
480
|
-
#
|
481
|
-
# @param [String, Number] expected
|
482
|
-
# @return [String]
|
483
|
-
# @raise [JsonLdError::ProcessingModeConflict]
|
484
|
-
def processingMode=(value = nil, **options)
|
485
|
-
value = "json-ld-1.1" if value == 1.1
|
486
|
-
case value
|
487
|
-
when "json-ld-1.0", "json-ld-1.1"
|
488
|
-
if @processingMode && @processingMode != value
|
489
|
-
raise JsonLdError::ProcessingModeConflict, "#{value} not compatible with #{@processingMode}"
|
490
|
-
end
|
491
|
-
@processingMode = value
|
492
|
-
else
|
493
|
-
raise JsonLdError::InvalidVersionValue, value.inspect
|
494
|
-
end
|
495
|
-
end
|
496
|
-
|
497
|
-
# 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.
|
498
|
-
# @param [String] value must be an absolute IRI
|
499
|
-
def vocab=(value, **options)
|
500
|
-
@vocab = case value
|
501
|
-
when /_:/
|
502
|
-
# BNode vocab is deprecated
|
503
|
-
warn "[DEPRECATION] Blank Node vocabularies deprecated in JSON-LD 1.1." if @options[:validate] && processingMode("json-ld-1.1")
|
504
|
-
value
|
505
|
-
when String, RDF::URI
|
506
|
-
if (RDF::URI(value.to_s).relative? && processingMode("json-ld-1.0"))
|
507
|
-
raise JsonLdError::InvalidVocabMapping, "@vocab must be an absolute IRI in 1.0 mode: #{value.inspect}"
|
508
|
-
end
|
509
|
-
v = expand_iri(value.to_s, vocab: true, documentRelative: true)
|
510
|
-
raise JsonLdError::InvalidVocabMapping, "@vocab must be an IRI: #{value.inspect}" if !v.valid? && @options[:validate]
|
511
|
-
v
|
512
|
-
when nil
|
513
|
-
nil
|
514
|
-
else
|
515
|
-
raise JsonLdError::InvalidVocabMapping, "@vocab must be an IRI: #{value.inspect}"
|
516
|
-
end
|
517
|
-
end
|
518
|
-
|
519
|
-
# Set propagation
|
520
|
-
# @note: by the time this is called, the work has already been done.
|
521
|
-
#
|
522
|
-
# @param [Boolean] value
|
523
|
-
def propagate=(value, **options)
|
524
|
-
raise JsonLdError::InvalidContextEntry, "@propagate may only be set in 1.1 mode" if processingMode("json-ld-1.0")
|
525
|
-
raise JsonLdError::InvalidPropagateValue, "@propagate must be boolean valued: #{value.inspect}" unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
526
|
-
value
|
527
|
-
end
|
528
|
-
|
529
224
|
# Create an Evaluation Context
|
530
225
|
#
|
531
226
|
# When processing a JSON-LD data structure, each processing rule is applied using information provided by the active context. This section describes how to produce an active context.
|
@@ -536,10 +231,14 @@ module JSON::LD
|
|
536
231
|
#
|
537
232
|
#
|
538
233
|
# @param [String, #read, Array, Hash, Context] local_context
|
539
|
-
# @param [
|
234
|
+
# @param [String, #to_s] :base
|
235
|
+
# The Base IRI to use when expanding the document. This overrides the value of `input` if it is a _IRI_. If not specified and `input` is not an _IRI_, the base IRI defaults to the current document IRI if in a browser context, or the empty string if there is no document context.
|
540
236
|
# @param [Boolean] override_protected Protected terms may be cleared.
|
541
|
-
# @param [Boolean] propagate
|
237
|
+
# @param [Boolean] propagate (true)
|
542
238
|
# If false, retains any previously defined term, which can be rolled back when the descending into a new node object changes.
|
239
|
+
# @param [Array<String>] remote_contexts ([])
|
240
|
+
# @param [Boolean] validate (false)
|
241
|
+
# Extra validatation
|
543
242
|
# @param [Boolean] validate_scoped (true).
|
544
243
|
# Validate scoped context, loading if necessary.
|
545
244
|
# If false, do not load scoped contexts.
|
@@ -548,12 +247,12 @@ module JSON::LD
|
|
548
247
|
# @return [Context]
|
549
248
|
# @see https://www.w3.org/TR/json-ld11-api/index.html#context-processing-algorithm
|
550
249
|
def parse(local_context,
|
551
|
-
|
250
|
+
base: nil,
|
552
251
|
override_protected: false,
|
553
252
|
propagate: true,
|
253
|
+
remote_contexts: [],
|
554
254
|
validate_scoped: true)
|
555
255
|
result = self.dup
|
556
|
-
result.provided_context = local_context if self.empty?
|
557
256
|
# Early check for @propagate, which can only appear in a local context
|
558
257
|
propagate = local_context.is_a?(Hash) ? local_context.fetch('@propagate', propagate) : propagate
|
559
258
|
result.previous_context ||= result.dup unless propagate
|
@@ -562,7 +261,7 @@ module JSON::LD
|
|
562
261
|
|
563
262
|
local_context.each do |context|
|
564
263
|
case context
|
565
|
-
when nil
|
264
|
+
when nil,false
|
566
265
|
# 3.1 If the `override_protected` is false, and the active context contains protected terms, an error is raised.
|
567
266
|
if override_protected || result.term_definitions.values.none?(&:protected?)
|
568
267
|
null_context = Context.new(**options)
|
@@ -581,18 +280,17 @@ module JSON::LD
|
|
581
280
|
begin
|
582
281
|
ctx = JSON.load(context)
|
583
282
|
raise JSON::LD::JsonLdError::InvalidRemoteContext, "Context missing @context key" if @options[:validate] && ctx['@context'].nil?
|
584
|
-
result = result.
|
585
|
-
result.provided_context = ctx["@context"] if [context] == local_context
|
283
|
+
result = result.parse(ctx["@context"] ? ctx["@context"] : {})
|
586
284
|
rescue JSON::ParserError => e
|
587
285
|
#log_debug("parse") {"Failed to parse @context from remote document at #{context}: #{e.message}"}
|
588
286
|
raise JSON::LD::JsonLdError::InvalidRemoteContext, "Failed to parse remote context at #{context}: #{e.message}" if @options[:validate]
|
589
|
-
self
|
287
|
+
self
|
590
288
|
end
|
591
289
|
when String, RDF::URI
|
592
290
|
#log_debug("parse") {"remote: #{context}, base: #{result.context_base || result.base}"}
|
593
291
|
|
594
292
|
# 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].
|
595
|
-
context = RDF::URI(result.context_base ||
|
293
|
+
context = RDF::URI(result.context_base || base).join(context)
|
596
294
|
context_canon = context.canonicalize
|
597
295
|
context_canon.scheme = 'http' if context_canon.scheme == 'https'
|
598
296
|
|
@@ -625,10 +323,10 @@ module JSON::LD
|
|
625
323
|
raise JsonLdError::InvalidRemoteContext, "#{context}" unless remote_doc.document.is_a?(Hash) && remote_doc.document.has_key?('@context')
|
626
324
|
|
627
325
|
# Parse stand-alone
|
628
|
-
ctx = Context.new(**options)
|
326
|
+
ctx = Context.new(unfrozen: true, **options).dup
|
629
327
|
ctx.context_base = context.to_s
|
630
328
|
ctx = ctx.parse(remote_doc.document['@context'], remote_contexts: remote_contexts.dup)
|
631
|
-
ctx.base
|
329
|
+
ctx.instance_variable_set(:@base, nil)
|
632
330
|
ctx
|
633
331
|
end
|
634
332
|
rescue JsonLdError::LoadingDocumentFailed => e
|
@@ -647,7 +345,6 @@ module JSON::LD
|
|
647
345
|
|
648
346
|
context.previous_context = self unless propagate
|
649
347
|
result = context
|
650
|
-
#log_debug("parse") {"=> provided_context: #{context.inspect}"}
|
651
348
|
when Hash
|
652
349
|
context = context.dup # keep from modifying a hash passed as a param
|
653
350
|
|
@@ -666,7 +363,7 @@ module JSON::LD
|
|
666
363
|
# Retrieve remote context and merge the remaining context object into the result.
|
667
364
|
raise JsonLdError::InvalidContextEntry, "@import may only be used in 1.1 mode}" if result.processingMode("json-ld-1.0")
|
668
365
|
raise JsonLdError::InvalidImportValue, "@import must be a string: #{context['@import'].inspect}" unless context['@import'].is_a?(String)
|
669
|
-
|
366
|
+
import_loc = RDF::URI(result.context_base || base).join(context['@import'])
|
670
367
|
begin
|
671
368
|
context_opts = @options.merge(
|
672
369
|
profile: 'http://www.w3.org/ns/json-ld#context',
|
@@ -674,9 +371,9 @@ module JSON::LD
|
|
674
371
|
base: nil)
|
675
372
|
context_opts.delete(:headers)
|
676
373
|
# FIXME: should cache this, but ContextCache is for parsed contexts
|
677
|
-
JSON::LD::API.loadRemoteDocument(
|
678
|
-
# Dereference
|
679
|
-
raise JsonLdError::InvalidRemoteContext, "#{
|
374
|
+
JSON::LD::API.loadRemoteDocument(import_loc, **context_opts) do |remote_doc|
|
375
|
+
# Dereference import_loc. If the dereferenced document has no top-level JSON object with an @context member, an invalid remote context has been detected and processing is aborted; otherwise, set context to the value of that member.
|
376
|
+
raise JsonLdError::InvalidRemoteContext, "#{import_loc}" unless remote_doc.document.is_a?(Hash) && remote_doc.document.has_key?('@context')
|
680
377
|
import_context = remote_doc.document['@context']
|
681
378
|
import_context.delete('@base')
|
682
379
|
raise JsonLdError::InvalidRemoteContext, "#{import_context.to_json} must be an object" unless import_context.is_a?(Hash)
|
@@ -685,11 +382,11 @@ module JSON::LD
|
|
685
382
|
context = import_context.merge(context)
|
686
383
|
end
|
687
384
|
rescue JsonLdError::LoadingDocumentFailed => e
|
688
|
-
raise JsonLdError::LoadingRemoteContextFailed, "#{
|
385
|
+
raise JsonLdError::LoadingRemoteContextFailed, "#{import_loc}: #{e.message}", e.backtrace
|
689
386
|
rescue JsonLdError
|
690
387
|
raise
|
691
388
|
rescue StandardError => e
|
692
|
-
raise JsonLdError::LoadingRemoteContextFailed, "#{
|
389
|
+
raise JsonLdError::LoadingRemoteContextFailed, "#{import_loc}: #{e.message}", e.backtrace
|
693
390
|
end
|
694
391
|
else
|
695
392
|
result.send(setter, context[key], remote_contexts: remote_contexts)
|
@@ -703,6 +400,7 @@ module JSON::LD
|
|
703
400
|
context.each_key do |key|
|
704
401
|
# ... where key is not @base, @vocab, @language, or @version
|
705
402
|
result.create_term_definition(context, key, defined,
|
403
|
+
base: base,
|
706
404
|
override_protected: override_protected,
|
707
405
|
protected: context['@protected'],
|
708
406
|
remote_contexts: remote_contexts.dup,
|
@@ -726,13 +424,12 @@ module JSON::LD
|
|
726
424
|
# @param [Boolean]
|
727
425
|
# @return [Context]
|
728
426
|
def merge(context, override_protected: false)
|
729
|
-
ctx = Context.new(term_definitions: self.term_definitions
|
427
|
+
ctx = Context.new(term_definitions: self.term_definitions, standard_prefixes: options[:standard_prefixes])
|
730
428
|
ctx.context_base = context.context_base || self.context_base
|
731
429
|
ctx.default_language = context.default_language || self.default_language
|
732
430
|
ctx.default_direction = context.default_direction || self.default_direction
|
733
431
|
ctx.vocab = context.vocab || self.vocab
|
734
|
-
ctx.base =
|
735
|
-
ctx.provided_context = self.provided_context
|
432
|
+
ctx.base = self.base unless self.base.nil?
|
736
433
|
if !override_protected
|
737
434
|
ctx.term_definitions.each do |term, definition|
|
738
435
|
next unless definition.protected? && (other = context.term_definitions[term])
|
@@ -769,6 +466,7 @@ module JSON::LD
|
|
769
466
|
# @param [Hash] local_context
|
770
467
|
# @param [String] term
|
771
468
|
# @param [Hash] defined
|
469
|
+
# @param [String, RDF::URI] base for resolving document-relative IRIs
|
772
470
|
# @param [Boolean] protected if true, causes all terms to be marked protected
|
773
471
|
# @param [Boolean] override_protected Protected terms may be cleared.
|
774
472
|
# @param [Boolean] propagate
|
@@ -781,6 +479,7 @@ module JSON::LD
|
|
781
479
|
# Represents a cyclical term dependency
|
782
480
|
# @see https://www.w3.org/TR/json-ld11-api/index.html#create-term-definition
|
783
481
|
def create_term_definition(local_context, term, defined,
|
482
|
+
base: nil,
|
784
483
|
override_protected: false,
|
785
484
|
protected: nil,
|
786
485
|
remote_contexts: [],
|
@@ -1000,6 +699,7 @@ module JSON::LD
|
|
1000
699
|
if value.has_key?('@context')
|
1001
700
|
begin
|
1002
701
|
new_ctx = self.parse(value['@context'],
|
702
|
+
base: base,
|
1003
703
|
override_protected: true,
|
1004
704
|
remote_contexts: remote_contexts,
|
1005
705
|
validate_scoped: false)
|
@@ -1066,9 +766,130 @@ module JSON::LD
|
|
1066
766
|
|
1067
767
|
term_definitions[term] = definition
|
1068
768
|
defined[term] = true
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
769
|
+
end
|
770
|
+
|
771
|
+
##
|
772
|
+
# Initial context, without mappings, vocab or default language
|
773
|
+
#
|
774
|
+
# @return [Boolean]
|
775
|
+
def empty?
|
776
|
+
@term_definitions.empty? && self.vocab.nil? && self.default_language.nil?
|
777
|
+
end
|
778
|
+
|
779
|
+
# @param [String] value must be an absolute IRI
|
780
|
+
def base=(value, **options)
|
781
|
+
if value
|
782
|
+
raise JsonLdError::InvalidBaseIRI, "@base must be a string: #{value.inspect}" unless value.is_a?(String) || value.is_a?(RDF::URI)
|
783
|
+
value = RDF::URI(value)
|
784
|
+
value = @base.join(value) if @base && value.relative?
|
785
|
+
# still might be relative to document
|
786
|
+
@base = value
|
787
|
+
else
|
788
|
+
@base = false
|
789
|
+
end
|
790
|
+
|
791
|
+
end
|
792
|
+
|
793
|
+
# @param [String] value
|
794
|
+
def default_language=(value, **options)
|
795
|
+
@default_language = case value
|
796
|
+
when String
|
797
|
+
# Warn on an invalid language tag, unless :validate is true, in which case it's an error
|
798
|
+
if value !~ /^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/
|
799
|
+
warn "@language must be valid BCP47: #{value.inspect}"
|
800
|
+
end
|
801
|
+
options[:lowercaseLanguage] ? value.downcase : value
|
802
|
+
when nil
|
803
|
+
nil
|
804
|
+
else
|
805
|
+
raise JsonLdError::InvalidDefaultLanguage, "@language must be a string: #{value.inspect}"
|
806
|
+
end
|
807
|
+
end
|
808
|
+
|
809
|
+
# @param [String] value
|
810
|
+
def default_direction=(value, **options)
|
811
|
+
@default_direction = if value
|
812
|
+
raise JsonLdError::InvalidBaseDirection, "@direction must be one or 'ltr', or 'rtl': #{value.inspect}" unless %w(ltr rtl).include?(value)
|
813
|
+
value
|
814
|
+
else
|
815
|
+
nil
|
816
|
+
end
|
817
|
+
end
|
818
|
+
|
819
|
+
##
|
820
|
+
# Retrieve, or check processing mode.
|
821
|
+
#
|
822
|
+
# * With no arguments, retrieves the current set processingMode.
|
823
|
+
# * With an argument, verifies that the processingMode is at least that provided, either as an integer, or a string of the form "json-ld-1.x"
|
824
|
+
# * If expecting 1.1, and not set, it has the side-effect of setting mode to json-ld-1.1.
|
825
|
+
#
|
826
|
+
# @param [String, Number] expected (nil)
|
827
|
+
# @return [String]
|
828
|
+
def processingMode(expected = nil)
|
829
|
+
case expected
|
830
|
+
when 1.0, 'json-ld-1.0'
|
831
|
+
@processingMode == 'json-ld-1.0'
|
832
|
+
when 1.1, 'json-ld-1.1'
|
833
|
+
@processingMode.nil? || @processingMode == 'json-ld-1.1'
|
834
|
+
when nil
|
835
|
+
@processingMode || 'json-ld-1.1'
|
836
|
+
else
|
837
|
+
false
|
838
|
+
end
|
839
|
+
end
|
840
|
+
|
841
|
+
##
|
842
|
+
# Set processing mode.
|
843
|
+
#
|
844
|
+
# * With an argument, verifies that the processingMode is at least that provided, either as an integer, or a string of the form "json-ld-1.x"
|
845
|
+
#
|
846
|
+
# If contex has a @version member, it's value MUST be 1.1, otherwise an "invalid @version value" has been detected, and processing is aborted.
|
847
|
+
# If processingMode has been set, and it is not "json-ld-1.1", a "processing mode conflict" has been detecting, and processing is aborted.
|
848
|
+
#
|
849
|
+
# @param [String, Number] expected
|
850
|
+
# @return [String]
|
851
|
+
# @raise [JsonLdError::ProcessingModeConflict]
|
852
|
+
def processingMode=(value = nil, **options)
|
853
|
+
value = "json-ld-1.1" if value == 1.1
|
854
|
+
case value
|
855
|
+
when "json-ld-1.0", "json-ld-1.1"
|
856
|
+
if @processingMode && @processingMode != value
|
857
|
+
raise JsonLdError::ProcessingModeConflict, "#{value} not compatible with #{@processingMode}"
|
858
|
+
end
|
859
|
+
@processingMode = value
|
860
|
+
else
|
861
|
+
raise JsonLdError::InvalidVersionValue, value.inspect
|
862
|
+
end
|
863
|
+
end
|
864
|
+
|
865
|
+
# 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.
|
866
|
+
# @param [String] value must be an absolute IRI
|
867
|
+
def vocab=(value, **options)
|
868
|
+
@vocab = case value
|
869
|
+
when /_:/
|
870
|
+
# BNode vocab is deprecated
|
871
|
+
warn "[DEPRECATION] Blank Node vocabularies deprecated in JSON-LD 1.1." if @options[:validate] && processingMode("json-ld-1.1")
|
872
|
+
value
|
873
|
+
when String, RDF::URI
|
874
|
+
if (RDF::URI(value.to_s).relative? && processingMode("json-ld-1.0"))
|
875
|
+
raise JsonLdError::InvalidVocabMapping, "@vocab must be an absolute IRI in 1.0 mode: #{value.inspect}"
|
876
|
+
end
|
877
|
+
expand_iri(value.to_s, vocab: true, documentRelative: true)
|
878
|
+
when nil
|
879
|
+
nil
|
880
|
+
else
|
881
|
+
raise JsonLdError::InvalidVocabMapping, "@vocab must be an IRI: #{value.inspect}"
|
882
|
+
end
|
883
|
+
end
|
884
|
+
|
885
|
+
# Set propagation
|
886
|
+
# @note: by the time this is called, the work has already been done.
|
887
|
+
#
|
888
|
+
# @param [Boolean] value
|
889
|
+
def propagate=(value, **options)
|
890
|
+
raise JsonLdError::InvalidContextEntry, "@propagate may only be set in 1.1 mode" if processingMode("json-ld-1.0")
|
891
|
+
raise JsonLdError::InvalidPropagateValue, "@propagate must be boolean valued: #{value.inspect}" unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
892
|
+
value
|
1072
893
|
end
|
1073
894
|
|
1074
895
|
##
|
@@ -1077,40 +898,44 @@ module JSON::LD
|
|
1077
898
|
# If a context was supplied in global options, use that, otherwise, generate one
|
1078
899
|
# from this representation.
|
1079
900
|
#
|
901
|
+
# @param [Array, Hash, Context, IO, StringIO] provided_context (nil)
|
902
|
+
# Original context to use, if available
|
1080
903
|
# @param [Hash{Symbol => Object}] options ({})
|
1081
904
|
# @return [Hash]
|
1082
|
-
def serialize(**options)
|
1083
|
-
#
|
905
|
+
def serialize(provided_context: nil, **options)
|
906
|
+
#log_debug("serlialize: generate context")
|
907
|
+
#log_debug("") {"=> context: #{inspect}"}
|
1084
908
|
use_context = case provided_context
|
1085
909
|
when String, RDF::URI
|
1086
910
|
#log_debug "serlialize: reuse context: #{provided_context.inspect}"
|
1087
911
|
provided_context.to_s
|
1088
|
-
when Hash
|
912
|
+
when Hash
|
913
|
+
#log_debug "serlialize: reuse context: #{provided_context.inspect}"
|
914
|
+
# If it has an @context entry use it, otherwise it is assumed to be the body of a context
|
915
|
+
provided_context.fetch('@context', provided_context)
|
916
|
+
when Array
|
1089
917
|
#log_debug "serlialize: reuse context: #{provided_context.inspect}"
|
1090
918
|
provided_context
|
919
|
+
when IO, StringIO
|
920
|
+
provided_context.rewind
|
921
|
+
JSON.load(provided_context).fetch('@context', {})
|
1091
922
|
else
|
1092
|
-
#log_debug("serlialize: generate context")
|
1093
|
-
#log_debug("") {"=> context: #{inspect}"}
|
1094
923
|
ctx = {}
|
1095
|
-
ctx['@
|
924
|
+
ctx['@version'] = 1.1 if @processingMode == 'json-ld-1.1'
|
925
|
+
ctx['@base'] = base.to_s if base
|
1096
926
|
ctx['@direction'] = default_direction.to_s if default_direction
|
1097
927
|
ctx['@language'] = default_language.to_s if default_language
|
1098
928
|
ctx['@vocab'] = vocab.to_s if vocab
|
1099
929
|
|
1100
930
|
# Term Definitions
|
1101
|
-
term_definitions.
|
1102
|
-
|
1103
|
-
ctx[term] = defn if defn
|
931
|
+
term_definitions.each do |term, defn|
|
932
|
+
ctx[term] = defn.to_context_definition(self)
|
1104
933
|
end
|
1105
|
-
|
1106
|
-
#log_debug("") {"start_doc: context=#{ctx.inspect}"}
|
1107
934
|
ctx
|
1108
935
|
end
|
1109
936
|
|
1110
937
|
# Return hash with @context, or empty
|
1111
|
-
|
1112
|
-
r['@context'] = use_context unless use_context.nil? || use_context.empty?
|
1113
|
-
r
|
938
|
+
use_context.nil? || use_context.empty? ? {} : {'@context' => use_context}
|
1114
939
|
end
|
1115
940
|
|
1116
941
|
##
|
@@ -1338,26 +1163,27 @@ module JSON::LD
|
|
1338
1163
|
#
|
1339
1164
|
# @param [String] value
|
1340
1165
|
# A keyword, term, prefix:suffix or possibly relative IRI
|
1166
|
+
# @param [Boolean] as_string (false) transform RDF::Resource values to string
|
1167
|
+
# @param [String, RDF::URI] base for resolving document-relative IRIs
|
1168
|
+
# @param [Hash] defined
|
1169
|
+
# Used during Context Processing.
|
1341
1170
|
# @param [Boolean] documentRelative (false)
|
1342
|
-
# @param [Boolean] vocab (false)
|
1343
1171
|
# @param [Hash] local_context
|
1344
1172
|
# Used during Context Processing.
|
1345
|
-
# @param [
|
1346
|
-
# Used during Context Processing.
|
1347
|
-
# @param [Boolean] as_string (false) transform RDF::Resource values to string
|
1173
|
+
# @param [Boolean] vocab (false)
|
1348
1174
|
# @param [Hash{Symbol => Object}] options
|
1349
1175
|
# @return [RDF::Resource, String]
|
1350
1176
|
# IRI or String, if it's a keyword
|
1351
1177
|
# @raise [JSON::LD::JsonLdError::InvalidIRIMapping] if the value cannot be expanded
|
1352
1178
|
# @see https://www.w3.org/TR/json-ld11-api/#iri-expansion
|
1353
1179
|
def expand_iri(value,
|
1180
|
+
as_string: false,
|
1181
|
+
base: nil,
|
1182
|
+
defined: nil,
|
1354
1183
|
documentRelative: false,
|
1355
|
-
vocab: false,
|
1356
1184
|
local_context: nil,
|
1357
|
-
|
1358
|
-
|
1359
|
-
**options
|
1360
|
-
)
|
1185
|
+
vocab: false,
|
1186
|
+
**options)
|
1361
1187
|
return (value && as_string ? value.to_s : value) unless value.is_a?(String)
|
1362
1188
|
|
1363
1189
|
return value if KEYWORDS.include?(value)
|
@@ -1377,7 +1203,8 @@ module JSON::LD
|
|
1377
1203
|
# If active context has a term definition for value, and the associated mapping is a keyword, return that keyword.
|
1378
1204
|
# If vocab is true and the active context has a term definition for value, return the associated IRI mapping.
|
1379
1205
|
if (v_td = term_definitions[value]) && (vocab || KEYWORDS.include?(v_td.id))
|
1380
|
-
|
1206
|
+
iri = base && v_td.id ? base.join(v_td.id) : v_td.id # vocab might be doc relative
|
1207
|
+
return (as_string ? iri.to_s : iri)
|
1381
1208
|
end
|
1382
1209
|
|
1383
1210
|
# If value contains a colon (:), it is either an absolute IRI or a compact IRI:
|
@@ -1410,18 +1237,32 @@ module JSON::LD
|
|
1410
1237
|
end
|
1411
1238
|
end
|
1412
1239
|
|
1240
|
+
iri = value.is_a?(RDF::URI) ? value : RDF::URI(value)
|
1413
1241
|
result = if vocab && self.vocab
|
1414
1242
|
# If vocab is true, and active context has a vocabulary mapping, return the result of concatenating the vocabulary mapping with value.
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1243
|
+
# Note that @vocab could still be relative to a document base
|
1244
|
+
(base && self.vocab.is_a?(RDF::URI) && self.vocab.relative? ? base.join(self.vocab) : self.vocab) + value
|
1245
|
+
elsif documentRelative
|
1246
|
+
if iri.absolute?
|
1247
|
+
iri
|
1248
|
+
elsif self.base.is_a?(RDF::URI) && self.base.absolute?
|
1249
|
+
self.base.join(iri)
|
1250
|
+
elsif self.base == false
|
1251
|
+
# No resollution of `@base: null`
|
1252
|
+
iri
|
1253
|
+
elsif base && self.base
|
1254
|
+
base.join(self.base).join(iri)
|
1255
|
+
elsif base
|
1256
|
+
base.join(iri)
|
1257
|
+
else
|
1258
|
+
# Returns a relative IRI in an odd case.
|
1259
|
+
iri
|
1260
|
+
end
|
1261
|
+
elsif local_context && iri.relative?
|
1421
1262
|
# 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.
|
1422
1263
|
raise JSON::LD::JsonLdError::InvalidIRIMapping, "not an absolute IRI: #{value}"
|
1423
1264
|
else
|
1424
|
-
|
1265
|
+
iri
|
1425
1266
|
end
|
1426
1267
|
result && as_string ? result.to_s : result
|
1427
1268
|
end
|
@@ -1442,17 +1283,17 @@ module JSON::LD
|
|
1442
1283
|
# Compacts an absolute IRI to the shortest matching term or compact IRI
|
1443
1284
|
#
|
1444
1285
|
# @param [RDF::URI] iri
|
1286
|
+
# @param [String, RDF::URI] base for resolving document-relative IRIs
|
1445
1287
|
# @param [Object] value
|
1446
1288
|
# Value, used to select among various maps for the same IRI
|
1447
|
-
# @param [Boolean] vocab
|
1448
|
-
# specifies whether the passed iri should be compacted using the active context's vocabulary mapping
|
1449
1289
|
# @param [Boolean] reverse
|
1450
1290
|
# specifies whether a reverse property is being compacted
|
1451
|
-
# @param
|
1291
|
+
# @param [Boolean] vocab
|
1292
|
+
# specifies whether the passed iri should be compacted using the active context's vocabulary mapping
|
1452
1293
|
#
|
1453
1294
|
# @return [String] compacted form of IRI
|
1454
1295
|
# @see https://www.w3.org/TR/json-ld11-api/#iri-compaction
|
1455
|
-
def compact_iri(iri,
|
1296
|
+
def compact_iri(iri, base: nil, reverse: false, value: nil, vocab: nil)
|
1456
1297
|
return if iri.nil?
|
1457
1298
|
iri = iri.to_s
|
1458
1299
|
|
@@ -1557,7 +1398,7 @@ module JSON::LD
|
|
1557
1398
|
preferred_values = []
|
1558
1399
|
preferred_values << '@reverse' if tl_value == '@reverse'
|
1559
1400
|
if (tl_value == '@id' || tl_value == '@reverse') && value.is_a?(Hash) && value.has_key?('@id')
|
1560
|
-
t_iri = compact_iri(value['@id'], vocab: true,
|
1401
|
+
t_iri = compact_iri(value['@id'], vocab: true, base: base)
|
1561
1402
|
if (r_td = term_definitions[t_iri]) && r_td.id == value['@id']
|
1562
1403
|
preferred_values.concat(CONTAINERS_VOCAB_ID)
|
1563
1404
|
else
|
@@ -1623,7 +1464,7 @@ module JSON::LD
|
|
1623
1464
|
|
1624
1465
|
if !vocab
|
1625
1466
|
# transform iri to a relative IRI using the document's base IRI
|
1626
|
-
iri = remove_base(iri)
|
1467
|
+
iri = remove_base(self.base || base, iri)
|
1627
1468
|
return iri
|
1628
1469
|
else
|
1629
1470
|
return iri
|
@@ -1643,26 +1484,24 @@ module JSON::LD
|
|
1643
1484
|
# Value (literal or IRI) to be expanded
|
1644
1485
|
# @param [Boolean] useNativeTypes (false) use native representations
|
1645
1486
|
# @param [Boolean] rdfDirection (nil) decode i18n datatype if i18n-datatype
|
1487
|
+
# @param [String, RDF::URI] base for resolving document-relative IRIs
|
1646
1488
|
# @param [Hash{Symbol => Object}] options
|
1647
1489
|
#
|
1648
1490
|
# @return [Hash] Object representation of value
|
1649
1491
|
# @raise [RDF::ReaderError] if the iri cannot be expanded
|
1650
1492
|
# @see https://www.w3.org/TR/json-ld11-api/#value-expansion
|
1651
|
-
def expand_value(property, value, useNativeTypes: false, rdfDirection: nil, **options)
|
1652
|
-
#log_debug("expand_value") {"property: #{property.inspect}, value: #{value.inspect}"}
|
1653
|
-
|
1493
|
+
def expand_value(property, value, useNativeTypes: false, rdfDirection: nil, base: nil, **options)
|
1654
1494
|
td = term_definitions.fetch(property, TermDefinition.new(property))
|
1655
1495
|
|
1656
1496
|
# 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.
|
1657
1497
|
if value.is_a?(String) && td.type_mapping == '@id'
|
1658
1498
|
#log_debug("") {"as relative IRI: #{value.inspect}"}
|
1659
|
-
return {'@id' => expand_iri(value, documentRelative: true).to_s}
|
1499
|
+
return {'@id' => expand_iri(value, documentRelative: true, base: base).to_s}
|
1660
1500
|
end
|
1661
1501
|
|
1662
1502
|
# 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.
|
1663
1503
|
if value.is_a?(String) && td.type_mapping == '@vocab'
|
1664
|
-
|
1665
|
-
return {'@id' => expand_iri(value, vocab: true, documentRelative: true).to_s}
|
1504
|
+
return {'@id' => expand_iri(value, vocab: true, documentRelative: true, base: base).to_s}
|
1666
1505
|
end
|
1667
1506
|
|
1668
1507
|
value = RDF::Literal(value) if
|
@@ -1672,16 +1511,14 @@ module JSON::LD
|
|
1672
1511
|
|
1673
1512
|
result = case value
|
1674
1513
|
when RDF::URI, RDF::Node
|
1675
|
-
#log_debug("URI | BNode") { value.to_s }
|
1676
1514
|
{'@id' => value.to_s}
|
1677
1515
|
when RDF::Literal
|
1678
|
-
#log_debug("Literal") {"datatype: #{value.datatype.inspect}"}
|
1679
1516
|
res = {}
|
1680
1517
|
if value.datatype == RDF::URI(RDF.to_uri + "JSON") && processingMode('json-ld-1.1')
|
1681
1518
|
# Value parsed as JSON
|
1682
1519
|
# FIXME: MultiJson
|
1683
|
-
res['@value'] = ::JSON.parse(value.object)
|
1684
1520
|
res['@type'] = '@json'
|
1521
|
+
res['@value'] = ::JSON.parse(value.object)
|
1685
1522
|
elsif value.datatype.start_with?("https://www.w3.org/ns/i18n#") && rdfDirection == 'i18n-datatype' && processingMode('json-ld-1.1')
|
1686
1523
|
lang, dir = value.datatype.fragment.split('_')
|
1687
1524
|
res['@value'] = value.to_s
|
@@ -1697,24 +1534,23 @@ module JSON::LD
|
|
1697
1534
|
end
|
1698
1535
|
res['@direction'] = dir
|
1699
1536
|
elsif useNativeTypes && RDF_LITERAL_NATIVE_TYPES.include?(value.datatype)
|
1700
|
-
res['@value'] = value.object
|
1701
1537
|
res['@type'] = uri(coerce(property)) if coerce(property)
|
1538
|
+
res['@value'] = value.object
|
1702
1539
|
else
|
1703
1540
|
value.canonicalize! if value.datatype == RDF::XSD.double
|
1704
|
-
res['@value'] = value.to_s
|
1705
1541
|
if coerce(property)
|
1706
1542
|
res['@type'] = uri(coerce(property)).to_s
|
1707
1543
|
elsif value.has_datatype?
|
1708
1544
|
res['@type'] = uri(value.datatype).to_s
|
1709
1545
|
elsif value.has_language? || language(property)
|
1710
1546
|
res['@language'] = (value.language || language(property)).to_s
|
1711
|
-
# FIXME: direction
|
1712
1547
|
end
|
1548
|
+
res['@value'] = value.to_s
|
1713
1549
|
end
|
1714
1550
|
res
|
1715
1551
|
else
|
1716
1552
|
# Otherwise, initialize result to a JSON object with an @value member whose value is set to value.
|
1717
|
-
res = {
|
1553
|
+
res = {}
|
1718
1554
|
|
1719
1555
|
if td.type_mapping && !CONTAINERS_ID_VOCAB.include?(td.type_mapping.to_s)
|
1720
1556
|
res['@type'] = td.type_mapping.to_s
|
@@ -1725,10 +1561,9 @@ module JSON::LD
|
|
1725
1561
|
res['@direction'] = direction if direction
|
1726
1562
|
end
|
1727
1563
|
|
1728
|
-
res
|
1564
|
+
res.merge('@value' => value)
|
1729
1565
|
end
|
1730
1566
|
|
1731
|
-
#log_debug("") {"=> #{result.inspect}"}
|
1732
1567
|
result
|
1733
1568
|
rescue ::JSON::ParserError => e
|
1734
1569
|
raise JSON::LD::JsonLdError::InvalidJsonLiteral, e.message
|
@@ -1741,13 +1576,13 @@ module JSON::LD
|
|
1741
1576
|
# Associated property used to find coercion rules
|
1742
1577
|
# @param [Hash] value
|
1743
1578
|
# Value (literal or IRI), in full object representation, to be compacted
|
1744
|
-
# @param
|
1579
|
+
# @param [String, RDF::URI] base for resolving document-relative IRIs
|
1745
1580
|
#
|
1746
1581
|
# @return [Hash] Object representation of value
|
1747
1582
|
# @raise [JsonLdError] if the iri cannot be expanded
|
1748
1583
|
# @see https://www.w3.org/TR/json-ld11-api/#value-compaction
|
1749
1584
|
# FIXME: revisit the specification version of this.
|
1750
|
-
def compact_value(property, value,
|
1585
|
+
def compact_value(property, value, base: nil)
|
1751
1586
|
#log_debug("compact_value") {"property: #{property.inspect}, value: #{value.inspect}"}
|
1752
1587
|
|
1753
1588
|
indexing = index?(value) && container(property).include?('@index')
|
@@ -1758,7 +1593,7 @@ module JSON::LD
|
|
1758
1593
|
when coerce(property) == '@id' && value.has_key?('@id') && (value.keys - %w(@id @index)).empty?
|
1759
1594
|
# Compact an @id coercion
|
1760
1595
|
#log_debug("") {" (@id & coerce)"}
|
1761
|
-
compact_iri(value['@id'])
|
1596
|
+
compact_iri(value['@id'], base: base)
|
1762
1597
|
when coerce(property) == '@vocab' && value.has_key?('@id') && (value.keys - %w(@id @index)).empty?
|
1763
1598
|
# Compact an @id coercion
|
1764
1599
|
#log_debug("") {" (@id & coerce & vocab)"}
|
@@ -1848,16 +1683,21 @@ module JSON::LD
|
|
1848
1683
|
v.join(" ") + "]"
|
1849
1684
|
end
|
1850
1685
|
|
1686
|
+
# Duplicate an active context, allowing it to be modified.
|
1851
1687
|
def dup
|
1852
|
-
# Also duplicate mappings, coerce and list
|
1853
1688
|
that = self
|
1854
|
-
ec =
|
1689
|
+
ec = Context.new(unfrozen: true, **@options)
|
1690
|
+
ec.context_base = that.context_base
|
1691
|
+
ec.base = that.base unless that.base.nil?
|
1692
|
+
ec.default_direction = that.default_direction
|
1693
|
+
ec.default_language = that.default_language
|
1694
|
+
ec.previous_context = that.previous_context
|
1695
|
+
ec.processingMode = that.processingMode if that.instance_variable_get(:@processingModee)
|
1696
|
+
ec.vocab = that.vocab if that.vocab
|
1697
|
+
|
1855
1698
|
ec.instance_eval do
|
1856
|
-
@term_definitions = that.term_definitions.
|
1857
|
-
|
1858
|
-
end
|
1859
|
-
@iri_to_term = that.iri_to_term.dup
|
1860
|
-
@inverse_context = nil
|
1699
|
+
@term_definitions = that.term_definitions.dup
|
1700
|
+
@iri_to_term = that.iri_to_term
|
1861
1701
|
end
|
1862
1702
|
ec
|
1863
1703
|
end
|
@@ -1903,20 +1743,11 @@ module JSON::LD
|
|
1903
1743
|
bnode(namer.get_sym($1))
|
1904
1744
|
else
|
1905
1745
|
value = RDF::URI(value)
|
1906
|
-
value.validate! if
|
1907
|
-
value.canonicalize! if @options[:canonicalize]
|
1908
|
-
value = RDF::URI.intern(value, {}) if @options[:intern]
|
1746
|
+
#value.validate! if options[:validate]
|
1909
1747
|
value
|
1910
1748
|
end
|
1911
1749
|
end
|
1912
1750
|
|
1913
|
-
# Clear the provided context, used for testing
|
1914
|
-
# @return [Context] self
|
1915
|
-
def clear_provided_context
|
1916
|
-
@provided_context = nil
|
1917
|
-
self
|
1918
|
-
end
|
1919
|
-
|
1920
1751
|
# Keep track of allocated BNodes
|
1921
1752
|
#
|
1922
1753
|
# Don't actually use the name provided, to prevent name alias issues.
|
@@ -1957,7 +1788,7 @@ module JSON::LD
|
|
1957
1788
|
# @return [Hash{String => Hash{String => String}}]
|
1958
1789
|
# @todo May want to include @set along with container to allow selecting terms using @set over those without @set. May require adding some notion of value cardinality to compact_iri
|
1959
1790
|
def inverse_context
|
1960
|
-
|
1791
|
+
Context.inverse_cache[self.hash] ||= begin
|
1961
1792
|
result = {}
|
1962
1793
|
default_language = (self.default_language || '@none').downcase
|
1963
1794
|
term_definitions.keys.sort do |a, b|
|
@@ -2052,10 +1883,11 @@ module JSON::LD
|
|
2052
1883
|
##
|
2053
1884
|
# Removes a base IRI from the given absolute IRI.
|
2054
1885
|
#
|
1886
|
+
# @param [String] base the base used for making `iri` relative
|
2055
1887
|
# @param [String] iri the absolute IRI
|
2056
1888
|
# @return [String]
|
2057
1889
|
# the relative IRI if relative to base, otherwise the absolute IRI.
|
2058
|
-
def remove_base(iri)
|
1890
|
+
def remove_base(base, iri)
|
2059
1891
|
return iri unless base
|
2060
1892
|
@base_and_parents ||= begin
|
2061
1893
|
u = base
|
@@ -2165,5 +1997,232 @@ module JSON::LD
|
|
2165
1997
|
end
|
2166
1998
|
Array(container)
|
2167
1999
|
end
|
2000
|
+
|
2001
|
+
# Term Definitions specify how properties and values have to be interpreted as well as the current vocabulary mapping and the default language
|
2002
|
+
class TermDefinition
|
2003
|
+
# @return [RDF::URI] IRI map
|
2004
|
+
attr_accessor :id
|
2005
|
+
|
2006
|
+
# @return [String] term name
|
2007
|
+
attr_accessor :term
|
2008
|
+
|
2009
|
+
# @return [String] Type mapping
|
2010
|
+
attr_accessor :type_mapping
|
2011
|
+
|
2012
|
+
# Base container mapping, without @set
|
2013
|
+
# @return [Array<'@index', '@language', '@index', '@set', '@type', '@id', '@graph'>] Container mapping
|
2014
|
+
attr_reader :container_mapping
|
2015
|
+
|
2016
|
+
# @return [String] Term used for nest properties
|
2017
|
+
attr_accessor :nest
|
2018
|
+
|
2019
|
+
# Language mapping of term, `false` is used if there is an explicit language mapping for this term.
|
2020
|
+
# @return [String] Language mapping
|
2021
|
+
attr_accessor :language_mapping
|
2022
|
+
|
2023
|
+
# Direction of term, `false` is used if there is explicit direction mapping mapping for this term.
|
2024
|
+
# @return ["ltr", "rtl"] direction_mapping
|
2025
|
+
attr_accessor :direction_mapping
|
2026
|
+
|
2027
|
+
# @return [Boolean] Reverse Property
|
2028
|
+
attr_accessor :reverse_property
|
2029
|
+
|
2030
|
+
# This is a simple term definition, not an expanded term definition
|
2031
|
+
# @return [Boolean]
|
2032
|
+
attr_accessor :simple
|
2033
|
+
|
2034
|
+
# Property used for data indexing; defaults to @index
|
2035
|
+
# @return [Boolean]
|
2036
|
+
attr_accessor :index
|
2037
|
+
|
2038
|
+
# Indicate that term may be used as a prefix
|
2039
|
+
attr_writer :prefix
|
2040
|
+
|
2041
|
+
# Term-specific context
|
2042
|
+
# @return [Hash{String => Object}]
|
2043
|
+
attr_accessor :context
|
2044
|
+
|
2045
|
+
# Term is protected.
|
2046
|
+
# @return [Boolean]
|
2047
|
+
attr_writer :protected
|
2048
|
+
|
2049
|
+
# This is a simple term definition, not an expanded term definition
|
2050
|
+
# @return [Boolean] simple
|
2051
|
+
def simple?; simple; end
|
2052
|
+
|
2053
|
+
# This is an appropriate term to use as the prefix of a compact IRI
|
2054
|
+
# @return [Boolean] simple
|
2055
|
+
def prefix?; @prefix; end
|
2056
|
+
|
2057
|
+
# Create a new Term Mapping with an ID
|
2058
|
+
# @param [String] term
|
2059
|
+
# @param [String] id
|
2060
|
+
# @param [String] type_mapping Type mapping
|
2061
|
+
# @param [Set<'@index', '@language', '@index', '@set', '@type', '@id', '@graph'>] container_mapping
|
2062
|
+
# @param [String] language_mapping
|
2063
|
+
# Language mapping of term, `false` is used if there is an explicit language mapping for this term
|
2064
|
+
# @param ["ltr", "rtl"] direction_mapping
|
2065
|
+
# Direction mapping of term, `false` is used if there is an explicit direction mapping for this term
|
2066
|
+
# @param [Boolean] reverse_property
|
2067
|
+
# @param [Boolean] protected
|
2068
|
+
# @param [String] nest term used for nest properties
|
2069
|
+
# @param [Boolean] simple
|
2070
|
+
# This is a simple term definition, not an expanded term definition
|
2071
|
+
# @param [Boolean] prefix
|
2072
|
+
# Term may be used as a prefix
|
2073
|
+
def initialize(term,
|
2074
|
+
id: nil,
|
2075
|
+
index: nil,
|
2076
|
+
type_mapping: nil,
|
2077
|
+
container_mapping: nil,
|
2078
|
+
language_mapping: nil,
|
2079
|
+
direction_mapping: nil,
|
2080
|
+
reverse_property: false,
|
2081
|
+
nest: nil,
|
2082
|
+
protected: nil,
|
2083
|
+
simple: false,
|
2084
|
+
prefix: nil,
|
2085
|
+
context: nil)
|
2086
|
+
@term = term
|
2087
|
+
@id = id.to_s unless id.nil?
|
2088
|
+
@index = index.to_s unless index.nil?
|
2089
|
+
@type_mapping = type_mapping.to_s unless type_mapping.nil?
|
2090
|
+
self.container_mapping = container_mapping
|
2091
|
+
@language_mapping = language_mapping unless language_mapping.nil?
|
2092
|
+
@direction_mapping = direction_mapping unless direction_mapping.nil?
|
2093
|
+
@reverse_property = reverse_property
|
2094
|
+
@protected = protected
|
2095
|
+
@nest = nest unless nest.nil?
|
2096
|
+
@simple = simple
|
2097
|
+
@prefix = prefix unless prefix.nil?
|
2098
|
+
@context = context unless context.nil?
|
2099
|
+
end
|
2100
|
+
|
2101
|
+
# Term is protected.
|
2102
|
+
# @return [Boolean]
|
2103
|
+
def protected?; !!@protected; end
|
2104
|
+
|
2105
|
+
# Set container mapping, from an array which may include @set
|
2106
|
+
def container_mapping=(mapping)
|
2107
|
+
mapping = case mapping
|
2108
|
+
when Set then mapping
|
2109
|
+
when Array then Set.new(mapping)
|
2110
|
+
when String then Set[mapping]
|
2111
|
+
when nil then Set.new
|
2112
|
+
else
|
2113
|
+
raise "Shouldn't happen with #{mapping.inspect}"
|
2114
|
+
end
|
2115
|
+
if @as_set = mapping.include?('@set')
|
2116
|
+
mapping = mapping.dup
|
2117
|
+
mapping.delete('@set')
|
2118
|
+
end
|
2119
|
+
@container_mapping = mapping
|
2120
|
+
@index ||= '@index' if mapping.include?('@index')
|
2121
|
+
end
|
2122
|
+
|
2123
|
+
##
|
2124
|
+
# Output Hash or String definition for this definition considering @language and @vocab
|
2125
|
+
#
|
2126
|
+
# @param [Context] context
|
2127
|
+
# @return [String, Hash{String => Array[String], String}]
|
2128
|
+
def to_context_definition(context)
|
2129
|
+
cid = if context.vocab && id.start_with?(context.vocab)
|
2130
|
+
# Nothing to return unless it's the same as the vocab
|
2131
|
+
id == context.vocab ? context.vocab : id.to_s[context.vocab.length..-1]
|
2132
|
+
else
|
2133
|
+
# Find a term to act as a prefix
|
2134
|
+
iri, prefix = context.iri_to_term.detect {|i,p| id.to_s.start_with?(i.to_s)}
|
2135
|
+
iri && iri != id ? "#{prefix}:#{id.to_s[iri.length..-1]}" : id
|
2136
|
+
end
|
2137
|
+
|
2138
|
+
if simple?
|
2139
|
+
cid.to_s unless cid == term && context.vocab
|
2140
|
+
else
|
2141
|
+
defn = {}
|
2142
|
+
defn[reverse_property ? '@reverse' : '@id'] = cid.to_s unless cid == term && !reverse_property
|
2143
|
+
if type_mapping
|
2144
|
+
defn['@type'] = if KEYWORDS.include?(type_mapping)
|
2145
|
+
type_mapping
|
2146
|
+
else
|
2147
|
+
context.compact_iri(type_mapping, vocab: true)
|
2148
|
+
end
|
2149
|
+
end
|
2150
|
+
|
2151
|
+
cm = Array(container_mapping)
|
2152
|
+
cm << "@set" if as_set? && !cm.include?("@set")
|
2153
|
+
cm = cm.first if cm.length == 1
|
2154
|
+
defn['@container'] = cm unless cm.empty?
|
2155
|
+
# Language set as false to be output as null
|
2156
|
+
defn['@language'] = (@language_mapping ? @language_mapping : nil) unless @language_mapping.nil?
|
2157
|
+
defn['@direction'] = (@direction_mapping ? @direction_mapping : nil) unless @direction_mapping.nil?
|
2158
|
+
defn['@context'] = @context if @context
|
2159
|
+
defn['@nest'] = @nest if @nest
|
2160
|
+
defn['@index'] = @index if @index
|
2161
|
+
defn['@prefix'] = @prefix unless @prefix.nil?
|
2162
|
+
defn
|
2163
|
+
end
|
2164
|
+
end
|
2165
|
+
|
2166
|
+
##
|
2167
|
+
# Turn this into a source for a new instantiation
|
2168
|
+
# FIXME: context serialization
|
2169
|
+
# @return [String]
|
2170
|
+
def to_rb
|
2171
|
+
defn = [%(TermDefinition.new\(#{term.inspect})]
|
2172
|
+
%w(id index type_mapping container_mapping language_mapping direction_mapping reverse_property nest simple prefix context protected).each do |acc|
|
2173
|
+
v = instance_variable_get("@#{acc}".to_sym)
|
2174
|
+
v = v.to_s if v.is_a?(RDF::Term)
|
2175
|
+
if acc == 'container_mapping'
|
2176
|
+
v = v.to_a
|
2177
|
+
v << '@set' if as_set?
|
2178
|
+
v = v.first if v.length <= 1
|
2179
|
+
end
|
2180
|
+
defn << "#{acc}: #{v.inspect}" if v
|
2181
|
+
end
|
2182
|
+
defn.join(', ') + ")"
|
2183
|
+
end
|
2184
|
+
|
2185
|
+
# If container mapping was defined along with @set
|
2186
|
+
# @return [Boolean]
|
2187
|
+
def as_set?; @as_set || false; end
|
2188
|
+
|
2189
|
+
# Check if term definitions are identical, modulo @protected
|
2190
|
+
# @return [Boolean]
|
2191
|
+
def ==(other)
|
2192
|
+
other.is_a?(TermDefinition) &&
|
2193
|
+
id == other.id &&
|
2194
|
+
term == other.term &&
|
2195
|
+
type_mapping == other.type_mapping &&
|
2196
|
+
container_mapping == other.container_mapping &&
|
2197
|
+
nest == other.nest &&
|
2198
|
+
language_mapping == other.language_mapping &&
|
2199
|
+
direction_mapping == other.direction_mapping &&
|
2200
|
+
reverse_property == other.reverse_property &&
|
2201
|
+
simple == other.simple &&
|
2202
|
+
index == other.index &&
|
2203
|
+
context == other.context &&
|
2204
|
+
prefix? == other.prefix? &&
|
2205
|
+
as_set? == other.as_set?
|
2206
|
+
end
|
2207
|
+
|
2208
|
+
def inspect
|
2209
|
+
v = %w([TD)
|
2210
|
+
v << "id=#{@id}"
|
2211
|
+
v << "index=#{index.inspect}" unless index.nil?
|
2212
|
+
v << "term=#{@term}"
|
2213
|
+
v << "rev" if reverse_property
|
2214
|
+
v << "container=#{container_mapping}" if container_mapping
|
2215
|
+
v << "as_set=#{as_set?.inspect}"
|
2216
|
+
v << "lang=#{language_mapping.inspect}" unless language_mapping.nil?
|
2217
|
+
v << "dir=#{direction_mapping.inspect}" unless direction_mapping.nil?
|
2218
|
+
v << "type=#{type_mapping}" unless type_mapping.nil?
|
2219
|
+
v << "nest=#{nest.inspect}" unless nest.nil?
|
2220
|
+
v << "simple=true" if @simple
|
2221
|
+
v << "protected=true" if @protected
|
2222
|
+
v << "prefix=#{@prefix.inspect}" unless @prefix.nil?
|
2223
|
+
v << "has-context" unless context.nil?
|
2224
|
+
v.join(" ") + "]"
|
2225
|
+
end
|
2226
|
+
end
|
2168
2227
|
end
|
2169
2228
|
end
|