json-ld 3.1.0 → 3.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +66 -46
- data/VERSION +1 -1
- data/bin/jsonld +27 -30
- data/lib/json/ld.rb +12 -8
- data/lib/json/ld/api.rb +51 -43
- data/lib/json/ld/compact.rb +82 -68
- data/lib/json/ld/conneg.rb +1 -1
- data/lib/json/ld/context.rb +650 -542
- data/lib/json/ld/expand.rb +154 -87
- data/lib/json/ld/flatten.rb +1 -1
- data/lib/json/ld/format.rb +10 -6
- data/lib/json/ld/frame.rb +1 -2
- data/lib/json/ld/from_rdf.rb +7 -8
- data/lib/json/ld/html/nokogiri.rb +2 -1
- data/lib/json/ld/html/rexml.rb +2 -1
- data/lib/json/ld/reader.rb +20 -11
- data/lib/json/ld/streaming_reader.rb +578 -0
- data/lib/json/ld/to_rdf.rb +9 -3
- data/lib/json/ld/writer.rb +12 -5
- data/spec/compact_spec.rb +1 -0
- data/spec/context_spec.rb +63 -116
- data/spec/expand_spec.rb +29 -9
- data/spec/frame_spec.rb +44 -0
- data/spec/matchers.rb +1 -1
- data/spec/reader_spec.rb +33 -34
- data/spec/streaming_reader_spec.rb +237 -0
- data/spec/suite_expand_spec.rb +4 -2
- data/spec/suite_frame_spec.rb +0 -1
- data/spec/suite_helper.rb +23 -8
- data/spec/suite_to_rdf_spec.rb +1 -1
- data/spec/to_rdf_spec.rb +3 -3
- metadata +11 -8
data/lib/json/ld/conneg.rb
CHANGED
@@ -46,7 +46,7 @@ module JSON::LD
|
|
46
46
|
#
|
47
47
|
# @param [Hash{String => String}] env
|
48
48
|
# @return [Array(Integer, Hash, #each)] Status, Headers and Body
|
49
|
-
# @see
|
49
|
+
# @see https://rubydoc.info/github/rack/rack/file/SPEC
|
50
50
|
def call(env)
|
51
51
|
response = app.call(env)
|
52
52
|
body = response[2].respond_to?(:body) ? response[2].body : response[2]
|
data/lib/json/ld/context.rb
CHANGED
@@ -3,6 +3,14 @@
|
|
3
3
|
require 'json'
|
4
4
|
require 'bigdecimal'
|
5
5
|
require 'set'
|
6
|
+
require 'rdf/util/cache'
|
7
|
+
|
8
|
+
begin
|
9
|
+
# Attempt to load this to avoid unnecessary context fetches
|
10
|
+
require 'json-ld-preloaded'
|
11
|
+
rescue LoadError
|
12
|
+
# Silently allow this to fail
|
13
|
+
end
|
6
14
|
|
7
15
|
module JSON::LD
|
8
16
|
class Context
|
@@ -15,6 +23,14 @@ module JSON::LD
|
|
15
23
|
# @return [Hash{Symbol => Context}]
|
16
24
|
PRELOADED = {}
|
17
25
|
|
26
|
+
# Initial contexts, defined on first access
|
27
|
+
INITIAL_CONTEXTS = {}
|
28
|
+
|
29
|
+
##
|
30
|
+
# Defines the maximum number of interned URI references that can be held
|
31
|
+
# cached in memory at any one time.
|
32
|
+
CACHE_SIZE = 100 # unlimited by default
|
33
|
+
|
18
34
|
class << self
|
19
35
|
##
|
20
36
|
# Add preloaded context. In the block form, the context is lazy evaulated on first use.
|
@@ -34,234 +50,11 @@ module JSON::LD
|
|
34
50
|
end
|
35
51
|
end
|
36
52
|
|
37
|
-
# Term Definitions specify how properties and values have to be interpreted as well as the current vocabulary mapping and the default language
|
38
|
-
class TermDefinition
|
39
|
-
# @return [RDF::URI] IRI map
|
40
|
-
attr_accessor :id
|
41
|
-
|
42
|
-
# @return [String] term name
|
43
|
-
attr_accessor :term
|
44
|
-
|
45
|
-
# @return [String] Type mapping
|
46
|
-
attr_accessor :type_mapping
|
47
|
-
|
48
|
-
# Base container mapping, without @set
|
49
|
-
# @return [Array<'@index', '@language', '@index', '@set', '@type', '@id', '@graph'>] Container mapping
|
50
|
-
attr_reader :container_mapping
|
51
|
-
|
52
|
-
# @return [String] Term used for nest properties
|
53
|
-
attr_accessor :nest
|
54
|
-
|
55
|
-
# Language mapping of term, `false` is used if there is an explicit language mapping for this term.
|
56
|
-
# @return [String] Language mapping
|
57
|
-
attr_accessor :language_mapping
|
58
|
-
|
59
|
-
# Direction of term, `false` is used if there is explicit direction mapping mapping for this term.
|
60
|
-
# @return ["ltr", "rtl"] direction_mapping
|
61
|
-
attr_accessor :direction_mapping
|
62
|
-
|
63
|
-
# @return [Boolean] Reverse Property
|
64
|
-
attr_accessor :reverse_property
|
65
|
-
|
66
|
-
# This is a simple term definition, not an expanded term definition
|
67
|
-
# @return [Boolean]
|
68
|
-
attr_accessor :simple
|
69
|
-
|
70
|
-
# Property used for data indexing; defaults to @index
|
71
|
-
# @return [Boolean]
|
72
|
-
attr_accessor :index
|
73
|
-
|
74
|
-
# Indicate that term may be used as a prefix
|
75
|
-
attr_writer :prefix
|
76
|
-
|
77
|
-
# Term-specific context
|
78
|
-
# @return [Hash{String => Object}]
|
79
|
-
attr_accessor :context
|
80
|
-
|
81
|
-
# Term is protected.
|
82
|
-
# @return [Boolean]
|
83
|
-
attr_writer :protected
|
84
|
-
|
85
|
-
# This is a simple term definition, not an expanded term definition
|
86
|
-
# @return [Boolean] simple
|
87
|
-
def simple?; simple; end
|
88
|
-
|
89
|
-
# This is an appropriate term to use as the prefix of a compact IRI
|
90
|
-
# @return [Boolean] simple
|
91
|
-
def prefix?; @prefix; end
|
92
|
-
|
93
|
-
# Create a new Term Mapping with an ID
|
94
|
-
# @param [String] term
|
95
|
-
# @param [String] id
|
96
|
-
# @param [String] type_mapping Type mapping
|
97
|
-
# @param [Array<'@index', '@language', '@index', '@set', '@type', '@id', '@graph'>] container_mapping
|
98
|
-
# @param [String] language_mapping
|
99
|
-
# Language mapping of term, `false` is used if there is an explicit language mapping for this term
|
100
|
-
# @param ["ltr", "rtl"] direction_mapping
|
101
|
-
# Direction mapping of term, `false` is used if there is an explicit direction mapping for this term
|
102
|
-
# @param [Boolean] reverse_property
|
103
|
-
# @param [Boolean] protected
|
104
|
-
# @param [String] nest term used for nest properties
|
105
|
-
# @param [Boolean] simple
|
106
|
-
# This is a simple term definition, not an expanded term definition
|
107
|
-
# @param [Boolean] prefix
|
108
|
-
# Term may be used as a prefix
|
109
|
-
def initialize(term,
|
110
|
-
id: nil,
|
111
|
-
index: nil,
|
112
|
-
type_mapping: nil,
|
113
|
-
container_mapping: nil,
|
114
|
-
language_mapping: nil,
|
115
|
-
direction_mapping: nil,
|
116
|
-
reverse_property: false,
|
117
|
-
nest: nil,
|
118
|
-
protected: false,
|
119
|
-
simple: false,
|
120
|
-
prefix: nil,
|
121
|
-
context: nil)
|
122
|
-
@term = term
|
123
|
-
@id = id.to_s unless id.nil?
|
124
|
-
@index = index.to_s unless index.nil?
|
125
|
-
@type_mapping = type_mapping.to_s unless type_mapping.nil?
|
126
|
-
self.container_mapping = container_mapping
|
127
|
-
@language_mapping = language_mapping unless language_mapping.nil?
|
128
|
-
@direction_mapping = direction_mapping unless direction_mapping.nil?
|
129
|
-
@reverse_property = reverse_property
|
130
|
-
@protected = protected
|
131
|
-
@nest = nest unless nest.nil?
|
132
|
-
@simple = simple
|
133
|
-
@prefix = prefix unless prefix.nil?
|
134
|
-
@context = context unless context.nil?
|
135
|
-
end
|
136
|
-
|
137
|
-
# Term is protected.
|
138
|
-
# @return [Boolean]
|
139
|
-
def protected?; !!@protected; end
|
140
|
-
|
141
|
-
# Set container mapping, from an array which may include @set
|
142
|
-
def container_mapping=(mapping)
|
143
|
-
mapping = Array(mapping)
|
144
|
-
if @as_set = mapping.include?('@set')
|
145
|
-
mapping = mapping.dup
|
146
|
-
mapping.delete('@set')
|
147
|
-
end
|
148
|
-
@container_mapping = mapping.sort
|
149
|
-
@index ||= '@index' if mapping.include?('@index')
|
150
|
-
end
|
151
|
-
|
152
|
-
##
|
153
|
-
# Output Hash or String definition for this definition considering @language and @vocab
|
154
|
-
#
|
155
|
-
# @param [Context] context
|
156
|
-
# @return [String, Hash{String => Array[String], String}]
|
157
|
-
def to_context_definition(context)
|
158
|
-
cid = if context.vocab && id.start_with?(context.vocab)
|
159
|
-
# Nothing to return unless it's the same as the vocab
|
160
|
-
id == context.vocab ? context.vocab : id.to_s[context.vocab.length..-1]
|
161
|
-
else
|
162
|
-
# Find a term to act as a prefix
|
163
|
-
iri, prefix = context.iri_to_term.detect {|i,p| id.to_s.start_with?(i.to_s)}
|
164
|
-
iri && iri != id ? "#{prefix}:#{id.to_s[iri.length..-1]}" : id
|
165
|
-
end
|
166
|
-
|
167
|
-
if simple?
|
168
|
-
cid.to_s unless cid == term && context.vocab
|
169
|
-
else
|
170
|
-
defn = {}
|
171
|
-
defn[reverse_property ? '@reverse' : '@id'] = cid.to_s unless cid == term && !reverse_property
|
172
|
-
if type_mapping
|
173
|
-
defn['@type'] = if KEYWORDS.include?(type_mapping)
|
174
|
-
type_mapping
|
175
|
-
else
|
176
|
-
context.compact_iri(type_mapping, vocab: true)
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
cm = Array(container_mapping)
|
181
|
-
cm << "@set" if as_set? && !cm.include?("@set")
|
182
|
-
cm = cm.first if cm.length == 1
|
183
|
-
defn['@container'] = cm unless cm.empty?
|
184
|
-
# Language set as false to be output as null
|
185
|
-
defn['@language'] = (@language_mapping ? @language_mapping : nil) unless @language_mapping.nil?
|
186
|
-
defn['@context'] = @context if @context
|
187
|
-
defn['@nest'] = @nest if @nest
|
188
|
-
defn['@index'] = @index if @index
|
189
|
-
defn['@prefix'] = @prefix unless @prefix.nil?
|
190
|
-
defn
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
##
|
195
|
-
# Turn this into a source for a new instantiation
|
196
|
-
# FIXME: context serialization
|
197
|
-
# @return [String]
|
198
|
-
def to_rb
|
199
|
-
defn = [%(TermDefinition.new\(#{term.inspect})]
|
200
|
-
%w(id index type_mapping container_mapping language_mapping direction_mapping reverse_property nest simple prefix context protected).each do |acc|
|
201
|
-
v = instance_variable_get("@#{acc}".to_sym)
|
202
|
-
v = v.to_s if v.is_a?(RDF::Term)
|
203
|
-
if acc == 'container_mapping'
|
204
|
-
v.concat(%w(@set)) if as_set?
|
205
|
-
v = v.first if v.length <= 1
|
206
|
-
end
|
207
|
-
defn << "#{acc}: #{v.inspect}" if v
|
208
|
-
end
|
209
|
-
defn.join(', ') + ")"
|
210
|
-
end
|
211
|
-
|
212
|
-
# If container mapping was defined along with @set
|
213
|
-
# @return [Boolean]
|
214
|
-
def as_set?; @as_set || false; end
|
215
|
-
|
216
|
-
# Check if term definitions are identical, modulo @protected
|
217
|
-
# @return [Boolean]
|
218
|
-
def ==(other)
|
219
|
-
other.is_a?(TermDefinition) &&
|
220
|
-
id == other.id &&
|
221
|
-
term == other.term &&
|
222
|
-
type_mapping == other.type_mapping &&
|
223
|
-
container_mapping == other.container_mapping &&
|
224
|
-
nest == other.nest &&
|
225
|
-
language_mapping == other.language_mapping &&
|
226
|
-
direction_mapping == other.direction_mapping &&
|
227
|
-
reverse_property == other.reverse_property &&
|
228
|
-
simple == other.simple &&
|
229
|
-
index == other.index &&
|
230
|
-
context == other.context &&
|
231
|
-
prefix? == other.prefix? &&
|
232
|
-
as_set? == other.as_set?
|
233
|
-
end
|
234
|
-
|
235
|
-
def inspect
|
236
|
-
v = %w([TD)
|
237
|
-
v << "id=#{@id}"
|
238
|
-
v << "index=#{index.inspect}" unless index.nil?
|
239
|
-
v << "term=#{@term}"
|
240
|
-
v << "rev" if reverse_property
|
241
|
-
v << "container=#{container_mapping}" if container_mapping
|
242
|
-
v << "as_set=#{as_set?.inspect}"
|
243
|
-
v << "lang=#{language_mapping.inspect}" unless language_mapping.nil?
|
244
|
-
v << "dir=#{direction_mapping.inspect}" unless direction_mapping.nil?
|
245
|
-
v << "type=#{type_mapping}" unless type_mapping.nil?
|
246
|
-
v << "nest=#{nest.inspect}" unless nest.nil?
|
247
|
-
v << "simple=true" if @simple
|
248
|
-
v << "protected=true" if @protected
|
249
|
-
v << "prefix=#{@prefix.inspect}" unless @prefix.nil?
|
250
|
-
v << "has-context" unless context.nil?
|
251
|
-
v.join(" ") + "]"
|
252
|
-
end
|
253
|
-
end
|
254
|
-
|
255
53
|
# The base.
|
256
54
|
#
|
257
55
|
# @return [RDF::URI] Current base IRI, used for expanding relative IRIs.
|
258
56
|
attr_reader :base
|
259
57
|
|
260
|
-
# The base.
|
261
|
-
#
|
262
|
-
# @return [RDF::URI] Document base IRI, to initialize `base`.
|
263
|
-
attr_reader :doc_base
|
264
|
-
|
265
58
|
# @return [RDF::URI] base IRI of the context, if loaded remotely.
|
266
59
|
attr_accessor :context_base
|
267
60
|
|
@@ -302,9 +95,6 @@ module JSON::LD
|
|
302
95
|
# @return [Hash{Symbol => Object}] Global options used in generating IRIs
|
303
96
|
attr_accessor :options
|
304
97
|
|
305
|
-
# @return [Context] A context provided to us that we can use without re-serializing XXX
|
306
|
-
attr_accessor :provided_context
|
307
|
-
|
308
98
|
# @return [BlankNodeNamer]
|
309
99
|
attr_accessor :namer
|
310
100
|
|
@@ -314,20 +104,81 @@ module JSON::LD
|
|
314
104
|
# @see #initialize
|
315
105
|
# @see #parse
|
316
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 [Boolean] override_protected (false)
|
110
|
+
# Protected terms may be cleared.
|
111
|
+
# @param [Boolean] propagate (true)
|
112
|
+
# If false, retains any previously defined term, which can be rolled back when the descending into a new node object changes.
|
317
113
|
# @raise [JsonLdError]
|
318
114
|
# on a remote context load error, syntax error, or a reference to a term which is not defined.
|
319
115
|
# @return [Context]
|
320
|
-
def self.parse(local_context,
|
321
|
-
|
116
|
+
def self.parse(local_context,
|
117
|
+
base: nil,
|
118
|
+
override_protected: false,
|
119
|
+
propagate: true,
|
120
|
+
**options)
|
121
|
+
c = self.new(**options)
|
122
|
+
if local_context.respond_to?(:empty?) && local_context.empty?
|
123
|
+
c
|
124
|
+
else
|
125
|
+
c.parse(local_context,
|
126
|
+
base: base,
|
127
|
+
override_protected: override_protected,
|
128
|
+
propagate: propagate)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
##
|
133
|
+
# Class-level cache used for retaining parsed remote contexts.
|
134
|
+
#
|
135
|
+
# @return [RDF::Util::Cache]
|
136
|
+
# @private
|
137
|
+
def self.cache
|
138
|
+
@cache ||= RDF::Util::Cache.new(CACHE_SIZE)
|
139
|
+
end
|
140
|
+
|
141
|
+
##
|
142
|
+
# Class-level cache inverse contexts.
|
143
|
+
#
|
144
|
+
# @return [RDF::Util::Cache]
|
145
|
+
# @private
|
146
|
+
def self.inverse_cache
|
147
|
+
@inverse_cache ||= RDF::Util::Cache.new(CACHE_SIZE)
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# @private
|
152
|
+
# Allow caching of well-known contexts
|
153
|
+
def self.new(**options)
|
154
|
+
if (options.keys - [
|
155
|
+
:compactArrays,
|
156
|
+
:documentLoader,
|
157
|
+
:extractAllScripts,
|
158
|
+
:ordered,
|
159
|
+
:processingMode,
|
160
|
+
:validate
|
161
|
+
]).empty?
|
162
|
+
# allow caching
|
163
|
+
key = options.hash
|
164
|
+
INITIAL_CONTEXTS[key] ||= begin
|
165
|
+
context = JSON::LD::Context.allocate
|
166
|
+
context.send(:initialize, **options)
|
167
|
+
context.freeze
|
168
|
+
context.term_definitions.freeze
|
169
|
+
context
|
170
|
+
end
|
171
|
+
else
|
172
|
+
# Don't try to cache
|
173
|
+
context = JSON::LD::Context.allocate
|
174
|
+
context.send(:initialize, **options)
|
175
|
+
context
|
176
|
+
end
|
322
177
|
end
|
323
178
|
|
324
179
|
##
|
325
180
|
# Create new evaluation context
|
326
181
|
# @param [Hash] options
|
327
|
-
# @option options [String, #to_s] :base
|
328
|
-
# 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.
|
329
|
-
# @option options [Proc] :documentLoader
|
330
|
-
# 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.
|
331
182
|
# @option options [Hash{Symbol => String}] :prefixes
|
332
183
|
# See `RDF::Reader#initialize`
|
333
184
|
# @option options [String, #to_s] :vocab
|
@@ -338,11 +189,9 @@ module JSON::LD
|
|
338
189
|
# @yieldparam [Context]
|
339
190
|
# @return [Context]
|
340
191
|
def initialize(**options)
|
341
|
-
if options[:
|
342
|
-
@
|
343
|
-
@doc_base.canonicalize! if options[:canonicalize]
|
192
|
+
if options[:processingMode] == 'json-ld-1.0'
|
193
|
+
@processingMode = 'json-ld-1.0'
|
344
194
|
end
|
345
|
-
self.processingMode = options[:processingMode] if options.has_key?(:processingMode)
|
346
195
|
@term_definitions = {}
|
347
196
|
@iri_to_term = {
|
348
197
|
RDF.to_uri.to_s => "rdf",
|
@@ -368,135 +217,6 @@ module JSON::LD
|
|
368
217
|
yield(self) if block_given?
|
369
218
|
end
|
370
219
|
|
371
|
-
##
|
372
|
-
# Initial context, without mappings, vocab or default language
|
373
|
-
#
|
374
|
-
# @return [Boolean]
|
375
|
-
def empty?
|
376
|
-
@term_definitions.empty? && self.vocab.nil? && self.default_language.nil?
|
377
|
-
end
|
378
|
-
|
379
|
-
# @param [String] value must be an absolute IRI
|
380
|
-
def base=(value, **options)
|
381
|
-
if value
|
382
|
-
raise JsonLdError::InvalidBaseIRI, "@base must be a string: #{value.inspect}" unless value.is_a?(String) || value.is_a?(RDF::URI)
|
383
|
-
value = RDF::URI(value).dup
|
384
|
-
value = @base.join(value) if @base && value.relative?
|
385
|
-
@base = value
|
386
|
-
@base.canonicalize! if @options[:canonicalize]
|
387
|
-
raise JsonLdError::InvalidBaseIRI, "@base must be an absolute IRI: #{value.inspect}" unless @base.absolute? || !@options[:validate]
|
388
|
-
@base
|
389
|
-
else
|
390
|
-
@base = nil
|
391
|
-
end
|
392
|
-
|
393
|
-
end
|
394
|
-
|
395
|
-
# @param [String] value
|
396
|
-
def default_language=(value, **options)
|
397
|
-
@default_language = case value
|
398
|
-
when String
|
399
|
-
# Warn on an invalid language tag, unless :validate is true, in which case it's an error
|
400
|
-
if value !~ /^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/
|
401
|
-
warn "@language must be valid BCP47: #{value.inspect}"
|
402
|
-
end
|
403
|
-
options[:lowercaseLanguage] ? value.downcase : value
|
404
|
-
when nil
|
405
|
-
nil
|
406
|
-
else
|
407
|
-
raise JsonLdError::InvalidDefaultLanguage, "@language must be a string: #{value.inspect}"
|
408
|
-
end
|
409
|
-
end
|
410
|
-
|
411
|
-
# @param [String] value
|
412
|
-
def default_direction=(value, **options)
|
413
|
-
@default_direction = if value
|
414
|
-
raise JsonLdError::InvalidBaseDirection, "@direction must be one or 'ltr', or 'rtl': #{value.inspect}" unless %w(ltr rtl).include?(value)
|
415
|
-
value
|
416
|
-
else
|
417
|
-
nil
|
418
|
-
end
|
419
|
-
end
|
420
|
-
|
421
|
-
##
|
422
|
-
# Retrieve, or check processing mode.
|
423
|
-
#
|
424
|
-
# * With no arguments, retrieves the current set processingMode.
|
425
|
-
# * 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"
|
426
|
-
# * If expecting 1.1, and not set, it has the side-effect of setting mode to json-ld-1.1.
|
427
|
-
#
|
428
|
-
# @param [String, Number] expected (nil)
|
429
|
-
# @return [String]
|
430
|
-
def processingMode(expected = nil)
|
431
|
-
case expected
|
432
|
-
when 1.0, 'json-ld-1.0'
|
433
|
-
@processingMode == 'json-ld-1.0'
|
434
|
-
when 1.1, 'json-ld-1.1'
|
435
|
-
@processingMode ||= 'json-ld-1.1'
|
436
|
-
@processingMode == 'json-ld-1.1'
|
437
|
-
when nil
|
438
|
-
@processingMode
|
439
|
-
else
|
440
|
-
false
|
441
|
-
end
|
442
|
-
end
|
443
|
-
|
444
|
-
##
|
445
|
-
# Set processing mode.
|
446
|
-
#
|
447
|
-
# * 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"
|
448
|
-
#
|
449
|
-
# 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.
|
450
|
-
# If processingMode has been set, and it is not "json-ld-1.1", a "processing mode conflict" has been detecting, and processing is aborted.
|
451
|
-
#
|
452
|
-
# @param [String, Number] expected
|
453
|
-
# @return [String]
|
454
|
-
# @raise [JsonLdError::ProcessingModeConflict]
|
455
|
-
def processingMode=(value = nil, **options)
|
456
|
-
value = "json-ld-1.1" if value == 1.1
|
457
|
-
case value
|
458
|
-
when "json-ld-1.0", "json-ld-1.1"
|
459
|
-
if @processingMode && @processingMode != value
|
460
|
-
raise JsonLdError::ProcessingModeConflict, "#{value} not compatible with #{@processingMode}"
|
461
|
-
end
|
462
|
-
@processingMode = value
|
463
|
-
else
|
464
|
-
raise JsonLdError::InvalidVersionValue, value.inspect
|
465
|
-
end
|
466
|
-
end
|
467
|
-
|
468
|
-
# 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.
|
469
|
-
# @param [String] value must be an absolute IRI
|
470
|
-
def vocab=(value, **options)
|
471
|
-
@vocab = case value
|
472
|
-
when /_:/
|
473
|
-
# BNode vocab is deprecated
|
474
|
-
warn "[DEPRECATION] Blank Node vocabularies deprecated in JSON-LD 1.1." if @options[:validate] && processingMode("json-ld-1.1")
|
475
|
-
value
|
476
|
-
when String, RDF::URI
|
477
|
-
if (RDF::URI(value.to_s).relative? && processingMode("json-ld-1.0"))
|
478
|
-
raise JsonLdError::InvalidVocabMapping, "@vocab must be an absolute IRI in 1.0 mode: #{value.inspect}"
|
479
|
-
end
|
480
|
-
v = expand_iri(value.to_s, vocab: true, documentRelative: true)
|
481
|
-
raise JsonLdError::InvalidVocabMapping, "@vocab must be an IRI: #{value.inspect}" if !v.valid? && @options[:validate]
|
482
|
-
v
|
483
|
-
when nil
|
484
|
-
nil
|
485
|
-
else
|
486
|
-
raise JsonLdError::InvalidVocabMapping, "@vocab must be an IRI: #{value.inspect}"
|
487
|
-
end
|
488
|
-
end
|
489
|
-
|
490
|
-
# Set propagation
|
491
|
-
# @note: by the time this is called, the work has already been done.
|
492
|
-
#
|
493
|
-
# @param [Boolean] value
|
494
|
-
def propagate=(value, **options)
|
495
|
-
raise JsonLdError::InvalidContextMember, "@propagate may only be set in 1.1 mode" if processingMode("json-ld-1.0")
|
496
|
-
raise JsonLdError::InvalidPropagateValue, "@propagate must be boolean valued: #{value.inspect}" unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
497
|
-
value
|
498
|
-
end
|
499
|
-
|
500
220
|
# Create an Evaluation Context
|
501
221
|
#
|
502
222
|
# 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.
|
@@ -507,18 +227,26 @@ module JSON::LD
|
|
507
227
|
#
|
508
228
|
#
|
509
229
|
# @param [String, #read, Array, Hash, Context] local_context
|
510
|
-
# @param [
|
511
|
-
#
|
230
|
+
# @param [String, #to_s] base
|
231
|
+
# The Base IRI to use when expanding the document. This overrides the value of `input` if it is a _IRI_. If not specified and `input` is not an _IRI_, the base IRI defaults to the current document IRI if in a browser context, or the empty string if there is no document context.
|
512
232
|
# @param [Boolean] override_protected Protected terms may be cleared.
|
513
|
-
# @param [Boolean] propagate
|
233
|
+
# @param [Boolean] propagate (true)
|
514
234
|
# If false, retains any previously defined term, which can be rolled back when the descending into a new node object changes.
|
235
|
+
# @param [Array<String>] remote_contexts ([])
|
236
|
+
# @param [Boolean] validate_scoped (true).
|
237
|
+
# Validate scoped context, loading if necessary.
|
238
|
+
# If false, do not load scoped contexts.
|
515
239
|
# @raise [JsonLdError]
|
516
240
|
# on a remote context load error, syntax error, or a reference to a term which is not defined.
|
517
241
|
# @return [Context]
|
518
242
|
# @see https://www.w3.org/TR/json-ld11-api/index.html#context-processing-algorithm
|
519
|
-
def parse(local_context,
|
243
|
+
def parse(local_context,
|
244
|
+
base: nil,
|
245
|
+
override_protected: false,
|
246
|
+
propagate: true,
|
247
|
+
remote_contexts: [],
|
248
|
+
validate_scoped: true)
|
520
249
|
result = self.dup
|
521
|
-
result.provided_context = local_context if self.empty?
|
522
250
|
# Early check for @propagate, which can only appear in a local context
|
523
251
|
propagate = local_context.is_a?(Hash) ? local_context.fetch('@propagate', propagate) : propagate
|
524
252
|
result.previous_context ||= result.dup unless propagate
|
@@ -527,7 +255,7 @@ module JSON::LD
|
|
527
255
|
|
528
256
|
local_context.each do |context|
|
529
257
|
case context
|
530
|
-
when nil
|
258
|
+
when nil,false
|
531
259
|
# 3.1 If the `override_protected` is false, and the active context contains protected terms, an error is raised.
|
532
260
|
if override_protected || result.term_definitions.values.none?(&:protected?)
|
533
261
|
null_context = Context.new(**options)
|
@@ -539,37 +267,34 @@ module JSON::LD
|
|
539
267
|
end
|
540
268
|
when Context
|
541
269
|
#log_debug("parse") {"context: #{context.inspect}"}
|
542
|
-
result = context
|
270
|
+
result = result.merge(context)
|
543
271
|
when IO, StringIO
|
544
272
|
#log_debug("parse") {"io: #{context}"}
|
545
273
|
# Load context document, if it is an open file
|
546
274
|
begin
|
547
275
|
ctx = JSON.load(context)
|
548
276
|
raise JSON::LD::JsonLdError::InvalidRemoteContext, "Context missing @context key" if @options[:validate] && ctx['@context'].nil?
|
549
|
-
result = result.
|
550
|
-
result.provided_context = ctx["@context"] if [context] == local_context
|
551
|
-
result
|
277
|
+
result = result.parse(ctx["@context"] ? ctx["@context"] : {})
|
552
278
|
rescue JSON::ParserError => e
|
553
279
|
#log_debug("parse") {"Failed to parse @context from remote document at #{context}: #{e.message}"}
|
554
280
|
raise JSON::LD::JsonLdError::InvalidRemoteContext, "Failed to parse remote context at #{context}: #{e.message}" if @options[:validate]
|
555
|
-
self
|
281
|
+
self
|
556
282
|
end
|
557
283
|
when String, RDF::URI
|
558
284
|
#log_debug("parse") {"remote: #{context}, base: #{result.context_base || result.base}"}
|
559
285
|
|
560
286
|
# 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].
|
561
|
-
context = RDF::URI(result.context_base ||
|
287
|
+
context = RDF::URI(result.context_base || base).join(context)
|
562
288
|
context_canon = context.canonicalize
|
563
|
-
context_canon.scheme
|
289
|
+
context_canon.scheme = 'http' if context_canon.scheme == 'https'
|
290
|
+
|
291
|
+
# If validating a scoped context which has already been loaded, skip to the next one
|
292
|
+
next if !validate_scoped && remote_contexts.include?(context.to_s)
|
564
293
|
|
565
294
|
remote_contexts << context.to_s
|
566
295
|
raise JsonLdError::ContextOverflow, "#{context}" if remote_contexts.length >= MAX_CONTEXTS_LOADED
|
567
296
|
|
568
|
-
|
569
|
-
context_no_base.base = nil
|
570
|
-
context_no_base.context_base = context.to_s
|
571
|
-
|
572
|
-
if PRELOADED[context_canon.to_s]
|
297
|
+
cached_context = if PRELOADED[context_canon.to_s]
|
573
298
|
# If we have a cached context, merge it into the current context (result) and use as the new context
|
574
299
|
#log_debug("parse") {"=> cached_context: #{context_canon.to_s.inspect}"}
|
575
300
|
|
@@ -578,10 +303,10 @@ module JSON::LD
|
|
578
303
|
#log_debug("parse") {"=> (call)"}
|
579
304
|
PRELOADED[context_canon.to_s] = PRELOADED[context_canon.to_s].call
|
580
305
|
end
|
581
|
-
|
306
|
+
PRELOADED[context_canon.to_s]
|
582
307
|
else
|
583
308
|
# Load context document, if it is a string
|
584
|
-
begin
|
309
|
+
Context.cache[context_canon.to_s] ||= begin
|
585
310
|
context_opts = @options.merge(
|
586
311
|
profile: 'http://www.w3.org/ns/json-ld#context',
|
587
312
|
requestProfile: 'http://www.w3.org/ns/json-ld#context',
|
@@ -590,26 +315,30 @@ module JSON::LD
|
|
590
315
|
JSON::LD::API.loadRemoteDocument(context.to_s, **context_opts) do |remote_doc|
|
591
316
|
# 3.2.5) Dereference context. If the dereferenced document has no top-level JSON object with an @context member, an invalid remote context has been detected and processing is aborted; otherwise, set context to the value of that member.
|
592
317
|
raise JsonLdError::InvalidRemoteContext, "#{context}" unless remote_doc.document.is_a?(Hash) && remote_doc.document.has_key?('@context')
|
593
|
-
|
318
|
+
|
319
|
+
# Parse stand-alone
|
320
|
+
ctx = Context.new(unfrozen: true, **options).dup
|
321
|
+
ctx.context_base = context.to_s
|
322
|
+
ctx = ctx.parse(remote_doc.document['@context'], remote_contexts: remote_contexts.dup)
|
323
|
+
ctx.instance_variable_set(:@base, nil)
|
324
|
+
ctx
|
594
325
|
end
|
595
326
|
rescue JsonLdError::LoadingDocumentFailed => e
|
596
327
|
#log_debug("parse") {"Failed to retrieve @context from remote document at #{context_no_base.context_base.inspect}: #{e.message}"}
|
597
|
-
raise JsonLdError::LoadingRemoteContextFailed, "#{
|
328
|
+
raise JsonLdError::LoadingRemoteContextFailed, "#{context}: #{e.message}", e.backtrace
|
598
329
|
rescue JsonLdError
|
599
330
|
raise
|
600
331
|
rescue StandardError => e
|
601
332
|
#log_debug("parse") {"Failed to retrieve @context from remote document at #{context_no_base.context_base.inspect}: #{e.message}"}
|
602
|
-
raise JsonLdError::LoadingRemoteContextFailed, "#{
|
333
|
+
raise JsonLdError::LoadingRemoteContextFailed, "#{context}: #{e.message}", e.backtrace
|
603
334
|
end
|
604
|
-
|
605
|
-
# 3.2.6) Set context to the result of recursively calling this algorithm, passing context no base for active context, context for local context, and remote contexts.
|
606
|
-
context = context_no_base.parse(context, remote_contexts: remote_contexts.dup, protected: protected, override_protected: override_protected, propagate: propagate)
|
607
|
-
PRELOADED[context_canon.to_s] = context.dup
|
608
|
-
context.provided_context = result.provided_context
|
609
335
|
end
|
610
|
-
|
336
|
+
|
337
|
+
# Merge loaded context noting protected term overriding
|
338
|
+
context = result.merge(cached_context, override_protected: override_protected)
|
339
|
+
|
340
|
+
context.previous_context = self unless propagate
|
611
341
|
result = context
|
612
|
-
#log_debug("parse") {"=> provided_context: #{context.inspect}"}
|
613
342
|
when Hash
|
614
343
|
context = context.dup # keep from modifying a hash passed as a param
|
615
344
|
|
@@ -626,33 +355,35 @@ module JSON::LD
|
|
626
355
|
next unless context.has_key?(key)
|
627
356
|
if key == '@import'
|
628
357
|
# Retrieve remote context and merge the remaining context object into the result.
|
629
|
-
raise JsonLdError::
|
358
|
+
raise JsonLdError::InvalidContextEntry, "@import may only be used in 1.1 mode}" if result.processingMode("json-ld-1.0")
|
630
359
|
raise JsonLdError::InvalidImportValue, "@import must be a string: #{context['@import'].inspect}" unless context['@import'].is_a?(String)
|
631
|
-
|
360
|
+
import_loc = RDF::URI(result.context_base || base).join(context['@import'])
|
632
361
|
begin
|
633
362
|
context_opts = @options.merge(
|
634
363
|
profile: 'http://www.w3.org/ns/json-ld#context',
|
635
364
|
requestProfile: 'http://www.w3.org/ns/json-ld#context',
|
636
365
|
base: nil)
|
637
366
|
context_opts.delete(:headers)
|
638
|
-
|
639
|
-
|
640
|
-
|
367
|
+
# FIXME: should cache this, but ContextCache is for parsed contexts
|
368
|
+
JSON::LD::API.loadRemoteDocument(import_loc, **context_opts) do |remote_doc|
|
369
|
+
# Dereference import_loc. If the dereferenced document has no top-level JSON object with an @context member, an invalid remote context has been detected and processing is aborted; otherwise, set context to the value of that member.
|
370
|
+
raise JsonLdError::InvalidRemoteContext, "#{import_loc}" unless remote_doc.document.is_a?(Hash) && remote_doc.document.has_key?('@context')
|
641
371
|
import_context = remote_doc.document['@context']
|
372
|
+
import_context.delete('@base')
|
642
373
|
raise JsonLdError::InvalidRemoteContext, "#{import_context.to_json} must be an object" unless import_context.is_a?(Hash)
|
643
|
-
raise JsonLdError::
|
374
|
+
raise JsonLdError::InvalidContextEntry, "#{import_context.to_json} must not include @import entry" if import_context.has_key?('@import')
|
644
375
|
context.delete(key)
|
645
376
|
context = import_context.merge(context)
|
646
377
|
end
|
647
378
|
rescue JsonLdError::LoadingDocumentFailed => e
|
648
|
-
raise JsonLdError::LoadingRemoteContextFailed, "#{
|
379
|
+
raise JsonLdError::LoadingRemoteContextFailed, "#{import_loc}: #{e.message}", e.backtrace
|
649
380
|
rescue JsonLdError
|
650
381
|
raise
|
651
382
|
rescue StandardError => e
|
652
|
-
raise JsonLdError::LoadingRemoteContextFailed, "#{
|
383
|
+
raise JsonLdError::LoadingRemoteContextFailed, "#{import_loc}: #{e.message}", e.backtrace
|
653
384
|
end
|
654
385
|
else
|
655
|
-
result.send(setter, context[key], remote_contexts: remote_contexts
|
386
|
+
result.send(setter, context[key], remote_contexts: remote_contexts)
|
656
387
|
end
|
657
388
|
context.delete(key)
|
658
389
|
end
|
@@ -663,8 +394,12 @@ module JSON::LD
|
|
663
394
|
context.each_key do |key|
|
664
395
|
# ... where key is not @base, @vocab, @language, or @version
|
665
396
|
result.create_term_definition(context, key, defined,
|
397
|
+
base: base,
|
666
398
|
override_protected: override_protected,
|
667
|
-
protected: context
|
399
|
+
protected: context['@protected'],
|
400
|
+
remote_contexts: remote_contexts.dup,
|
401
|
+
validate_scoped: validate_scoped
|
402
|
+
) unless NON_TERMDEF_KEYS.include?(key)
|
668
403
|
end
|
669
404
|
else
|
670
405
|
# 3.3) If context is not a JSON object, an invalid local context error has been detected and processing is aborted.
|
@@ -678,28 +413,29 @@ module JSON::LD
|
|
678
413
|
# Merge in a context, creating a new context with updates from `context`
|
679
414
|
#
|
680
415
|
# @param [Context] context
|
416
|
+
# @param [Boolean] override_protected Allow or disallow protected terms to be changed
|
681
417
|
# @return [Context]
|
682
|
-
def merge(context)
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
418
|
+
def merge(context, override_protected: false)
|
419
|
+
ctx = Context.new(term_definitions: self.term_definitions, standard_prefixes: options[:standard_prefixes])
|
420
|
+
ctx.context_base = context.context_base || self.context_base
|
421
|
+
ctx.default_language = context.default_language || self.default_language
|
422
|
+
ctx.default_direction = context.default_direction || self.default_direction
|
423
|
+
ctx.vocab = context.vocab || self.vocab
|
424
|
+
ctx.base = self.base unless self.base.nil?
|
425
|
+
if !override_protected
|
426
|
+
ctx.term_definitions.each do |term, definition|
|
427
|
+
next unless definition.protected? && (other = context.term_definitions[term])
|
428
|
+
unless definition == other
|
429
|
+
raise JSON::LD::JsonLdError::ProtectedTermRedefinition, "Attempt to redefine protected term #{term}"
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
687
433
|
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
def merge!(context)
|
694
|
-
# FIXME: if new context removes the default language, this won't do anything
|
695
|
-
self.default_language = context.default_language if context.default_language
|
696
|
-
self.vocab = context.vocab if context.vocab
|
697
|
-
self.base = context.base if context.base
|
698
|
-
|
699
|
-
# Merge in Term Definitions
|
700
|
-
term_definitions.merge!(context.term_definitions)
|
701
|
-
@inverse_context = nil # Re-build after term definitions set
|
702
|
-
self
|
434
|
+
# Add term definitions
|
435
|
+
context.term_definitions.each do |term, definition|
|
436
|
+
ctx.term_definitions[term] = definition
|
437
|
+
end
|
438
|
+
ctx
|
703
439
|
end
|
704
440
|
|
705
441
|
# The following constants are used to reduce object allocations in #create_term_definition below
|
@@ -722,14 +458,22 @@ module JSON::LD
|
|
722
458
|
# @param [Hash] local_context
|
723
459
|
# @param [String] term
|
724
460
|
# @param [Hash] defined
|
461
|
+
# @param [String, RDF::URI] base for resolving document-relative IRIs
|
725
462
|
# @param [Boolean] protected if true, causes all terms to be marked protected
|
726
463
|
# @param [Boolean] override_protected Protected terms may be cleared.
|
727
|
-
# @param [
|
728
|
-
#
|
464
|
+
# @param [Array<String>] remote_contexts
|
465
|
+
# @param [Boolean] validate_scoped (true).
|
466
|
+
# Validate scoped context, loading if necessary.
|
467
|
+
# If false, do not load scoped contexts.
|
729
468
|
# @raise [JsonLdError]
|
730
469
|
# Represents a cyclical term dependency
|
731
470
|
# @see https://www.w3.org/TR/json-ld11-api/index.html#create-term-definition
|
732
|
-
def create_term_definition(local_context, term, defined,
|
471
|
+
def create_term_definition(local_context, term, defined,
|
472
|
+
base: nil,
|
473
|
+
override_protected: false,
|
474
|
+
protected: nil,
|
475
|
+
remote_contexts: [],
|
476
|
+
validate_scoped: true)
|
733
477
|
# Expand a string value, unless it matches a keyword
|
734
478
|
#log_debug("create_term_definition") {"term = #{term.inspect}"}
|
735
479
|
|
@@ -749,6 +493,7 @@ module JSON::LD
|
|
749
493
|
# Since keywords cannot be overridden, term must not be a keyword. Otherwise, an invalid value has been detected, which is an error.
|
750
494
|
if term == '@type' &&
|
751
495
|
value.is_a?(Hash) &&
|
496
|
+
!value.empty? &&
|
752
497
|
processingMode("json-ld-1.1") &&
|
753
498
|
(value.keys - %w(@container @protected)).empty? &&
|
754
499
|
value.fetch('@container', '@set') == '@set'
|
@@ -827,6 +572,11 @@ module JSON::LD
|
|
827
572
|
raise JsonLdError::InvalidIRIMapping, "expected value of @reverse to be a string: #{value['@reverse'].inspect} on term #{term.inspect}" unless
|
828
573
|
value['@reverse'].is_a?(String)
|
829
574
|
|
575
|
+
if value['@reverse'].to_s.match?(/^@[a-zA-Z]+$/) && @options[:validate]
|
576
|
+
warn "Values beginning with '@' are reserved for future use and ignored: #{value['@reverse']}."
|
577
|
+
return
|
578
|
+
end
|
579
|
+
|
830
580
|
# Otherwise, set the IRI mapping of definition to the result of using the IRI Expansion algorithm, passing active context, the value associated with the @reverse key for value, true for vocab, true for document relative, local context, and defined. If the result is not an absolute IRI, i.e., it contains no colon (:), an invalid IRI mapping error has been detected and processing is aborted.
|
831
581
|
definition.id = expand_iri(value['@reverse'],
|
832
582
|
vocab: true,
|
@@ -835,11 +585,6 @@ module JSON::LD
|
|
835
585
|
raise JsonLdError::InvalidIRIMapping, "non-absolute @reverse IRI: #{definition.id} on term #{term.inspect}" unless
|
836
586
|
definition.id.is_a?(RDF::Node) || definition.id.is_a?(RDF::URI) && definition.id.absolute?
|
837
587
|
|
838
|
-
if value['@reverse'].to_s.match?(/^@[a-zA-Z]+$/) && @options[:validate]
|
839
|
-
warn "Values beginning with '@' are reserved for future use and ignored: #{value['@reverse']}."
|
840
|
-
return
|
841
|
-
end
|
842
|
-
|
843
588
|
if term[1..-1].to_s.include?(':') && (term_iri = expand_iri(term)) != definition.id
|
844
589
|
raise JsonLdError::InvalidIRIMapping, "term #{term} expands to #{definition.id}, not #{term_iri}"
|
845
590
|
end
|
@@ -943,9 +688,17 @@ module JSON::LD
|
|
943
688
|
|
944
689
|
if value.has_key?('@context')
|
945
690
|
begin
|
946
|
-
self.parse(value['@context'],
|
691
|
+
new_ctx = self.parse(value['@context'],
|
692
|
+
base: base,
|
693
|
+
override_protected: true,
|
694
|
+
remote_contexts: remote_contexts,
|
695
|
+
validate_scoped: false)
|
947
696
|
# Record null context in array form
|
948
|
-
definition.context =
|
697
|
+
definition.context = case value['@context']
|
698
|
+
when String then new_ctx.context_base
|
699
|
+
when nil then [nil]
|
700
|
+
else value['@context']
|
701
|
+
end
|
949
702
|
rescue JsonLdError => e
|
950
703
|
raise JsonLdError::InvalidScopedContext, "Term definition for #{term.inspect} contains illegal value for @context: #{e.message}"
|
951
704
|
end
|
@@ -1003,9 +756,130 @@ module JSON::LD
|
|
1003
756
|
|
1004
757
|
term_definitions[term] = definition
|
1005
758
|
defined[term] = true
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
759
|
+
end
|
760
|
+
|
761
|
+
##
|
762
|
+
# Initial context, without mappings, vocab or default language
|
763
|
+
#
|
764
|
+
# @return [Boolean]
|
765
|
+
def empty?
|
766
|
+
@term_definitions.empty? && self.vocab.nil? && self.default_language.nil?
|
767
|
+
end
|
768
|
+
|
769
|
+
# @param [String] value must be an absolute IRI
|
770
|
+
def base=(value, **options)
|
771
|
+
if value
|
772
|
+
raise JsonLdError::InvalidBaseIRI, "@base must be a string: #{value.inspect}" unless value.is_a?(String) || value.is_a?(RDF::URI)
|
773
|
+
value = RDF::URI(value)
|
774
|
+
value = @base.join(value) if @base && value.relative?
|
775
|
+
# still might be relative to document
|
776
|
+
@base = value
|
777
|
+
else
|
778
|
+
@base = false
|
779
|
+
end
|
780
|
+
|
781
|
+
end
|
782
|
+
|
783
|
+
# @param [String] value
|
784
|
+
def default_language=(value, **options)
|
785
|
+
@default_language = case value
|
786
|
+
when String
|
787
|
+
# Warn on an invalid language tag, unless :validate is true, in which case it's an error
|
788
|
+
if value !~ /^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/
|
789
|
+
warn "@language must be valid BCP47: #{value.inspect}"
|
790
|
+
end
|
791
|
+
options[:lowercaseLanguage] ? value.downcase : value
|
792
|
+
when nil
|
793
|
+
nil
|
794
|
+
else
|
795
|
+
raise JsonLdError::InvalidDefaultLanguage, "@language must be a string: #{value.inspect}"
|
796
|
+
end
|
797
|
+
end
|
798
|
+
|
799
|
+
# @param [String] value
|
800
|
+
def default_direction=(value, **options)
|
801
|
+
@default_direction = if value
|
802
|
+
raise JsonLdError::InvalidBaseDirection, "@direction must be one or 'ltr', or 'rtl': #{value.inspect}" unless %w(ltr rtl).include?(value)
|
803
|
+
value
|
804
|
+
else
|
805
|
+
nil
|
806
|
+
end
|
807
|
+
end
|
808
|
+
|
809
|
+
##
|
810
|
+
# Retrieve, or check processing mode.
|
811
|
+
#
|
812
|
+
# * With no arguments, retrieves the current set processingMode.
|
813
|
+
# * 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"
|
814
|
+
# * If expecting 1.1, and not set, it has the side-effect of setting mode to json-ld-1.1.
|
815
|
+
#
|
816
|
+
# @param [String, Number] expected (nil)
|
817
|
+
# @return [String]
|
818
|
+
def processingMode(expected = nil)
|
819
|
+
case expected
|
820
|
+
when 1.0, 'json-ld-1.0'
|
821
|
+
@processingMode == 'json-ld-1.0'
|
822
|
+
when 1.1, 'json-ld-1.1'
|
823
|
+
@processingMode.nil? || @processingMode == 'json-ld-1.1'
|
824
|
+
when nil
|
825
|
+
@processingMode || 'json-ld-1.1'
|
826
|
+
else
|
827
|
+
false
|
828
|
+
end
|
829
|
+
end
|
830
|
+
|
831
|
+
##
|
832
|
+
# Set processing mode.
|
833
|
+
#
|
834
|
+
# * 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"
|
835
|
+
#
|
836
|
+
# If contex has a @version member, it's value MUST be 1.1, otherwise an "invalid @version value" has been detected, and processing is aborted.
|
837
|
+
# If processingMode has been set, and it is not "json-ld-1.1", a "processing mode conflict" has been detecting, and processing is aborted.
|
838
|
+
#
|
839
|
+
# @param [String, Number] value
|
840
|
+
# @return [String]
|
841
|
+
# @raise [JsonLdError::ProcessingModeConflict]
|
842
|
+
def processingMode=(value = nil, **options)
|
843
|
+
value = "json-ld-1.1" if value == 1.1
|
844
|
+
case value
|
845
|
+
when "json-ld-1.0", "json-ld-1.1"
|
846
|
+
if @processingMode && @processingMode != value
|
847
|
+
raise JsonLdError::ProcessingModeConflict, "#{value} not compatible with #{@processingMode}"
|
848
|
+
end
|
849
|
+
@processingMode = value
|
850
|
+
else
|
851
|
+
raise JsonLdError::InvalidVersionValue, value.inspect
|
852
|
+
end
|
853
|
+
end
|
854
|
+
|
855
|
+
# 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.
|
856
|
+
# @param [String] value must be an absolute IRI
|
857
|
+
def vocab=(value, **options)
|
858
|
+
@vocab = case value
|
859
|
+
when /_:/
|
860
|
+
# BNode vocab is deprecated
|
861
|
+
warn "[DEPRECATION] Blank Node vocabularies deprecated in JSON-LD 1.1." if @options[:validate] && processingMode("json-ld-1.1")
|
862
|
+
value
|
863
|
+
when String, RDF::URI
|
864
|
+
if (RDF::URI(value.to_s).relative? && processingMode("json-ld-1.0"))
|
865
|
+
raise JsonLdError::InvalidVocabMapping, "@vocab must be an absolute IRI in 1.0 mode: #{value.inspect}"
|
866
|
+
end
|
867
|
+
expand_iri(value.to_s, vocab: true, documentRelative: true)
|
868
|
+
when nil
|
869
|
+
nil
|
870
|
+
else
|
871
|
+
raise JsonLdError::InvalidVocabMapping, "@vocab must be an IRI: #{value.inspect}"
|
872
|
+
end
|
873
|
+
end
|
874
|
+
|
875
|
+
# Set propagation
|
876
|
+
# @note: by the time this is called, the work has already been done.
|
877
|
+
#
|
878
|
+
# @param [Boolean] value
|
879
|
+
def propagate=(value, **options)
|
880
|
+
raise JsonLdError::InvalidContextEntry, "@propagate may only be set in 1.1 mode" if processingMode("json-ld-1.0")
|
881
|
+
raise JsonLdError::InvalidPropagateValue, "@propagate must be boolean valued: #{value.inspect}" unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
882
|
+
value
|
1009
883
|
end
|
1010
884
|
|
1011
885
|
##
|
@@ -1014,40 +888,44 @@ module JSON::LD
|
|
1014
888
|
# If a context was supplied in global options, use that, otherwise, generate one
|
1015
889
|
# from this representation.
|
1016
890
|
#
|
891
|
+
# @param [Array, Hash, Context, IO, StringIO] provided_context (nil)
|
892
|
+
# Original context to use, if available
|
1017
893
|
# @param [Hash{Symbol => Object}] options ({})
|
1018
894
|
# @return [Hash]
|
1019
|
-
def serialize(**options)
|
1020
|
-
#
|
895
|
+
def serialize(provided_context: nil, **options)
|
896
|
+
#log_debug("serlialize: generate context")
|
897
|
+
#log_debug("") {"=> context: #{inspect}"}
|
1021
898
|
use_context = case provided_context
|
1022
899
|
when String, RDF::URI
|
1023
900
|
#log_debug "serlialize: reuse context: #{provided_context.inspect}"
|
1024
901
|
provided_context.to_s
|
1025
|
-
when Hash
|
902
|
+
when Hash
|
903
|
+
#log_debug "serlialize: reuse context: #{provided_context.inspect}"
|
904
|
+
# If it has an @context entry use it, otherwise it is assumed to be the body of a context
|
905
|
+
provided_context.fetch('@context', provided_context)
|
906
|
+
when Array
|
1026
907
|
#log_debug "serlialize: reuse context: #{provided_context.inspect}"
|
1027
908
|
provided_context
|
909
|
+
when IO, StringIO
|
910
|
+
provided_context.rewind
|
911
|
+
JSON.load(provided_context).fetch('@context', {})
|
1028
912
|
else
|
1029
|
-
#log_debug("serlialize: generate context")
|
1030
|
-
#log_debug("") {"=> context: #{inspect}"}
|
1031
913
|
ctx = {}
|
1032
|
-
ctx['@
|
914
|
+
ctx['@version'] = 1.1 if @processingMode == 'json-ld-1.1'
|
915
|
+
ctx['@base'] = base.to_s if base
|
1033
916
|
ctx['@direction'] = default_direction.to_s if default_direction
|
1034
917
|
ctx['@language'] = default_language.to_s if default_language
|
1035
918
|
ctx['@vocab'] = vocab.to_s if vocab
|
1036
919
|
|
1037
920
|
# Term Definitions
|
1038
|
-
term_definitions.
|
1039
|
-
|
1040
|
-
ctx[term] = defn if defn
|
921
|
+
term_definitions.each do |term, defn|
|
922
|
+
ctx[term] = defn.to_context_definition(self)
|
1041
923
|
end
|
1042
|
-
|
1043
|
-
#log_debug("") {"start_doc: context=#{ctx.inspect}"}
|
1044
924
|
ctx
|
1045
925
|
end
|
1046
926
|
|
1047
927
|
# Return hash with @context, or empty
|
1048
|
-
|
1049
|
-
r['@context'] = use_context unless use_context.nil? || use_context.empty?
|
1050
|
-
r
|
928
|
+
use_context.nil? || use_context.empty? ? {} : {'@context' => use_context}
|
1051
929
|
end
|
1052
930
|
|
1053
931
|
##
|
@@ -1162,7 +1040,7 @@ module JSON::LD
|
|
1162
1040
|
def container(term)
|
1163
1041
|
return [term] if term == '@list'
|
1164
1042
|
term = find_definition(term)
|
1165
|
-
term ? term.container_mapping :
|
1043
|
+
term ? term.container_mapping : Set.new
|
1166
1044
|
end
|
1167
1045
|
|
1168
1046
|
##
|
@@ -1214,7 +1092,7 @@ module JSON::LD
|
|
1214
1092
|
term.nest
|
1215
1093
|
else
|
1216
1094
|
nest_term = find_definition(term.nest)
|
1217
|
-
raise JsonLdError::InvalidNestValue, "nest must a term resolving to @nest, was #{nest_term.inspect}" unless nest_term && nest_term.
|
1095
|
+
raise JsonLdError::InvalidNestValue, "nest must a term resolving to @nest, was #{nest_term.inspect}" unless nest_term && nest_term.id == '@nest'
|
1218
1096
|
term.nest
|
1219
1097
|
end
|
1220
1098
|
end
|
@@ -1275,24 +1153,31 @@ module JSON::LD
|
|
1275
1153
|
#
|
1276
1154
|
# @param [String] value
|
1277
1155
|
# A keyword, term, prefix:suffix or possibly relative IRI
|
1156
|
+
# @param [Boolean] as_string (false) transform RDF::Resource values to string
|
1157
|
+
# @param [String, RDF::URI] base for resolving document-relative IRIs
|
1158
|
+
# @param [Hash] defined
|
1159
|
+
# Used during Context Processing.
|
1278
1160
|
# @param [Boolean] documentRelative (false)
|
1279
|
-
# @param [Boolean] vocab (false)
|
1280
1161
|
# @param [Hash] local_context
|
1281
1162
|
# Used during Context Processing.
|
1282
|
-
# @param [
|
1283
|
-
# Used during Context Processing.
|
1284
|
-
# @param [Boolean] quiet (false)
|
1163
|
+
# @param [Boolean] vocab (false)
|
1285
1164
|
# @param [Hash{Symbol => Object}] options
|
1286
|
-
# @return [RDF::
|
1165
|
+
# @return [RDF::Resource, String]
|
1287
1166
|
# IRI or String, if it's a keyword
|
1288
1167
|
# @raise [JSON::LD::JsonLdError::InvalidIRIMapping] if the value cannot be expanded
|
1289
1168
|
# @see https://www.w3.org/TR/json-ld11-api/#iri-expansion
|
1290
|
-
def expand_iri(value,
|
1291
|
-
|
1169
|
+
def expand_iri(value,
|
1170
|
+
as_string: false,
|
1171
|
+
base: nil,
|
1172
|
+
defined: nil,
|
1173
|
+
documentRelative: false,
|
1174
|
+
local_context: nil,
|
1175
|
+
vocab: false,
|
1176
|
+
**options)
|
1177
|
+
return (value && as_string ? value.to_s : value) unless value.is_a?(String)
|
1292
1178
|
|
1293
1179
|
return value if KEYWORDS.include?(value)
|
1294
1180
|
return nil if value.match?(/^@[a-zA-Z]+$/)
|
1295
|
-
#log_debug("expand_iri") {"value: #{value.inspect}"} unless quiet
|
1296
1181
|
|
1297
1182
|
defined = defined || {} # if we initialized in the keyword arg we would allocate {} at each invokation, even in the 2 (common) early returns above.
|
1298
1183
|
|
@@ -1302,25 +1187,29 @@ module JSON::LD
|
|
1302
1187
|
end
|
1303
1188
|
|
1304
1189
|
if (v_td = term_definitions[value]) && KEYWORDS.include?(v_td.id)
|
1305
|
-
|
1306
|
-
return v_td.id
|
1190
|
+
return (as_string ? v_td.id.to_s : v_td.id)
|
1307
1191
|
end
|
1308
1192
|
|
1309
1193
|
# If active context has a term definition for value, and the associated mapping is a keyword, return that keyword.
|
1310
1194
|
# If vocab is true and the active context has a term definition for value, return the associated IRI mapping.
|
1311
1195
|
if (v_td = term_definitions[value]) && (vocab || KEYWORDS.include?(v_td.id))
|
1312
|
-
|
1313
|
-
return
|
1196
|
+
iri = base && v_td.id ? base.join(v_td.id) : v_td.id # vocab might be doc relative
|
1197
|
+
return (as_string ? iri.to_s : iri)
|
1314
1198
|
end
|
1315
1199
|
|
1316
1200
|
# If value contains a colon (:), it is either an absolute IRI or a compact IRI:
|
1317
1201
|
if value[1..-1].to_s.include?(':')
|
1318
1202
|
prefix, suffix = value.split(':', 2)
|
1319
|
-
#log_debug("") {"prefix: #{prefix.inspect}, suffix: #{suffix.inspect}, vocab: #{self.vocab.inspect}"} unless quiet
|
1320
1203
|
|
1321
1204
|
# If prefix is underscore (_) or suffix begins with double-forward-slash (//), return value as it is already an absolute IRI or a blank node identifier.
|
1322
|
-
|
1323
|
-
|
1205
|
+
if prefix == '_'
|
1206
|
+
v = RDF::Node.new(namer.get_sym(suffix))
|
1207
|
+
return (as_string ? v.to_s : v)
|
1208
|
+
end
|
1209
|
+
if suffix.start_with?('//')
|
1210
|
+
v = RDF::URI(value)
|
1211
|
+
return (as_string ? v.to_s : v)
|
1212
|
+
end
|
1324
1213
|
|
1325
1214
|
# If local context is not null, it contains a key that equals prefix, and the value associated with the key that equals prefix in defined is not true, invoke the Create Term Definition algorithm, passing active context, local context, prefix as term, and defined. This will ensure that a term definition is created for prefix in active context during Context Processing.
|
1326
1215
|
if local_context && local_context.has_key?(prefix) && !defined[prefix]
|
@@ -1329,31 +1218,43 @@ module JSON::LD
|
|
1329
1218
|
|
1330
1219
|
# If active context contains a term definition for prefix, return the result of concatenating the IRI mapping associated with prefix and suffix.
|
1331
1220
|
if (td = term_definitions[prefix]) && !td.id.nil? && td.prefix?
|
1332
|
-
return td.id + suffix
|
1221
|
+
return (as_string ? td.id.to_s : td.id) + suffix
|
1333
1222
|
elsif RDF::URI(value).absolute?
|
1334
1223
|
# Otherwise, if the value has the form of an absolute IRI, return it
|
1335
|
-
return RDF::URI(value)
|
1224
|
+
return (as_string ? value.to_s : RDF::URI(value))
|
1336
1225
|
else
|
1337
1226
|
# Otherwise, it is a relative IRI
|
1338
1227
|
end
|
1339
1228
|
end
|
1340
|
-
#log_debug("") {"=> #{result.inspect}"} unless quiet
|
1341
1229
|
|
1230
|
+
iri = value.is_a?(RDF::URI) ? value : RDF::URI(value)
|
1342
1231
|
result = if vocab && self.vocab
|
1343
1232
|
# If vocab is true, and active context has a vocabulary mapping, return the result of concatenating the vocabulary mapping with value.
|
1344
|
-
|
1345
|
-
|
1346
|
-
|
1347
|
-
|
1348
|
-
|
1349
|
-
|
1233
|
+
# Note that @vocab could still be relative to a document base
|
1234
|
+
(base && self.vocab.is_a?(RDF::URI) && self.vocab.relative? ? base.join(self.vocab) : self.vocab) + value
|
1235
|
+
elsif documentRelative
|
1236
|
+
if iri.absolute?
|
1237
|
+
iri
|
1238
|
+
elsif self.base.is_a?(RDF::URI) && self.base.absolute?
|
1239
|
+
self.base.join(iri)
|
1240
|
+
elsif self.base == false
|
1241
|
+
# No resollution of `@base: null`
|
1242
|
+
iri
|
1243
|
+
elsif base && self.base
|
1244
|
+
base.join(self.base).join(iri)
|
1245
|
+
elsif base
|
1246
|
+
base.join(iri)
|
1247
|
+
else
|
1248
|
+
# Returns a relative IRI in an odd case.
|
1249
|
+
iri
|
1250
|
+
end
|
1251
|
+
elsif local_context && iri.relative?
|
1350
1252
|
# 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.
|
1351
1253
|
raise JSON::LD::JsonLdError::InvalidIRIMapping, "not an absolute IRI: #{value}"
|
1352
1254
|
else
|
1353
|
-
|
1255
|
+
iri
|
1354
1256
|
end
|
1355
|
-
|
1356
|
-
result
|
1257
|
+
result && as_string ? result.to_s : result
|
1357
1258
|
end
|
1358
1259
|
|
1359
1260
|
# The following constants are used to reduce object allocations in #compact_iri below
|
@@ -1372,24 +1273,21 @@ module JSON::LD
|
|
1372
1273
|
# Compacts an absolute IRI to the shortest matching term or compact IRI
|
1373
1274
|
#
|
1374
1275
|
# @param [RDF::URI] iri
|
1276
|
+
# @param [String, RDF::URI] base for resolving document-relative IRIs
|
1375
1277
|
# @param [Object] value
|
1376
1278
|
# Value, used to select among various maps for the same IRI
|
1377
|
-
# @param [Boolean] vocab
|
1378
|
-
# specifies whether the passed iri should be compacted using the active context's vocabulary mapping
|
1379
1279
|
# @param [Boolean] reverse
|
1380
1280
|
# specifies whether a reverse property is being compacted
|
1381
|
-
# @param [Boolean]
|
1382
|
-
#
|
1281
|
+
# @param [Boolean] vocab
|
1282
|
+
# specifies whether the passed iri should be compacted using the active context's vocabulary mapping
|
1383
1283
|
#
|
1384
1284
|
# @return [String] compacted form of IRI
|
1385
1285
|
# @see https://www.w3.org/TR/json-ld11-api/#iri-compaction
|
1386
|
-
def compact_iri(iri,
|
1286
|
+
def compact_iri(iri, base: nil, reverse: false, value: nil, vocab: nil)
|
1387
1287
|
return if iri.nil?
|
1388
1288
|
iri = iri.to_s
|
1389
|
-
#log_debug("compact_iri(#{iri.inspect}", options) {[value, vocab, reverse].inspect} unless quiet
|
1390
1289
|
|
1391
1290
|
if vocab && inverse_context.has_key?(iri)
|
1392
|
-
#log_debug("") {"vocab and key in inverse context"} unless quiet
|
1393
1291
|
default_language = if self.default_direction
|
1394
1292
|
"#{self.default_language}_#{self.default_direction}".downcase
|
1395
1293
|
else
|
@@ -1406,7 +1304,6 @@ module JSON::LD
|
|
1406
1304
|
tl, tl_value = "@type", "@reverse"
|
1407
1305
|
containers << '@set'
|
1408
1306
|
elsif list?(value)
|
1409
|
-
#log_debug("") {"list(#{value.inspect})"} unless quiet
|
1410
1307
|
# if value is a list object, then set type/language and type/language value to the most specific values that work for all items in the list as follows:
|
1411
1308
|
containers << "@list" unless index?(value)
|
1412
1309
|
list = value['@list']
|
@@ -1429,25 +1326,21 @@ module JSON::LD
|
|
1429
1326
|
end
|
1430
1327
|
common_language ||= item_language
|
1431
1328
|
if item_language != common_language && value?(item)
|
1432
|
-
#log_debug("") {"-- #{item_language} conflicts with #{common_language}, use @none"} unless quiet
|
1433
1329
|
common_language = '@none'
|
1434
1330
|
end
|
1435
1331
|
common_type ||= item_type
|
1436
1332
|
if item_type != common_type
|
1437
1333
|
common_type = '@none'
|
1438
|
-
#log_debug("") {"#{item_type} conflicts with #{common_type}, use @none"} unless quiet
|
1439
1334
|
end
|
1440
1335
|
end
|
1441
1336
|
|
1442
1337
|
common_language ||= '@none'
|
1443
1338
|
common_type ||= '@none'
|
1444
|
-
#log_debug("") {"common type: #{common_type}, common language: #{common_language}"} unless quiet
|
1445
1339
|
if common_type != '@none'
|
1446
1340
|
tl, tl_value = '@type', common_type
|
1447
1341
|
else
|
1448
1342
|
tl_value = common_language
|
1449
1343
|
end
|
1450
|
-
#log_debug("") {"list: containers: #{containers.inspect}, type/language: #{tl.inspect}, type/language value: #{tl_value.inspect}"} unless quiet
|
1451
1344
|
elsif graph?(value)
|
1452
1345
|
# Prefer @index and @id containers, then @graph, then @index
|
1453
1346
|
containers.concat(CONTAINERS_GRAPH_INDEX_INDEX) if index?(value)
|
@@ -1482,7 +1375,6 @@ module JSON::LD
|
|
1482
1375
|
tl, tl_value = '@type', '@id'
|
1483
1376
|
end
|
1484
1377
|
containers << '@set'
|
1485
|
-
#log_debug("") {"value: containers: #{containers.inspect}, type/language: #{tl.inspect}, type/language value: #{tl_value.inspect}"} unless quiet
|
1486
1378
|
end
|
1487
1379
|
|
1488
1380
|
containers << '@none'
|
@@ -1496,7 +1388,7 @@ module JSON::LD
|
|
1496
1388
|
preferred_values = []
|
1497
1389
|
preferred_values << '@reverse' if tl_value == '@reverse'
|
1498
1390
|
if (tl_value == '@id' || tl_value == '@reverse') && value.is_a?(Hash) && value.has_key?('@id')
|
1499
|
-
t_iri = compact_iri(value['@id'], vocab: true,
|
1391
|
+
t_iri = compact_iri(value['@id'], vocab: true, base: base)
|
1500
1392
|
if (r_td = term_definitions[t_iri]) && r_td.id == value['@id']
|
1501
1393
|
preferred_values.concat(CONTAINERS_VOCAB_ID)
|
1502
1394
|
else
|
@@ -1506,7 +1398,6 @@ module JSON::LD
|
|
1506
1398
|
tl = '@any' if list?(value) && value['@list'].empty?
|
1507
1399
|
preferred_values.concat([tl_value, '@none'].compact)
|
1508
1400
|
end
|
1509
|
-
#log_debug("") {"preferred_values: #{preferred_values.inspect}"} unless quiet
|
1510
1401
|
preferred_values << '@any'
|
1511
1402
|
|
1512
1403
|
# if containers included `@language` and preferred_values includes something of the form language-tag_direction, add just the _direction part, to select terms that have that direction.
|
@@ -1515,7 +1406,6 @@ module JSON::LD
|
|
1515
1406
|
end
|
1516
1407
|
|
1517
1408
|
if p_term = select_term(iri, containers, tl, preferred_values)
|
1518
|
-
#log_debug("") {"=> term: #{p_term.inspect}"} unless quiet
|
1519
1409
|
return p_term
|
1520
1410
|
end
|
1521
1411
|
end
|
@@ -1523,7 +1413,6 @@ module JSON::LD
|
|
1523
1413
|
# At this point, there is no simple term that iri can be compacted to. If vocab is true and active context has a vocabulary mapping:
|
1524
1414
|
if vocab && self.vocab && iri.start_with?(self.vocab) && iri.length > self.vocab.length
|
1525
1415
|
suffix = iri[self.vocab.length..-1]
|
1526
|
-
#log_debug("") {"=> vocab suffix: #{suffix.inspect}"} unless quiet
|
1527
1416
|
return suffix unless term_definitions.has_key?(suffix)
|
1528
1417
|
end
|
1529
1418
|
|
@@ -1565,11 +1454,9 @@ module JSON::LD
|
|
1565
1454
|
|
1566
1455
|
if !vocab
|
1567
1456
|
# transform iri to a relative IRI using the document's base IRI
|
1568
|
-
iri = remove_base(iri)
|
1569
|
-
#log_debug("") {"=> relative iri: #{iri.inspect}"} unless quiet
|
1457
|
+
iri = remove_base(self.base || base, iri)
|
1570
1458
|
return iri
|
1571
1459
|
else
|
1572
|
-
#log_debug("") {"=> absolute iri: #{iri.inspect}"} unless quiet
|
1573
1460
|
return iri
|
1574
1461
|
end
|
1575
1462
|
end
|
@@ -1587,26 +1474,24 @@ module JSON::LD
|
|
1587
1474
|
# Value (literal or IRI) to be expanded
|
1588
1475
|
# @param [Boolean] useNativeTypes (false) use native representations
|
1589
1476
|
# @param [Boolean] rdfDirection (nil) decode i18n datatype if i18n-datatype
|
1477
|
+
# @param [String, RDF::URI] base for resolving document-relative IRIs
|
1590
1478
|
# @param [Hash{Symbol => Object}] options
|
1591
1479
|
#
|
1592
1480
|
# @return [Hash] Object representation of value
|
1593
1481
|
# @raise [RDF::ReaderError] if the iri cannot be expanded
|
1594
1482
|
# @see https://www.w3.org/TR/json-ld11-api/#value-expansion
|
1595
|
-
def expand_value(property, value, useNativeTypes: false, rdfDirection: nil, **options)
|
1596
|
-
#log_debug("expand_value") {"property: #{property.inspect}, value: #{value.inspect}"}
|
1597
|
-
|
1483
|
+
def expand_value(property, value, useNativeTypes: false, rdfDirection: nil, base: nil, **options)
|
1598
1484
|
td = term_definitions.fetch(property, TermDefinition.new(property))
|
1599
1485
|
|
1600
1486
|
# 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.
|
1601
1487
|
if value.is_a?(String) && td.type_mapping == '@id'
|
1602
1488
|
#log_debug("") {"as relative IRI: #{value.inspect}"}
|
1603
|
-
return {'@id' => expand_iri(value, documentRelative: true).to_s}
|
1489
|
+
return {'@id' => expand_iri(value, documentRelative: true, base: base).to_s}
|
1604
1490
|
end
|
1605
1491
|
|
1606
1492
|
# 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.
|
1607
1493
|
if value.is_a?(String) && td.type_mapping == '@vocab'
|
1608
|
-
|
1609
|
-
return {'@id' => expand_iri(value, vocab: true, documentRelative: true).to_s}
|
1494
|
+
return {'@id' => expand_iri(value, vocab: true, documentRelative: true, base: base).to_s}
|
1610
1495
|
end
|
1611
1496
|
|
1612
1497
|
value = RDF::Literal(value) if
|
@@ -1616,16 +1501,14 @@ module JSON::LD
|
|
1616
1501
|
|
1617
1502
|
result = case value
|
1618
1503
|
when RDF::URI, RDF::Node
|
1619
|
-
#log_debug("URI | BNode") { value.to_s }
|
1620
1504
|
{'@id' => value.to_s}
|
1621
1505
|
when RDF::Literal
|
1622
|
-
#log_debug("Literal") {"datatype: #{value.datatype.inspect}"}
|
1623
1506
|
res = {}
|
1624
1507
|
if value.datatype == RDF::URI(RDF.to_uri + "JSON") && processingMode('json-ld-1.1')
|
1625
1508
|
# Value parsed as JSON
|
1626
1509
|
# FIXME: MultiJson
|
1627
|
-
res['@value'] = ::JSON.parse(value.object)
|
1628
1510
|
res['@type'] = '@json'
|
1511
|
+
res['@value'] = ::JSON.parse(value.object)
|
1629
1512
|
elsif value.datatype.start_with?("https://www.w3.org/ns/i18n#") && rdfDirection == 'i18n-datatype' && processingMode('json-ld-1.1')
|
1630
1513
|
lang, dir = value.datatype.fragment.split('_')
|
1631
1514
|
res['@value'] = value.to_s
|
@@ -1641,24 +1524,23 @@ module JSON::LD
|
|
1641
1524
|
end
|
1642
1525
|
res['@direction'] = dir
|
1643
1526
|
elsif useNativeTypes && RDF_LITERAL_NATIVE_TYPES.include?(value.datatype)
|
1644
|
-
res['@value'] = value.object
|
1645
1527
|
res['@type'] = uri(coerce(property)) if coerce(property)
|
1528
|
+
res['@value'] = value.object
|
1646
1529
|
else
|
1647
1530
|
value.canonicalize! if value.datatype == RDF::XSD.double
|
1648
|
-
res['@value'] = value.to_s
|
1649
1531
|
if coerce(property)
|
1650
1532
|
res['@type'] = uri(coerce(property)).to_s
|
1651
1533
|
elsif value.has_datatype?
|
1652
1534
|
res['@type'] = uri(value.datatype).to_s
|
1653
1535
|
elsif value.has_language? || language(property)
|
1654
1536
|
res['@language'] = (value.language || language(property)).to_s
|
1655
|
-
# FIXME: direction
|
1656
1537
|
end
|
1538
|
+
res['@value'] = value.to_s
|
1657
1539
|
end
|
1658
1540
|
res
|
1659
1541
|
else
|
1660
1542
|
# Otherwise, initialize result to a JSON object with an @value member whose value is set to value.
|
1661
|
-
res = {
|
1543
|
+
res = {}
|
1662
1544
|
|
1663
1545
|
if td.type_mapping && !CONTAINERS_ID_VOCAB.include?(td.type_mapping.to_s)
|
1664
1546
|
res['@type'] = td.type_mapping.to_s
|
@@ -1669,10 +1551,9 @@ module JSON::LD
|
|
1669
1551
|
res['@direction'] = direction if direction
|
1670
1552
|
end
|
1671
1553
|
|
1672
|
-
res
|
1554
|
+
res.merge('@value' => value)
|
1673
1555
|
end
|
1674
1556
|
|
1675
|
-
#log_debug("") {"=> #{result.inspect}"}
|
1676
1557
|
result
|
1677
1558
|
rescue ::JSON::ParserError => e
|
1678
1559
|
raise JSON::LD::JsonLdError::InvalidJsonLiteral, e.message
|
@@ -1685,13 +1566,13 @@ module JSON::LD
|
|
1685
1566
|
# Associated property used to find coercion rules
|
1686
1567
|
# @param [Hash] value
|
1687
1568
|
# Value (literal or IRI), in full object representation, to be compacted
|
1688
|
-
# @param
|
1569
|
+
# @param [String, RDF::URI] base for resolving document-relative IRIs
|
1689
1570
|
#
|
1690
1571
|
# @return [Hash] Object representation of value
|
1691
1572
|
# @raise [JsonLdError] if the iri cannot be expanded
|
1692
1573
|
# @see https://www.w3.org/TR/json-ld11-api/#value-compaction
|
1693
1574
|
# FIXME: revisit the specification version of this.
|
1694
|
-
def compact_value(property, value,
|
1575
|
+
def compact_value(property, value, base: nil)
|
1695
1576
|
#log_debug("compact_value") {"property: #{property.inspect}, value: #{value.inspect}"}
|
1696
1577
|
|
1697
1578
|
indexing = index?(value) && container(property).include?('@index')
|
@@ -1702,7 +1583,7 @@ module JSON::LD
|
|
1702
1583
|
when coerce(property) == '@id' && value.has_key?('@id') && (value.keys - %w(@id @index)).empty?
|
1703
1584
|
# Compact an @id coercion
|
1704
1585
|
#log_debug("") {" (@id & coerce)"}
|
1705
|
-
compact_iri(value['@id'])
|
1586
|
+
compact_iri(value['@id'], base: base)
|
1706
1587
|
when coerce(property) == '@vocab' && value.has_key?('@id') && (value.keys - %w(@id @index)).empty?
|
1707
1588
|
# Compact an @id coercion
|
1708
1589
|
#log_debug("") {" (@id & coerce & vocab)"}
|
@@ -1792,13 +1673,21 @@ module JSON::LD
|
|
1792
1673
|
v.join(" ") + "]"
|
1793
1674
|
end
|
1794
1675
|
|
1676
|
+
# Duplicate an active context, allowing it to be modified.
|
1795
1677
|
def dup
|
1796
|
-
# Also duplicate mappings, coerce and list
|
1797
1678
|
that = self
|
1798
|
-
ec =
|
1679
|
+
ec = Context.new(unfrozen: true, **@options)
|
1680
|
+
ec.context_base = that.context_base
|
1681
|
+
ec.base = that.base unless that.base.nil?
|
1682
|
+
ec.default_direction = that.default_direction
|
1683
|
+
ec.default_language = that.default_language
|
1684
|
+
ec.previous_context = that.previous_context
|
1685
|
+
ec.processingMode = that.processingMode if that.instance_variable_get(:@processingModee)
|
1686
|
+
ec.vocab = that.vocab if that.vocab
|
1687
|
+
|
1799
1688
|
ec.instance_eval do
|
1800
1689
|
@term_definitions = that.term_definitions.dup
|
1801
|
-
@iri_to_term = that.iri_to_term
|
1690
|
+
@iri_to_term = that.iri_to_term
|
1802
1691
|
end
|
1803
1692
|
ec
|
1804
1693
|
end
|
@@ -1812,7 +1701,7 @@ module JSON::LD
|
|
1812
1701
|
# @param [String] term
|
1813
1702
|
# @return [Boolean]
|
1814
1703
|
def term_valid?(term)
|
1815
|
-
term.is_a?(String)
|
1704
|
+
term.is_a?(String) && !term.empty?
|
1816
1705
|
end
|
1817
1706
|
|
1818
1707
|
# Reverse term mapping, typically used for finding aliases for keys.
|
@@ -1830,9 +1719,9 @@ module JSON::LD
|
|
1830
1719
|
|
1831
1720
|
private
|
1832
1721
|
|
1833
|
-
CONTEXT_CONTAINER_ARRAY_TERMS = %w(@set @list @graph).freeze
|
1834
|
-
CONTEXT_CONTAINER_ID_GRAPH = %w(@id @graph).freeze
|
1835
|
-
CONTEXT_CONTAINER_INDEX_GRAPH = %w(@index @graph).freeze
|
1722
|
+
CONTEXT_CONTAINER_ARRAY_TERMS = Set.new(%w(@set @list @graph)).freeze
|
1723
|
+
CONTEXT_CONTAINER_ID_GRAPH = Set.new(%w(@id @graph)).freeze
|
1724
|
+
CONTEXT_CONTAINER_INDEX_GRAPH = Set.new(%w(@index @graph)).freeze
|
1836
1725
|
CONTEXT_BASE_FRAG_OR_QUERY = %w(? #).freeze
|
1837
1726
|
CONTEXT_TYPE_ID_VOCAB = %w(@id @vocab).freeze
|
1838
1727
|
|
@@ -1844,20 +1733,11 @@ module JSON::LD
|
|
1844
1733
|
bnode(namer.get_sym($1))
|
1845
1734
|
else
|
1846
1735
|
value = RDF::URI(value)
|
1847
|
-
value.validate! if
|
1848
|
-
value.canonicalize! if @options[:canonicalize]
|
1849
|
-
value = RDF::URI.intern(value, {}) if @options[:intern]
|
1736
|
+
#value.validate! if options[:validate]
|
1850
1737
|
value
|
1851
1738
|
end
|
1852
1739
|
end
|
1853
1740
|
|
1854
|
-
# Clear the provided context, used for testing
|
1855
|
-
# @return [Context] self
|
1856
|
-
def clear_provided_context
|
1857
|
-
@provided_context = nil
|
1858
|
-
self
|
1859
|
-
end
|
1860
|
-
|
1861
1741
|
# Keep track of allocated BNodes
|
1862
1742
|
#
|
1863
1743
|
# Don't actually use the name provided, to prevent name alias issues.
|
@@ -1898,7 +1778,7 @@ module JSON::LD
|
|
1898
1778
|
# @return [Hash{String => Hash{String => String}}]
|
1899
1779
|
# @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
|
1900
1780
|
def inverse_context
|
1901
|
-
|
1781
|
+
Context.inverse_cache[self.hash] ||= begin
|
1902
1782
|
result = {}
|
1903
1783
|
default_language = (self.default_language || '@none').downcase
|
1904
1784
|
term_definitions.keys.sort do |a, b|
|
@@ -1906,7 +1786,7 @@ module JSON::LD
|
|
1906
1786
|
end.each do |term|
|
1907
1787
|
next unless td = term_definitions[term]
|
1908
1788
|
|
1909
|
-
container = td.container_mapping.join('')
|
1789
|
+
container = td.container_mapping.to_a.join('')
|
1910
1790
|
if container.empty?
|
1911
1791
|
container = td.as_set? ? %(@set) : %(@none)
|
1912
1792
|
end
|
@@ -1943,7 +1823,7 @@ module JSON::LD
|
|
1943
1823
|
lang_dir = td.direction_mapping ? "_#{td.direction_mapping}" : '@none'
|
1944
1824
|
language_map[lang_dir] ||= term
|
1945
1825
|
elsif default_direction
|
1946
|
-
language_map[
|
1826
|
+
language_map["_#{default_direction}"] ||= term
|
1947
1827
|
language_map['@none'] ||= term
|
1948
1828
|
type_map['@none'] ||= term
|
1949
1829
|
else
|
@@ -1993,10 +1873,11 @@ module JSON::LD
|
|
1993
1873
|
##
|
1994
1874
|
# Removes a base IRI from the given absolute IRI.
|
1995
1875
|
#
|
1876
|
+
# @param [String] base the base used for making `iri` relative
|
1996
1877
|
# @param [String] iri the absolute IRI
|
1997
1878
|
# @return [String]
|
1998
1879
|
# the relative IRI if relative to base, otherwise the absolute IRI.
|
1999
|
-
def remove_base(iri)
|
1880
|
+
def remove_base(base, iri)
|
2000
1881
|
return iri unless base
|
2001
1882
|
@base_and_parents ||= begin
|
2002
1883
|
u = base
|
@@ -2058,7 +1939,7 @@ module JSON::LD
|
|
2058
1939
|
"'@container' on term #{term.inspect} must be a string: #{container.inspect}"
|
2059
1940
|
end
|
2060
1941
|
|
2061
|
-
val = Array(container)
|
1942
|
+
val = Set.new(Array(container))
|
2062
1943
|
val.delete('@set') if has_set = val.include?('@set')
|
2063
1944
|
|
2064
1945
|
if val.include?('@list')
|
@@ -2088,7 +1969,7 @@ module JSON::LD
|
|
2088
1969
|
processingMode('json-ld-1.0')
|
2089
1970
|
raise JsonLdError::InvalidContainerMapping,
|
2090
1971
|
"'@container' on term #{term.inspect} using @id cannot have any values other than @set and/or @graph, found #{container.inspect}" unless
|
2091
|
-
(
|
1972
|
+
val.subset?(CONTEXT_CONTAINER_ID_GRAPH)
|
2092
1973
|
# Okay
|
2093
1974
|
elsif val.include?('@type') || val.include?('@graph')
|
2094
1975
|
raise JsonLdError::InvalidContainerMapping,
|
@@ -2106,5 +1987,232 @@ module JSON::LD
|
|
2106
1987
|
end
|
2107
1988
|
Array(container)
|
2108
1989
|
end
|
1990
|
+
|
1991
|
+
# Term Definitions specify how properties and values have to be interpreted as well as the current vocabulary mapping and the default language
|
1992
|
+
class TermDefinition
|
1993
|
+
# @return [RDF::URI] IRI map
|
1994
|
+
attr_accessor :id
|
1995
|
+
|
1996
|
+
# @return [String] term name
|
1997
|
+
attr_accessor :term
|
1998
|
+
|
1999
|
+
# @return [String] Type mapping
|
2000
|
+
attr_accessor :type_mapping
|
2001
|
+
|
2002
|
+
# Base container mapping, without @set
|
2003
|
+
# @return [Array<'@index', '@language', '@index', '@set', '@type', '@id', '@graph'>] Container mapping
|
2004
|
+
attr_reader :container_mapping
|
2005
|
+
|
2006
|
+
# @return [String] Term used for nest properties
|
2007
|
+
attr_accessor :nest
|
2008
|
+
|
2009
|
+
# Language mapping of term, `false` is used if there is an explicit language mapping for this term.
|
2010
|
+
# @return [String] Language mapping
|
2011
|
+
attr_accessor :language_mapping
|
2012
|
+
|
2013
|
+
# Direction of term, `false` is used if there is explicit direction mapping mapping for this term.
|
2014
|
+
# @return ["ltr", "rtl"] direction_mapping
|
2015
|
+
attr_accessor :direction_mapping
|
2016
|
+
|
2017
|
+
# @return [Boolean] Reverse Property
|
2018
|
+
attr_accessor :reverse_property
|
2019
|
+
|
2020
|
+
# This is a simple term definition, not an expanded term definition
|
2021
|
+
# @return [Boolean]
|
2022
|
+
attr_accessor :simple
|
2023
|
+
|
2024
|
+
# Property used for data indexing; defaults to @index
|
2025
|
+
# @return [Boolean]
|
2026
|
+
attr_accessor :index
|
2027
|
+
|
2028
|
+
# Indicate that term may be used as a prefix
|
2029
|
+
attr_writer :prefix
|
2030
|
+
|
2031
|
+
# Term-specific context
|
2032
|
+
# @return [Hash{String => Object}]
|
2033
|
+
attr_accessor :context
|
2034
|
+
|
2035
|
+
# Term is protected.
|
2036
|
+
# @return [Boolean]
|
2037
|
+
attr_writer :protected
|
2038
|
+
|
2039
|
+
# This is a simple term definition, not an expanded term definition
|
2040
|
+
# @return [Boolean] simple
|
2041
|
+
def simple?; simple; end
|
2042
|
+
|
2043
|
+
# This is an appropriate term to use as the prefix of a compact IRI
|
2044
|
+
# @return [Boolean] simple
|
2045
|
+
def prefix?; @prefix; end
|
2046
|
+
|
2047
|
+
# Create a new Term Mapping with an ID
|
2048
|
+
# @param [String] term
|
2049
|
+
# @param [String] id
|
2050
|
+
# @param [String] type_mapping Type mapping
|
2051
|
+
# @param [Set<'@index', '@language', '@index', '@set', '@type', '@id', '@graph'>] container_mapping
|
2052
|
+
# @param [String] language_mapping
|
2053
|
+
# Language mapping of term, `false` is used if there is an explicit language mapping for this term
|
2054
|
+
# @param ["ltr", "rtl"] direction_mapping
|
2055
|
+
# Direction mapping of term, `false` is used if there is an explicit direction mapping for this term
|
2056
|
+
# @param [Boolean] reverse_property
|
2057
|
+
# @param [Boolean] protected mark resulting context as protected
|
2058
|
+
# @param [String] nest term used for nest properties
|
2059
|
+
# @param [Boolean] simple
|
2060
|
+
# This is a simple term definition, not an expanded term definition
|
2061
|
+
# @param [Boolean] prefix
|
2062
|
+
# Term may be used as a prefix
|
2063
|
+
def initialize(term,
|
2064
|
+
id: nil,
|
2065
|
+
index: nil,
|
2066
|
+
type_mapping: nil,
|
2067
|
+
container_mapping: nil,
|
2068
|
+
language_mapping: nil,
|
2069
|
+
direction_mapping: nil,
|
2070
|
+
reverse_property: false,
|
2071
|
+
nest: nil,
|
2072
|
+
protected: nil,
|
2073
|
+
simple: false,
|
2074
|
+
prefix: nil,
|
2075
|
+
context: nil)
|
2076
|
+
@term = term
|
2077
|
+
@id = id.to_s unless id.nil?
|
2078
|
+
@index = index.to_s unless index.nil?
|
2079
|
+
@type_mapping = type_mapping.to_s unless type_mapping.nil?
|
2080
|
+
self.container_mapping = container_mapping
|
2081
|
+
@language_mapping = language_mapping unless language_mapping.nil?
|
2082
|
+
@direction_mapping = direction_mapping unless direction_mapping.nil?
|
2083
|
+
@reverse_property = reverse_property
|
2084
|
+
@protected = protected
|
2085
|
+
@nest = nest unless nest.nil?
|
2086
|
+
@simple = simple
|
2087
|
+
@prefix = prefix unless prefix.nil?
|
2088
|
+
@context = context unless context.nil?
|
2089
|
+
end
|
2090
|
+
|
2091
|
+
# Term is protected.
|
2092
|
+
# @return [Boolean]
|
2093
|
+
def protected?; !!@protected; end
|
2094
|
+
|
2095
|
+
# Set container mapping, from an array which may include @set
|
2096
|
+
def container_mapping=(mapping)
|
2097
|
+
mapping = case mapping
|
2098
|
+
when Set then mapping
|
2099
|
+
when Array then Set.new(mapping)
|
2100
|
+
when String then Set[mapping]
|
2101
|
+
when nil then Set.new
|
2102
|
+
else
|
2103
|
+
raise "Shouldn't happen with #{mapping.inspect}"
|
2104
|
+
end
|
2105
|
+
if @as_set = mapping.include?('@set')
|
2106
|
+
mapping = mapping.dup
|
2107
|
+
mapping.delete('@set')
|
2108
|
+
end
|
2109
|
+
@container_mapping = mapping
|
2110
|
+
@index ||= '@index' if mapping.include?('@index')
|
2111
|
+
end
|
2112
|
+
|
2113
|
+
##
|
2114
|
+
# Output Hash or String definition for this definition considering @language and @vocab
|
2115
|
+
#
|
2116
|
+
# @param [Context] context
|
2117
|
+
# @return [String, Hash{String => Array[String], String}]
|
2118
|
+
def to_context_definition(context)
|
2119
|
+
cid = if context.vocab && id.start_with?(context.vocab)
|
2120
|
+
# Nothing to return unless it's the same as the vocab
|
2121
|
+
id == context.vocab ? context.vocab : id.to_s[context.vocab.length..-1]
|
2122
|
+
else
|
2123
|
+
# Find a term to act as a prefix
|
2124
|
+
iri, prefix = context.iri_to_term.detect {|i,p| id.to_s.start_with?(i.to_s)}
|
2125
|
+
iri && iri != id ? "#{prefix}:#{id.to_s[iri.length..-1]}" : id
|
2126
|
+
end
|
2127
|
+
|
2128
|
+
if simple?
|
2129
|
+
cid.to_s unless cid == term && context.vocab
|
2130
|
+
else
|
2131
|
+
defn = {}
|
2132
|
+
defn[reverse_property ? '@reverse' : '@id'] = cid.to_s unless cid == term && !reverse_property
|
2133
|
+
if type_mapping
|
2134
|
+
defn['@type'] = if KEYWORDS.include?(type_mapping)
|
2135
|
+
type_mapping
|
2136
|
+
else
|
2137
|
+
context.compact_iri(type_mapping, vocab: true)
|
2138
|
+
end
|
2139
|
+
end
|
2140
|
+
|
2141
|
+
cm = Array(container_mapping)
|
2142
|
+
cm << "@set" if as_set? && !cm.include?("@set")
|
2143
|
+
cm = cm.first if cm.length == 1
|
2144
|
+
defn['@container'] = cm unless cm.empty?
|
2145
|
+
# Language set as false to be output as null
|
2146
|
+
defn['@language'] = (@language_mapping ? @language_mapping : nil) unless @language_mapping.nil?
|
2147
|
+
defn['@direction'] = (@direction_mapping ? @direction_mapping : nil) unless @direction_mapping.nil?
|
2148
|
+
defn['@context'] = @context if @context
|
2149
|
+
defn['@nest'] = @nest if @nest
|
2150
|
+
defn['@index'] = @index if @index
|
2151
|
+
defn['@prefix'] = @prefix unless @prefix.nil?
|
2152
|
+
defn
|
2153
|
+
end
|
2154
|
+
end
|
2155
|
+
|
2156
|
+
##
|
2157
|
+
# Turn this into a source for a new instantiation
|
2158
|
+
# FIXME: context serialization
|
2159
|
+
# @return [String]
|
2160
|
+
def to_rb
|
2161
|
+
defn = [%(TermDefinition.new\(#{term.inspect})]
|
2162
|
+
%w(id index type_mapping container_mapping language_mapping direction_mapping reverse_property nest simple prefix context protected).each do |acc|
|
2163
|
+
v = instance_variable_get("@#{acc}".to_sym)
|
2164
|
+
v = v.to_s if v.is_a?(RDF::Term)
|
2165
|
+
if acc == 'container_mapping'
|
2166
|
+
v = v.to_a
|
2167
|
+
v << '@set' if as_set?
|
2168
|
+
v = v.first if v.length <= 1
|
2169
|
+
end
|
2170
|
+
defn << "#{acc}: #{v.inspect}" if v
|
2171
|
+
end
|
2172
|
+
defn.join(', ') + ")"
|
2173
|
+
end
|
2174
|
+
|
2175
|
+
# If container mapping was defined along with @set
|
2176
|
+
# @return [Boolean]
|
2177
|
+
def as_set?; @as_set || false; end
|
2178
|
+
|
2179
|
+
# Check if term definitions are identical, modulo @protected
|
2180
|
+
# @return [Boolean]
|
2181
|
+
def ==(other)
|
2182
|
+
other.is_a?(TermDefinition) &&
|
2183
|
+
id == other.id &&
|
2184
|
+
term == other.term &&
|
2185
|
+
type_mapping == other.type_mapping &&
|
2186
|
+
container_mapping == other.container_mapping &&
|
2187
|
+
nest == other.nest &&
|
2188
|
+
language_mapping == other.language_mapping &&
|
2189
|
+
direction_mapping == other.direction_mapping &&
|
2190
|
+
reverse_property == other.reverse_property &&
|
2191
|
+
simple == other.simple &&
|
2192
|
+
index == other.index &&
|
2193
|
+
context == other.context &&
|
2194
|
+
prefix? == other.prefix? &&
|
2195
|
+
as_set? == other.as_set?
|
2196
|
+
end
|
2197
|
+
|
2198
|
+
def inspect
|
2199
|
+
v = %w([TD)
|
2200
|
+
v << "id=#{@id}"
|
2201
|
+
v << "index=#{index.inspect}" unless index.nil?
|
2202
|
+
v << "term=#{@term}"
|
2203
|
+
v << "rev" if reverse_property
|
2204
|
+
v << "container=#{container_mapping}" if container_mapping
|
2205
|
+
v << "as_set=#{as_set?.inspect}"
|
2206
|
+
v << "lang=#{language_mapping.inspect}" unless language_mapping.nil?
|
2207
|
+
v << "dir=#{direction_mapping.inspect}" unless direction_mapping.nil?
|
2208
|
+
v << "type=#{type_mapping}" unless type_mapping.nil?
|
2209
|
+
v << "nest=#{nest.inspect}" unless nest.nil?
|
2210
|
+
v << "simple=true" if @simple
|
2211
|
+
v << "protected=true" if @protected
|
2212
|
+
v << "prefix=#{@prefix.inspect}" unless @prefix.nil?
|
2213
|
+
v << "has-context" unless context.nil?
|
2214
|
+
v.join(" ") + "]"
|
2215
|
+
end
|
2216
|
+
end
|
2109
2217
|
end
|
2110
2218
|
end
|