json-ld 1.1.5 → 1.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -0
- data/VERSION +1 -1
- data/bin/jsonld +2 -2
- data/lib/json/ld/api.rb +9 -3
- data/lib/json/ld/context.rb +252 -117
- data/lib/json/ld/frame.rb +8 -8
- data/lib/json/ld/from_rdf.rb +8 -3
- data/lib/json/ld/writer.rb +9 -2
- data/spec/api_spec.rb +4 -4
- data/spec/compact_spec.rb +11 -11
- data/spec/context_spec.rb +274 -136
- data/spec/expand_spec.rb +15 -15
- data/spec/flatten_spec.rb +1 -1
- data/spec/format_spec.rb +6 -6
- data/spec/frame_spec.rb +54 -1
- data/spec/from_rdf_spec.rb +112 -77
- data/spec/matchers.rb +3 -3
- data/spec/reader_spec.rb +6 -6
- data/spec/resource_spec.rb +6 -6
- data/spec/to_rdf_spec.rb +4 -8
- data/spec/writer_spec.rb +1 -1
- metadata +18 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: efe1ae3742a83e43c9d36afb4aa6c1538a7eab4d
|
4
|
+
data.tar.gz: d78e142b4f1576090ada71c7ecb7e6e002834251
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 18cab81a39bdb983a404cd67e98bc26fb8d447300a9a0d7c971e068c3246bb243cbf5943e5965b3806a015c0c71c1ac489606a6beb62ee7f366d56618bf53282
|
7
|
+
data.tar.gz: 383e350f9300be24e6a6ad5e21e3c41253de440628207de0190aba959524da3d28d672965c96b5a7b03c5ec44657886229fe2c43eec1d6da9b64ef4c617f3f1d
|
data/README.md
CHANGED
@@ -9,6 +9,8 @@
|
|
9
9
|
|
10
10
|
JSON::LD parses and serializes [JSON-LD][] into [RDF][] and implements expansion, compaction and framing API interfaces.
|
11
11
|
|
12
|
+
JSON::LD can now be used to create a _context_ from an RDFS/OWL definition, and optionally include a JSON-LD representation of the ontology itself. This is currently accessed through the `script/gen_context` script.
|
13
|
+
|
12
14
|
Install with `gem install json-ld`
|
13
15
|
|
14
16
|
## Examples
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.1.
|
1
|
+
1.1.6
|
data/bin/jsonld
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'rubygems'
|
3
|
-
$:.unshift(File.expand_path(
|
3
|
+
$:.unshift(File.expand_path("../../lib", __FILE__))
|
4
4
|
begin
|
5
5
|
require 'linkeddata'
|
6
6
|
rescue LoadError
|
@@ -62,7 +62,7 @@ rescue
|
|
62
62
|
end
|
63
63
|
|
64
64
|
parser_options = {
|
65
|
-
:base =>
|
65
|
+
:base => nil,
|
66
66
|
:progress => false,
|
67
67
|
:validate => false,
|
68
68
|
:strict => false,
|
data/lib/json/ld/api.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'openssl'
|
2
2
|
require 'json/ld/expand'
|
3
3
|
require 'json/ld/compact'
|
4
4
|
require 'json/ld/flatten'
|
@@ -74,6 +74,8 @@ module JSON::LD
|
|
74
74
|
# Rename bnodes as part of expansion, or keep them the same.
|
75
75
|
# @option options [Boolean] :unique_bnodes (false)
|
76
76
|
# Use unique bnode identifiers, defaults to using the identifier which the node was originall initialized with (if any).
|
77
|
+
# @option options [Boolean] :simple_compact_iris (false)
|
78
|
+
# When compacting IRIs, do not use terms with expanded term definitions
|
77
79
|
# @yield [api]
|
78
80
|
# @yieldparam [API]
|
79
81
|
def initialize(input, context, options = {}, &block)
|
@@ -328,7 +330,7 @@ module JSON::LD
|
|
328
330
|
debug(".frame") {"node_map: #{@node_map.to_json(JSON_STATE)}"}
|
329
331
|
|
330
332
|
result = []
|
331
|
-
frame(framing_state, @node_map, (expanded_frame.first || {}), result
|
333
|
+
frame(framing_state, @node_map, (expanded_frame.first || {}), parent: result)
|
332
334
|
debug(".frame") {"after frame: #{result.to_json(JSON_STATE)}"}
|
333
335
|
|
334
336
|
# Initalize context from frame
|
@@ -468,7 +470,11 @@ module JSON::LD
|
|
468
470
|
when /^http/
|
469
471
|
parsed_url = ::URI.parse(url.to_s)
|
470
472
|
until remote_document do
|
471
|
-
Net::HTTP::start(parsed_url.host, parsed_url.port
|
473
|
+
Net::HTTP::start(parsed_url.host, parsed_url.port,
|
474
|
+
open_timeout: 60 * 1000,
|
475
|
+
use_ssl: parsed_url.scheme == 'https',
|
476
|
+
verify_mode: OpenSSL::SSL::VERIFY_NONE
|
477
|
+
) do |http|
|
472
478
|
request = Net::HTTP::Get.new(parsed_url.request_uri, options[:headers])
|
473
479
|
http.request(request) do |response|
|
474
480
|
case response
|
data/lib/json/ld/context.rb
CHANGED
@@ -8,7 +8,7 @@ module JSON::LD
|
|
8
8
|
|
9
9
|
# Term Definitions specify how properties and values have to be interpreted as well as the current vocabulary mapping and the default language
|
10
10
|
class TermDefinition
|
11
|
-
# @return [
|
11
|
+
# @return [RDF::URI] IRI map
|
12
12
|
attr_accessor :id
|
13
13
|
|
14
14
|
# @return [String] term name
|
@@ -27,6 +27,14 @@ module JSON::LD
|
|
27
27
|
# @return [Boolean] Reverse Property
|
28
28
|
attr_accessor :reverse_property
|
29
29
|
|
30
|
+
# This is a simple term definition, not an expanded term definition
|
31
|
+
# @return [Boolean] simple
|
32
|
+
attr_accessor :simple
|
33
|
+
|
34
|
+
# This is a simple term definition, not an expanded term definition
|
35
|
+
# @return [Boolean] simple
|
36
|
+
def simple?; simple; end
|
37
|
+
|
30
38
|
# Create a new Term Mapping with an ID
|
31
39
|
# @param [String] term
|
32
40
|
# @param [String] id
|
@@ -35,25 +43,35 @@ module JSON::LD
|
|
35
43
|
@id = id.to_s if id
|
36
44
|
end
|
37
45
|
|
38
|
-
|
46
|
+
##
|
47
|
+
# Output Hash or String definition for this definition considering @language and @vocab
|
48
|
+
#
|
39
49
|
# @param [Context] context
|
40
50
|
# @return [String, Hash{String => Array[String], String}]
|
41
51
|
def to_context_definition(context)
|
52
|
+
cid = if context.vocab && id.start_with?(context.vocab)
|
53
|
+
# Nothing to return unless it's the same as the vocab
|
54
|
+
id == context.vocab ? context.vocab : id.to_s[context.vocab.length..-1]
|
55
|
+
else
|
56
|
+
# Find a term to act as a prefix
|
57
|
+
iri, prefix = context.iri_to_term.detect {|i,p| id.to_s.start_with?(i.to_s)}
|
58
|
+
iri && iri != id ? "#{prefix}:#{id.to_s[iri.length..-1]}" : id
|
59
|
+
end
|
60
|
+
|
42
61
|
if language_mapping.nil? &&
|
43
62
|
container_mapping.nil? &&
|
44
63
|
type_mapping.nil? &&
|
45
64
|
reverse_property.nil?
|
46
|
-
|
47
|
-
cid == term
|
65
|
+
|
66
|
+
cid.to_s unless cid == term && context.vocab
|
48
67
|
else
|
49
68
|
defn = {}
|
50
|
-
|
51
|
-
defn[reverse_property ? '@reverse' : '@id'] = cid unless cid == term && !reverse_property
|
69
|
+
defn[reverse_property ? '@reverse' : '@id'] = cid.to_s unless cid == term && !reverse_property
|
52
70
|
if type_mapping
|
53
71
|
defn['@type'] = if KEYWORDS.include?(type_mapping)
|
54
72
|
type_mapping
|
55
73
|
else
|
56
|
-
context.compact_iri(type_mapping, :
|
74
|
+
context.compact_iri(type_mapping, vocab: true)
|
57
75
|
end
|
58
76
|
end
|
59
77
|
defn['@container'] = container_mapping if container_mapping
|
@@ -66,6 +84,7 @@ module JSON::LD
|
|
66
84
|
def inspect
|
67
85
|
v = %w([TD)
|
68
86
|
v << "id=#{@id}"
|
87
|
+
v << "term=#{@term}"
|
69
88
|
v << "rev" if reverse_property
|
70
89
|
v << "container=#{container_mapping}" if container_mapping
|
71
90
|
v << "lang=#{language_mapping.inspect}" unless language_mapping.nil?
|
@@ -107,7 +126,7 @@ module JSON::LD
|
|
107
126
|
#
|
108
127
|
# Sets the default vocabulary used for expanding terms which
|
109
128
|
# aren't otherwise absolute IRIs
|
110
|
-
# @return [
|
129
|
+
# @return [RDF::URI]
|
111
130
|
attr_reader :vocab
|
112
131
|
|
113
132
|
# @return [Hash{Symbol => Object}] Global options used in generating IRIs
|
@@ -129,6 +148,12 @@ module JSON::LD
|
|
129
148
|
# 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.
|
130
149
|
# @option options [Hash{Symbol => String}] :prefixes
|
131
150
|
# See `RDF::Reader#initialize`
|
151
|
+
# @option options [Boolean] :simple_compact_iris (false)
|
152
|
+
# When compacting IRIs, do not use terms with expanded term definitions
|
153
|
+
# @option options [String, #to_s] :vocab
|
154
|
+
# Initial value for @vocab
|
155
|
+
# @option options [String, #to_s] :language
|
156
|
+
# Initial value for @langauge
|
132
157
|
# @yield [ec]
|
133
158
|
# @yieldparam [Context]
|
134
159
|
# @return [Context]
|
@@ -151,9 +176,15 @@ module JSON::LD
|
|
151
176
|
|
152
177
|
# Load any defined prefixes
|
153
178
|
(options[:prefixes] || {}).each_pair do |k, v|
|
154
|
-
|
179
|
+
next if k.nil?
|
180
|
+
@iri_to_term[v.to_s] = k
|
181
|
+
@term_definitions[k.to_s] = TermDefinition.new(k, v.to_s)
|
182
|
+
@term_definitions[k.to_s].simple = true
|
155
183
|
end
|
156
184
|
|
185
|
+
self.vocab = options[:vocab] if options[:vocab]
|
186
|
+
self.default_language = options[:language] if options[:language]
|
187
|
+
|
157
188
|
#debug("init") {"iri_to_term: #{iri_to_term.inspect}"}
|
158
189
|
|
159
190
|
yield(self) if block_given?
|
@@ -273,18 +304,8 @@ module JSON::LD
|
|
273
304
|
rescue JsonLdError
|
274
305
|
raise
|
275
306
|
rescue Exception => e
|
276
|
-
|
277
|
-
|
278
|
-
RDF::Util::File.open_file("http://json-ld.org/contexts/schema.org.jsonld") do |f|
|
279
|
-
context = JSON.parse(f.read)['@context']
|
280
|
-
if @options[:processingMode] == "json-ld-1.0"
|
281
|
-
context_no_base.provided_context = context.dup
|
282
|
-
end
|
283
|
-
end
|
284
|
-
else
|
285
|
-
debug("parse") {"Failed to retrieve @context from remote document at #{context_no_base.context_base.inspect}: #{e.message}"}
|
286
|
-
raise JsonLdError::LoadingRemoteContextFailed, "#{context_no_base.context_base}", e.backtrace if @options[:validate]
|
287
|
-
end
|
307
|
+
debug("parse") {"Failed to retrieve @context from remote document at #{context_no_base.context_base.inspect}: #{e.message}"}
|
308
|
+
raise JsonLdError::LoadingRemoteContextFailed, "#{context_no_base.context_base}", e.backtrace
|
288
309
|
end
|
289
310
|
|
290
311
|
# 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.
|
@@ -318,14 +339,13 @@ module JSON::LD
|
|
318
339
|
end
|
319
340
|
else
|
320
341
|
# 3.3) If context is not a JSON object, an invalid local context error has been detected and processing is aborted.
|
321
|
-
raise JsonLdError::InvalidLocalContext
|
342
|
+
raise JsonLdError::InvalidLocalContext, context.inspect
|
322
343
|
end
|
323
344
|
end
|
324
345
|
end
|
325
346
|
result
|
326
347
|
end
|
327
348
|
|
328
|
-
|
329
349
|
##
|
330
350
|
# Create Term Definition
|
331
351
|
#
|
@@ -349,15 +369,15 @@ module JSON::LD
|
|
349
369
|
when nil
|
350
370
|
defined[term] = false
|
351
371
|
else
|
352
|
-
raise JsonLdError::CyclicIRIMapping, "Cyclical term dependency found
|
372
|
+
raise JsonLdError::CyclicIRIMapping, "Cyclical term dependency found: #{term.inspect}"
|
353
373
|
end
|
354
374
|
|
355
375
|
# Since keywords cannot be overridden, term must not be a keyword. Otherwise, an invalid value has been detected, which is an error.
|
356
376
|
if KEYWORDS.include?(term) && !%w(@vocab @language).include?(term)
|
357
|
-
raise JsonLdError::KeywordRedefinition, "term
|
377
|
+
raise JsonLdError::KeywordRedefinition, "term must not be a keyword: #{term.inspect}" if
|
358
378
|
@options[:validate]
|
359
379
|
elsif !term_valid?(term) && @options[:validate]
|
360
|
-
raise JsonLdError::InvalidTermDefinition, "term #{term.inspect}
|
380
|
+
raise JsonLdError::InvalidTermDefinition, "term is invalid: #{term.inspect}"
|
361
381
|
end
|
362
382
|
|
363
383
|
# Remove any existing term definition for term in active context.
|
@@ -365,6 +385,7 @@ module JSON::LD
|
|
365
385
|
|
366
386
|
# Initialize value to a the value associated with the key term in local context.
|
367
387
|
value = local_context.fetch(term, false)
|
388
|
+
simple_term = value.is_a?(String)
|
368
389
|
value = {'@id' => value} if value.is_a?(String)
|
369
390
|
|
370
391
|
case value
|
@@ -377,6 +398,7 @@ module JSON::LD
|
|
377
398
|
when Hash
|
378
399
|
debug("") {"Hash[#{term.inspect}] = #{value.inspect}"}
|
379
400
|
definition = TermDefinition.new(term)
|
401
|
+
definition.simple = simple_term
|
380
402
|
|
381
403
|
if value.has_key?('@type')
|
382
404
|
type = value['@type']
|
@@ -388,22 +410,22 @@ module JSON::LD
|
|
388
410
|
begin
|
389
411
|
expand_iri(type, :vocab => true, :documentRelative => false, :local_context => local_context, :defined => defined)
|
390
412
|
rescue JsonLdError::InvalidIRIMapping
|
391
|
-
raise JsonLdError::InvalidTypeMapping, "invalid mapping for '@type'
|
413
|
+
raise JsonLdError::InvalidTypeMapping, "invalid mapping for '@type': #{type.inspect} on term #{term.inspect}"
|
392
414
|
end
|
393
415
|
else
|
394
416
|
:error
|
395
417
|
end
|
396
418
|
unless %w(@id @vocab).include?(type) || type.is_a?(RDF::URI) && type.absolute?
|
397
|
-
raise JsonLdError::InvalidTypeMapping, "unknown mapping for '@type'
|
419
|
+
raise JsonLdError::InvalidTypeMapping, "unknown mapping for '@type': #{type.inspect} on term #{term.inspect}"
|
398
420
|
end
|
399
421
|
debug("") {"type_mapping: #{type.inspect}"}
|
400
422
|
definition.type_mapping = type
|
401
423
|
end
|
402
424
|
|
403
425
|
if value.has_key?('@reverse')
|
404
|
-
raise JsonLdError::InvalidReverseProperty, "unexpected key in #{value.inspect}" if
|
426
|
+
raise JsonLdError::InvalidReverseProperty, "unexpected key in #{value.inspect} on term #{term.inspect}" if
|
405
427
|
value.keys.any? {|k| %w(@id).include?(k)}
|
406
|
-
raise JsonLdError::InvalidIRIMapping, "expected value of @reverse to be a string" unless
|
428
|
+
raise JsonLdError::InvalidIRIMapping, "expected value of @reverse to be a string: #{value['@reverse'].inspect} on term #{term.inspect}" unless
|
407
429
|
value['@reverse'].is_a?(String)
|
408
430
|
|
409
431
|
# 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.
|
@@ -412,26 +434,26 @@ module JSON::LD
|
|
412
434
|
:documentRelative => true,
|
413
435
|
:local_context => local_context,
|
414
436
|
:defined => defined)
|
415
|
-
raise JsonLdError::InvalidIRIMapping, "non-absolute @reverse IRI: #{definition.id}" unless
|
437
|
+
raise JsonLdError::InvalidIRIMapping, "non-absolute @reverse IRI: #{definition.id} on term #{term.inspect}" unless
|
416
438
|
definition.id.is_a?(RDF::URI) && definition.id.absolute?
|
417
439
|
|
418
440
|
# If value contains an @container member, set the container mapping of definition to its value; if its value is neither @set, nor @index, nor null, an invalid reverse property error has been detected (reverse properties only support set- and index-containers) and processing is aborted.
|
419
|
-
if (container = value
|
441
|
+
if (container = value.fetch('@container', false))
|
420
442
|
raise JsonLdError::InvalidReverseProperty,
|
421
|
-
"unknown mapping for '@container' to #{container.inspect}" unless
|
443
|
+
"unknown mapping for '@container' to #{container.inspect} on term #{term.inspect}" unless
|
422
444
|
['@set', '@index', nil].include?(container)
|
423
445
|
definition.container_mapping = container
|
424
446
|
end
|
425
447
|
definition.reverse_property = true
|
426
448
|
elsif value.has_key?('@id') && value['@id'] != term
|
427
|
-
raise JsonLdError::InvalidIRIMapping, "expected value of @id to be a string" unless
|
449
|
+
raise JsonLdError::InvalidIRIMapping, "expected value of @id to be a string: #{value['@id'].inspect} on term #{term.inspect}" unless
|
428
450
|
value['@id'].is_a?(String)
|
429
451
|
definition.id = expand_iri(value['@id'],
|
430
452
|
:vocab => true,
|
431
453
|
:documentRelative => true,
|
432
454
|
:local_context => local_context,
|
433
455
|
:defined => defined)
|
434
|
-
raise JsonLdError::InvalidKeywordAlias, "expected value of @id to not be @context" if
|
456
|
+
raise JsonLdError::InvalidKeywordAlias, "expected value of @id to not be @context on term #{term.inspect}" if
|
435
457
|
definition.id == '@context'
|
436
458
|
elsif term.include?(':')
|
437
459
|
# If term is a compact IRI with a prefix that is a key in local context then a dependency has been found. Use this algorithm recursively passing active context, local context, the prefix as term, and defined.
|
@@ -448,21 +470,23 @@ module JSON::LD
|
|
448
470
|
debug("") {"=> #{definition.id}"}
|
449
471
|
else
|
450
472
|
# Otherwise, active context must have a vocabulary mapping, otherwise an invalid value has been detected, which is an error. Set the IRI mapping for definition to the result of concatenating the value associated with the vocabulary mapping and term.
|
451
|
-
raise JsonLdError::InvalidIRIMapping, "relative term definition without vocab" unless vocab
|
473
|
+
raise JsonLdError::InvalidIRIMapping, "relative term definition without vocab: #{term} on term #{term.inspect}" unless vocab
|
452
474
|
definition.id = vocab + term
|
453
475
|
debug("") {"=> #{definition.id}"}
|
454
476
|
end
|
455
477
|
|
478
|
+
@iri_to_term[definition.id] = term if simple_term && definition.id
|
479
|
+
|
456
480
|
if value.has_key?('@container')
|
457
481
|
container = value['@container']
|
458
|
-
raise JsonLdError::InvalidContainerMapping, "unknown mapping for '@container' to #{container.inspect}" unless %w(@list @set @language @index).include?(container)
|
482
|
+
raise JsonLdError::InvalidContainerMapping, "unknown mapping for '@container' to #{container.inspect} on term #{term.inspect}" unless %w(@list @set @language @index).include?(container)
|
459
483
|
debug("") {"container_mapping: #{container.inspect}"}
|
460
484
|
definition.container_mapping = container
|
461
485
|
end
|
462
486
|
|
463
487
|
if value.has_key?('@language')
|
464
488
|
language = value['@language']
|
465
|
-
raise JsonLdError::InvalidLanguageMapping, "language must be null or a string, was #{language.inspect}}" unless language.nil? || (language || "").is_a?(String)
|
489
|
+
raise JsonLdError::InvalidLanguageMapping, "language must be null or a string, was #{language.inspect}} on term #{term.inspect}" unless language.nil? || (language || "").is_a?(String)
|
466
490
|
language = language.downcase if language.is_a?(String)
|
467
491
|
debug("") {"language_mapping: #{language.inspect}"}
|
468
492
|
definition.language_mapping = language || false
|
@@ -471,7 +495,7 @@ module JSON::LD
|
|
471
495
|
term_definitions[term] = definition
|
472
496
|
defined[term] = true
|
473
497
|
else
|
474
|
-
raise JsonLdError::InvalidTermDefinition, "Term definition for #{term.inspect} is an #{value.class}"
|
498
|
+
raise JsonLdError::InvalidTermDefinition, "Term definition for #{term.inspect} is an #{value.class} on term #{term.inspect}"
|
475
499
|
end
|
476
500
|
end
|
477
501
|
|
@@ -502,8 +526,9 @@ module JSON::LD
|
|
502
526
|
ctx['@vocab'] = vocab.to_s if vocab
|
503
527
|
|
504
528
|
# Term Definitions
|
505
|
-
term_definitions.
|
506
|
-
|
529
|
+
term_definitions.keys.sort.each do |term|
|
530
|
+
defn = term_definitions[term].to_context_definition(self)
|
531
|
+
ctx[term] = defn if defn
|
507
532
|
end
|
508
533
|
|
509
534
|
debug("") {"start_doc: context=#{ctx.inspect}"}
|
@@ -517,125 +542,160 @@ module JSON::LD
|
|
517
542
|
end
|
518
543
|
end
|
519
544
|
|
520
|
-
##
|
521
|
-
#
|
545
|
+
##
|
546
|
+
# Build a context from an RDF::Vocabulary definition.
|
522
547
|
#
|
523
|
-
# @
|
524
|
-
# @deprecated
|
525
|
-
def mappings
|
526
|
-
term_definitions.inject({}) do |memo, (t,td)|
|
527
|
-
memo[t] = td ? td.id : nil
|
528
|
-
memo
|
529
|
-
end
|
530
|
-
end
|
531
|
-
|
532
|
-
## FIXME: this should go away
|
533
|
-
# Retrieve term mapping
|
548
|
+
# @example building from an external vocabulary definition
|
534
549
|
#
|
535
|
-
#
|
550
|
+
# g = RDF::Graph.load("http://schema.org/docs/schema_org_rdfa.html")
|
536
551
|
#
|
537
|
-
#
|
538
|
-
#
|
539
|
-
|
540
|
-
|
552
|
+
# context = JSON::LD::Context.new.from_vocabulary(g,
|
553
|
+
# vocab: "http://schema.org/",
|
554
|
+
# prefixes: {schema: "http://schema.org/"},
|
555
|
+
# language: "en")
|
556
|
+
#
|
557
|
+
# @param [RDF::Queryable] graph
|
558
|
+
#
|
559
|
+
# @return [self]
|
560
|
+
def from_vocabulary(graph)
|
561
|
+
statements = {}
|
562
|
+
ranges = {}
|
563
|
+
|
564
|
+
# Add term definitions for each class and property not in schema:, and
|
565
|
+
# for those properties having an object range
|
566
|
+
graph.each do |statement|
|
567
|
+
next if statement.subject.node?
|
568
|
+
(statements[statement.subject] ||= []) << statement
|
569
|
+
|
570
|
+
# Keep track of predicate ranges
|
571
|
+
if [RDF::RDFS.range, RDF::SCHEMA.rangeIncludes].include?(statement.predicate)
|
572
|
+
(ranges[statement.subject] ||= []) << statement.object
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
# Add term definitions for each class and property not in vocab, and
|
577
|
+
# for those properties having an object range
|
578
|
+
statements.each do |subject, values|
|
579
|
+
types = values.select {|v| v.predicate == RDF.type}.map(&:object)
|
580
|
+
is_property = types.any? {|t| t.to_s.include?("Property")}
|
581
|
+
|
582
|
+
term = subject.to_s.split(/[\/\#]/).last
|
583
|
+
|
584
|
+
if !is_property
|
585
|
+
# Ignore if there's a default voabulary and this is not a property
|
586
|
+
next if vocab && subject.to_s.start_with?(vocab)
|
587
|
+
|
588
|
+
# otherwise, create a term definition
|
589
|
+
td = term_definitions[term] = TermDefinition.new(term, subject.to_s)
|
590
|
+
else
|
591
|
+
prop_ranges = ranges.fetch(subject, [])
|
592
|
+
# If any range is empty or member of range includes rdfs:Literal or schema:Text
|
593
|
+
next if vocab && prop_ranges.empty? ||
|
594
|
+
prop_ranges.include?(RDF::SCHEMA.Text) ||
|
595
|
+
prop_ranges.include?(RDF::RDFS.Literal)
|
596
|
+
td = term_definitions[term] = TermDefinition.new(term, subject.to_s)
|
597
|
+
|
598
|
+
# Set context typing based on first element in range
|
599
|
+
case r = prop_ranges.first
|
600
|
+
when RDF::XSD.string
|
601
|
+
if self.default_language
|
602
|
+
td.language_mapping = false
|
603
|
+
end
|
604
|
+
when RDF::XSD.boolean, RDF::SCHEMA.Boolean, RDF::XSD.date, RDF::SCHEMA.Date,
|
605
|
+
RDF::XSD.dateTime, RDF::SCHEMA.DateTime, RDF::XSD.time, RDF::SCHEMA.Time,
|
606
|
+
RDF::XSD.duration, RDF::SCHEMA.Duration, RDF::XSD.decimal, RDF::SCHEMA.Number,
|
607
|
+
RDF::XSD.float, RDF::SCHEMA.Float, RDF::XSD.integer, RDF::SCHEMA.Integer
|
608
|
+
td.type_mapping = r
|
609
|
+
td.simple = false
|
610
|
+
else
|
611
|
+
# It's an object range (includes schema:URL)
|
612
|
+
td.type_mapping = '@id'
|
613
|
+
end
|
614
|
+
end
|
615
|
+
end
|
616
|
+
|
617
|
+
self
|
541
618
|
end
|
542
619
|
|
543
|
-
## FIXME: this should go away
|
544
620
|
# Set term mapping
|
545
621
|
#
|
546
622
|
# @param [#to_s] term
|
547
623
|
# @param [RDF::URI, String, nil] value
|
548
624
|
#
|
549
|
-
# @return [
|
550
|
-
# @deprecated
|
625
|
+
# @return [TermDefinition]
|
551
626
|
def set_mapping(term, value)
|
552
627
|
debug("") {"map #{term.inspect} to #{value.inspect}"}
|
553
628
|
term = term.to_s
|
554
629
|
term_definitions[term] = TermDefinition.new(term, value)
|
630
|
+
term_definitions[term].simple = true
|
555
631
|
|
556
632
|
term_sym = term.empty? ? "" : term.to_sym
|
557
633
|
iri_to_term.delete(term_definitions[term].id.to_s) if term_definitions[term].id.is_a?(String)
|
558
634
|
@options[:prefixes][term_sym] = value if @options.has_key?(:prefixes)
|
559
635
|
iri_to_term[value.to_s] = term
|
560
|
-
|
561
|
-
|
562
|
-
## FIXME: this should go away
|
563
|
-
# Reverse term mapping, typically used for finding aliases for keys.
|
564
|
-
#
|
565
|
-
# Returns either the original value, or a mapping for this value.
|
566
|
-
#
|
567
|
-
# @example
|
568
|
-
# {"@context": {"id": "@id"}, "@id": "foo"} => {"id": "foo"}
|
569
|
-
#
|
570
|
-
# @param [RDF::URI, String] value
|
571
|
-
# @return [String]
|
572
|
-
# @deprecated
|
573
|
-
def alias(value)
|
574
|
-
iri_to_term.fetch(value, value)
|
636
|
+
term_definitions[term]
|
575
637
|
end
|
576
638
|
|
577
639
|
##
|
578
|
-
#
|
579
|
-
#
|
580
|
-
# @param [String] property in unexpanded form
|
640
|
+
# Find a term definition
|
581
641
|
#
|
582
|
-
# @
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
return '@id' if [RDF.type, '@type'].include?(property)
|
587
|
-
term_definitions[property] && term_definitions[property].type_mapping
|
642
|
+
# @param [Term, #to_s] term in unexpanded form
|
643
|
+
# @return [Term]
|
644
|
+
def find_definition(term)
|
645
|
+
term.is_a?(TermDefinition) ? term : term_definitions[term.to_s]
|
588
646
|
end
|
589
|
-
protected :coerce
|
590
647
|
|
591
648
|
##
|
592
649
|
# Retrieve container mapping, add it if `value` is provided
|
593
650
|
#
|
594
|
-
# @param [
|
651
|
+
# @param [Term, #to_s] term in unexpanded form
|
595
652
|
# @return [String]
|
596
|
-
def container(
|
597
|
-
return '@set' if
|
598
|
-
return
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
## FIXME: this should go away
|
603
|
-
# Retrieve language mappings
|
604
|
-
#
|
605
|
-
# @return [Array<String>]
|
606
|
-
# @deprecated
|
607
|
-
def languages
|
608
|
-
term_definitions.inject({}) do |memo, (t,td)|
|
609
|
-
memo[t] = td.language_mapping
|
610
|
-
memo
|
611
|
-
end
|
653
|
+
def container(term)
|
654
|
+
return '@set' if term == '@graph'
|
655
|
+
return term if KEYWORDS.include?(term)
|
656
|
+
term = find_definition(term)
|
657
|
+
term && term.container_mapping
|
612
658
|
end
|
613
659
|
|
614
660
|
##
|
615
|
-
# Retrieve the language associated with a
|
661
|
+
# Retrieve the language associated with a term, or the default language otherwise
|
662
|
+
# @param [Term, #to_s] term in unexpanded form
|
616
663
|
# @return [String]
|
617
|
-
def language(
|
618
|
-
|
664
|
+
def language(term)
|
665
|
+
term = find_definition(term)
|
666
|
+
lang = term && term.language_mapping
|
619
667
|
lang.nil? ? @default_language : lang
|
620
668
|
end
|
621
669
|
|
622
670
|
##
|
623
671
|
# Is this a reverse term
|
672
|
+
# @param [Term, #to_s] term in unexpanded form
|
624
673
|
# @return [Boolean]
|
625
|
-
def reverse?(
|
626
|
-
|
674
|
+
def reverse?(term)
|
675
|
+
term = find_definition(term)
|
676
|
+
term && term.reverse_property
|
627
677
|
end
|
628
678
|
|
629
679
|
##
|
630
|
-
#
|
631
|
-
# Term may be any valid JSON string.
|
680
|
+
# Given a term or IRI, find a reverse term definition matching that term. If the term is already reversed, find a non-reversed version.
|
632
681
|
#
|
633
|
-
# @param [
|
634
|
-
# @return [
|
635
|
-
def
|
636
|
-
term
|
682
|
+
# @param [Term, #to_s] term
|
683
|
+
# @return [Term] related term definition
|
684
|
+
def reverse_term(term)
|
685
|
+
# Direct lookup of term
|
686
|
+
term = term_definitions[term.to_s] if term_definitions.has_key?(term.to_s) && !term.is_a?(TermDefinition)
|
687
|
+
|
688
|
+
# Lookup term, assuming term is an IRI
|
689
|
+
unless term.is_a?(TermDefinition)
|
690
|
+
td = term_definitions.values.detect {|t| t.id == term.to_s}
|
691
|
+
|
692
|
+
# Otherwise create a temporary term definition
|
693
|
+
term = td || TermDefinition.new(term.to_s, expand_iri(term, vocab:true))
|
694
|
+
end
|
695
|
+
|
696
|
+
# Now, return a term, which reverses this term
|
697
|
+
term_definitions.values.detect {|t| t.id == term.id && t.reverse_property != term.reverse_property}
|
637
698
|
end
|
638
|
-
protected :term_valid?
|
639
699
|
|
640
700
|
##
|
641
701
|
# Expand an IRI. Relative IRIs are expanded against any document base.
|
@@ -839,6 +899,10 @@ module JSON::LD
|
|
839
899
|
term_definitions.each do |term, td|
|
840
900
|
next if term.include?(":")
|
841
901
|
next if td.nil? || td.id.nil? || td.id == iri || !iri.start_with?(td.id)
|
902
|
+
|
903
|
+
# Also skip term if it was not a simple term and the :simple_compact_iris flag is true
|
904
|
+
next if @options[:simple_compact_iris] && !td.simple?
|
905
|
+
|
842
906
|
suffix = iri[td.id.length..-1]
|
843
907
|
ciri = "#{term}:#{suffix}"
|
844
908
|
candidates << ciri unless value && term_definitions.has_key?(ciri)
|
@@ -1052,7 +1116,45 @@ module JSON::LD
|
|
1052
1116
|
ec
|
1053
1117
|
end
|
1054
1118
|
|
1055
|
-
|
1119
|
+
protected
|
1120
|
+
|
1121
|
+
##
|
1122
|
+
# Retrieve term coercion
|
1123
|
+
#
|
1124
|
+
# @param [String] property in unexpanded form
|
1125
|
+
#
|
1126
|
+
# @return [RDF::URI, '@id']
|
1127
|
+
def coerce(property)
|
1128
|
+
# Map property, if it's not an RDF::Value
|
1129
|
+
# @type is always is an IRI
|
1130
|
+
return '@id' if [RDF.type, '@type'].include?(property)
|
1131
|
+
term_definitions[property] && term_definitions[property].type_mapping
|
1132
|
+
end
|
1133
|
+
|
1134
|
+
##
|
1135
|
+
# Determine if `term` is a suitable term.
|
1136
|
+
# Term may be any valid JSON string.
|
1137
|
+
#
|
1138
|
+
# @param [String] term
|
1139
|
+
# @return [Boolean]
|
1140
|
+
def term_valid?(term)
|
1141
|
+
term.is_a?(String)
|
1142
|
+
end
|
1143
|
+
|
1144
|
+
# Reverse term mapping, typically used for finding aliases for keys.
|
1145
|
+
#
|
1146
|
+
# Returns either the original value, or a mapping for this value.
|
1147
|
+
#
|
1148
|
+
# @example
|
1149
|
+
# {"@context": {"id": "@id"}, "@id": "foo"} => {"id": "foo"}
|
1150
|
+
#
|
1151
|
+
# @param [RDF::URI, String] value
|
1152
|
+
# @return [String]
|
1153
|
+
def alias(value)
|
1154
|
+
iri_to_term.fetch(value, value)
|
1155
|
+
end
|
1156
|
+
|
1157
|
+
private
|
1056
1158
|
|
1057
1159
|
def uri(value)
|
1058
1160
|
case value.to_s
|
@@ -1185,5 +1287,38 @@ module JSON::LD
|
|
1185
1287
|
end
|
1186
1288
|
iri
|
1187
1289
|
end
|
1290
|
+
|
1291
|
+
## Used for testing
|
1292
|
+
# Retrieve term mappings
|
1293
|
+
#
|
1294
|
+
# @return [Array<RDF::URI>]
|
1295
|
+
def mappings
|
1296
|
+
term_definitions.inject({}) do |memo, (t,td)|
|
1297
|
+
memo[t] = td ? td.id : nil
|
1298
|
+
memo
|
1299
|
+
end
|
1300
|
+
end
|
1301
|
+
|
1302
|
+
## Used for testing
|
1303
|
+
# Retrieve term mapping
|
1304
|
+
#
|
1305
|
+
# @param [String, #to_s] term
|
1306
|
+
#
|
1307
|
+
# @return [RDF::URI, String]
|
1308
|
+
def mapping(term)
|
1309
|
+
term_definitions[term] ? term_definitions[term].id : nil
|
1310
|
+
end
|
1311
|
+
|
1312
|
+
## Used for testing
|
1313
|
+
# Retrieve language mappings
|
1314
|
+
#
|
1315
|
+
# @return [Array<String>]
|
1316
|
+
# @deprecated
|
1317
|
+
def languages
|
1318
|
+
term_definitions.inject({}) do |memo, (t,td)|
|
1319
|
+
memo[t] = td.language_mapping
|
1320
|
+
memo
|
1321
|
+
end
|
1322
|
+
end
|
1188
1323
|
end
|
1189
1324
|
end
|