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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f1c1389086ba31e386a000da8030526ae1ea6e8e
4
- data.tar.gz: 499467568e4eceedea6d0617c5c1abef5416b532
3
+ metadata.gz: efe1ae3742a83e43c9d36afb4aa6c1538a7eab4d
4
+ data.tar.gz: d78e142b4f1576090ada71c7ecb7e6e002834251
5
5
  SHA512:
6
- metadata.gz: 2d5c24e2bc724dfa18413f801893b8bf2fdbd2cef0dc228f037f2b746c8dc9bf9a0de854a182467302d75f3716327b8058754615b479dc5855e2f51d695fd063
7
- data.tar.gz: 71c39dc45f62e606fb49a43ef73f7b1f05316aca43d995dbd2f5bf93f617766c6dfb10f785187843915d344c3694b4e83049dd23331dfa407651c832f9b8ed51
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.5
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(File.join(File.dirname(__FILE__), "..", 'lib')))
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,
@@ -1,4 +1,4 @@
1
- require 'open-uri'
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, nil)
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) do |http|
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
@@ -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 [String] IRI map
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
- # Output Hash or String definition for this definition
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
- cid = context.compact_iri(id)
47
- cid == term ? id : cid
65
+
66
+ cid.to_s unless cid == term && context.vocab
48
67
  else
49
68
  defn = {}
50
- cid = context.compact_iri(id)
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, :vocab => true)
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 [String]
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
- @iri_to_term[v.to_s] = k unless k.nil?
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
- # Speical case for schema.org, until they get their act together
277
- if context.to_s.start_with?('http://schema.org')
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 for #{term.inspect}"
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 #{term.inspect} must not be a keyword" if
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} is invalid"
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' to #{type.inspect}"
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' to #{type.inspect}"
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['@container'])
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.dup.each do |term, definition|
506
- ctx[term] = definition.to_context_definition(self)
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
- ## FIXME: this should go away
521
- # Retrieve term mappings
545
+ ##
546
+ # Build a context from an RDF::Vocabulary definition.
522
547
  #
523
- # @return [Array<String>]
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
- # @param [String, #to_s] term
550
+ # g = RDF::Graph.load("http://schema.org/docs/schema_org_rdfa.html")
536
551
  #
537
- # @return [RDF::URI, String]
538
- # @deprecated
539
- def mapping(term)
540
- term_definitions[term] ? term_definitions[term].id : nil
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 [RDF::URI, String]
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
- end
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
- # Retrieve term coercion
579
- #
580
- # @param [String] property in unexpanded form
640
+ # Find a term definition
581
641
  #
582
- # @return [RDF::URI, '@id']
583
- def coerce(property)
584
- # Map property, if it's not an RDF::Value
585
- # @type is always is an IRI
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 [String] property in unexpanded form
651
+ # @param [Term, #to_s] term in unexpanded form
595
652
  # @return [String]
596
- def container(property)
597
- return '@set' if property == '@graph'
598
- return property if KEYWORDS.include?(property)
599
- term_definitions[property] && term_definitions[property].container_mapping
600
- end
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 property, or the default language otherwise
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(property)
618
- lang = term_definitions[property] && term_definitions[property].language_mapping
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?(property)
626
- term_definitions[property] && term_definitions[property].reverse_property
674
+ def reverse?(term)
675
+ term = find_definition(term)
676
+ term && term.reverse_property
627
677
  end
628
678
 
629
679
  ##
630
- # Determine if `term` is a suitable term.
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 [String] term
634
- # @return [Boolean]
635
- def term_valid?(term)
636
- term.is_a?(String)
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
- private
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