json-ld 3.1.3 → 3.1.4
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 +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
|