json-ld 2.1.7 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,7 +23,7 @@ module JSON::LD
23
23
  result = case input
24
24
  when Array
25
25
  # If element is an array,
26
- is_list = context.container(active_property) == '@list'
26
+ is_list = context.container(active_property) == %w(@list)
27
27
  value = input.each_with_object([]) do |v, memo|
28
28
  # Initialize expanded item to the result of using this algorithm recursively, passing active context, active property, and item as element.
29
29
  v = expand(v, active_property, context, ordered: ordered)
@@ -175,7 +175,7 @@ module JSON::LD
175
175
  when Array
176
176
  # Framing allows an array of IRIs, and always puts values in an array
177
177
  raise JsonLdError::InvalidIdValue,
178
- "value of @id must be a string, array of string or hash if framing: #{value.inspect}" unless framing
178
+ "value of @id must be a string unless framing: #{value.inspect}" unless framing
179
179
  context.expand_iri(value, documentRelative: true, quiet: true).to_s
180
180
  value.map do |v|
181
181
  raise JsonLdError::InvalidTypeValue,
@@ -187,7 +187,7 @@ module JSON::LD
187
187
  "value of @id must be a string unless framing: #{value.inspect}" unless framing
188
188
  raise JsonLdError::InvalidTypeValue,
189
189
  "value of @id must be a an empty object for framing: #{value.inspect}" unless
190
- value.empty? && framing
190
+ value.empty?
191
191
  [{}]
192
192
  else
193
193
  raise JsonLdError::InvalidIdValue,
@@ -352,7 +352,7 @@ module JSON::LD
352
352
  term_context = context.term_definitions[key].context if context.term_definitions[key]
353
353
  active_context = term_context ? context.parse(term_context) : context
354
354
  container = active_context.container(key)
355
- expanded_value = if container == '@language' && value.is_a?(Hash)
355
+ expanded_value = if container == %w(@language) && value.is_a?(Hash)
356
356
  # Otherwise, if key's container mapping in active context is @language and value is a JSON object then value is expanded from a language map as follows:
357
357
 
358
358
  # Set multilingual array to an empty array.
@@ -375,17 +375,17 @@ module JSON::LD
375
375
  end
376
376
 
377
377
  ary
378
- elsif CONTAINER_MAPPING_INDEX_ID_TYPE.include?(container) && value.is_a?(Hash)
379
- # Otherwise, if key's container mapping in active context is @index, @id, @type, an IRI or Blank Node and value is a JSON object then value is expanded from an index map as follows:
378
+ elsif !(CONTAINER_MAPPING_INDEX_ID_TYPE & container).empty? && value.is_a?(Hash)
379
+ # Otherwise, if key's container mapping in active context contains @index, @id, @type and value is a JSON object then value is expanded from an index map as follows:
380
380
 
381
381
  # Set ary to an empty array.
382
- container, ary = container.to_s, []
382
+ ary = []
383
383
 
384
384
  # For each key-value in the object:
385
385
  keys = ordered ? value.keys.sort : value.keys
386
386
  keys.each do |k|
387
- # If container mapping in the active context is @type, and k is a term in the active context having a local context, use that context when expanding values
388
- map_context = active_context.term_definitions[k].context if container == '@type' && active_context.term_definitions[k]
387
+ # If container mapping in the active context includes @type, and k is a term in the active context having a local context, use that context when expanding values
388
+ map_context = active_context.term_definitions[k].context if container.include?('@type') && active_context.term_definitions[k]
389
389
  map_context = active_context.parse(map_context) if map_context
390
390
  map_context ||= active_context
391
391
 
@@ -393,15 +393,25 @@ module JSON::LD
393
393
  index_value = expand([value[k]].flatten, key, map_context, ordered: ordered)
394
394
  index_value.each do |item|
395
395
  case container
396
- when '@index' then item[container] ||= k
397
- when '@id'
396
+ when %w(@index) then item['@index'] ||= k
397
+ when %w(@id)
398
398
  # Expand k document relative
399
399
  expanded_k = active_context.expand_iri(k, documentRelative: true, quiet: true).to_s
400
- item[container] ||= expanded_k
401
- when '@type'
400
+ item['@id'] ||= expanded_k
401
+ when %w(@type)
402
402
  # Expand k vocabulary relative
403
403
  expanded_k = active_context.expand_iri(k, vocab: true, documentRelative: true, quiet: true).to_s
404
- item[container] = [expanded_k].concat(Array(item[container]))
404
+ item['@type'] = [expanded_k].concat(Array(item['@type']))
405
+ when %w(@graph @index), %w(@graph @id)
406
+ # Indexed graph by graph name
407
+ if !graph?(item)
408
+ item = [item] unless expanded_value.is_a?(Array)
409
+ item = {'@graph' => item}
410
+ end
411
+ expanded_k = container.include?('@index') ? k :
412
+ active_context.expand_iri(k, documentRelative: true, quiet: true).to_s
413
+ # Expand k document relative
414
+ item[container.include?('@index') ? '@index' : '@id'] ||= k
405
415
  end
406
416
 
407
417
  # Append item to expanded value.
@@ -422,12 +432,21 @@ module JSON::LD
422
432
  #log_debug {" => #{expanded_value.inspect}"}
423
433
 
424
434
  # If the container mapping associated to key in active context is @list and expanded value is not already a list object, convert expanded value to a list object by first setting it to an array containing only expanded value if it is not already an array, and then by setting it to a JSON object containing the key-value pair @list-expanded value.
425
- if active_context.container(key) == '@list' && !list?(expanded_value)
435
+ if active_context.container(key) == %w(@list) && !list?(expanded_value)
426
436
  #log_debug(" => ") { "convert #{expanded_value.inspect} to list"}
427
- expanded_value = {'@list' => [expanded_value].flatten}
437
+ expanded_value = [expanded_value] unless expanded_value.is_a?(Array)
438
+ expanded_value = {'@list' => expanded_value}
428
439
  end
429
440
  #log_debug {" => #{expanded_value.inspect}"}
430
441
 
442
+ # convert expanded value to @graph if container specifies it
443
+ # FIXME value may be a named graph, as well as a simple graph.
444
+ if active_context.container(key) == %w(@graph) && !graph?(expanded_value)
445
+ #log_debug(" => ") { "convert #{expanded_value.inspect} to list"}
446
+ expanded_value = [expanded_value] unless expanded_value.is_a?(Array)
447
+ expanded_value = {'@graph' => expanded_value}
448
+ end
449
+
431
450
  # Otherwise, if the term definition associated to key indicates that it is a reverse property
432
451
  # Spec FIXME: this is not an otherwise.
433
452
  if (td = context.term_definitions[key]) && td.reverse_property
data/lib/json/ld/utils.rb CHANGED
@@ -46,6 +46,27 @@ module JSON::LD
46
46
  end
47
47
  end
48
48
 
49
+ ##
50
+ # Is value an expaned @graph?
51
+ #
52
+ # Note: A value is a simple graph if all of these hold true:
53
+ # 1. It is an object.
54
+ # 2. It has an `@graph` key.
55
+ # 3. It may have '@context', '@id' or '@index'
56
+ #
57
+ # @param [Object] value
58
+ # @return [Boolean]
59
+ def graph?(value)
60
+ value.is_a?(Hash) && (value.keys - UTIL_GRAPH_KEYS) == ['@graph']
61
+ end
62
+ ##
63
+ # Is value a simple @graph (lacking @id)?
64
+ # @param [Object] value
65
+ # @return [Boolean]
66
+ def simple_graph?(value)
67
+ graph?(value) && !value.has_key?('@id')
68
+ end
69
+
49
70
  ##
50
71
  # Is value an expaned @list?
51
72
  #
@@ -184,6 +205,7 @@ module JSON::LD
184
205
  end
185
206
 
186
207
  private
208
+ UTIL_GRAPH_KEYS = %w(@context @id @index).freeze
187
209
 
188
210
  # Merge the last value into an array based for the specified key if hash is not null and value is not already in that array
189
211
  def merge_value(hash, key, value)
data/spec/api_spec.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # coding: utf-8
2
- $:.unshift "."
3
- require 'spec_helper'
2
+ require_relative 'spec_helper'
4
3
 
5
4
  describe JSON::LD::API do
6
5
  let(:logger) {RDF::Spec.logger}
data/spec/compact_spec.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # coding: utf-8
2
- $:.unshift "."
3
- require 'spec_helper'
2
+ require_relative 'spec_helper'
4
3
 
5
4
  describe JSON::LD::API do
6
5
  let(:logger) {RDF::Spec.logger}
@@ -744,6 +743,301 @@ describe JSON::LD::API do
744
743
  end
745
744
  end
746
745
 
746
+ context "@container: @graph" do
747
+ {
748
+ "Compacts simple graph" => {
749
+ input: %([{
750
+ "http://example.org/input": [{
751
+ "@graph": [{
752
+ "http://example.org/value": [{"@value": "x"}]
753
+ }]
754
+ }]
755
+ }]),
756
+ context: %({
757
+ "@vocab": "http://example.org/",
758
+ "input": {"@container": "@graph"}
759
+ }),
760
+ output: %({
761
+ "@context": {
762
+ "@vocab": "http://example.org/",
763
+ "input": {"@container": "@graph"}
764
+ },
765
+ "input": {
766
+ "value": "x"
767
+ }
768
+ })
769
+ },
770
+ "Compacts simple graph with @set" => {
771
+ input: %([{
772
+ "http://example.org/input": [{
773
+ "@graph": [{
774
+ "http://example.org/value": [{"@value": "x"}]
775
+ }]
776
+ }]
777
+ }]),
778
+ context: %({
779
+ "@vocab": "http://example.org/",
780
+ "input": {"@container": ["@graph", "@set"]}
781
+ }),
782
+ output: %({
783
+ "@context": {
784
+ "@vocab": "http://example.org/",
785
+ "input": {"@container": ["@graph", "@set"]}
786
+ },
787
+ "input": [{
788
+ "value": "x"
789
+ }]
790
+ })
791
+ },
792
+ "Compacts simple graph with @index" => {
793
+ input: %([{
794
+ "http://example.org/input": [{
795
+ "@graph": [{
796
+ "http://example.org/value": [{"@value": "x"}]
797
+ }],
798
+ "@index": "ndx"
799
+ }]
800
+ }]),
801
+ context: %({
802
+ "@vocab": "http://example.org/",
803
+ "input": {"@container": "@graph"}
804
+ }),
805
+ output: %({
806
+ "@context": {
807
+ "@vocab": "http://example.org/",
808
+ "input": {"@container": "@graph"}
809
+ },
810
+ "input": {
811
+ "value": "x"
812
+ }
813
+ })
814
+ },
815
+ "Does not compact graph with @id" => {
816
+ input: %([{
817
+ "http://example.org/input": [{
818
+ "@graph": [{
819
+ "http://example.org/value": [{"@value": "x"}]
820
+ }],
821
+ "@id": "http://example.org/id"
822
+ }]
823
+ }]),
824
+ context: %({
825
+ "@vocab": "http://example.org/",
826
+ "input": {"@container": "@graph"}
827
+ }),
828
+ output: %({
829
+ "@context": {
830
+ "@vocab": "http://example.org/",
831
+ "input": {"@container": "@graph"}
832
+ },
833
+ "input": {
834
+ "@id": "http://example.org/id",
835
+ "@graph": [{"value": "x"}]
836
+ }
837
+ })
838
+ },
839
+ }.each_pair do |title, params|
840
+ it(title) {run_compact({processingMode: "json-ld-1.1"}.merge(params))}
841
+ end
842
+
843
+ context "+ @index" do
844
+ {
845
+ "Compacts simple graph" => {
846
+ input: %([{
847
+ "http://example.org/input": [{
848
+ "@index": "g1",
849
+ "@graph": [{
850
+ "http://example.org/value": [{"@value": "x"}]
851
+ }]
852
+ }]
853
+ }]),
854
+ context: %({
855
+ "@vocab": "http://example.org/",
856
+ "input": {"@container": ["@graph", "@index"]}
857
+ }),
858
+ output: %({
859
+ "@context": {
860
+ "@vocab": "http://example.org/",
861
+ "input": {"@container": ["@graph", "@index"]}
862
+ },
863
+ "input": {
864
+ "g1": {"value": "x"}
865
+ }
866
+ })
867
+ },
868
+ "Compacts simple graph with @set" => {
869
+ input: %([{
870
+ "http://example.org/input": [{
871
+ "@index": "g1",
872
+ "@graph": [{
873
+ "http://example.org/value": [{"@value": "x"}]
874
+ }]
875
+ }]
876
+ }]),
877
+ context: %({
878
+ "@vocab": "http://example.org/",
879
+ "input": {"@container": ["@graph", "@index", "@set"]}
880
+ }),
881
+ output: %({
882
+ "@context": {
883
+ "@vocab": "http://example.org/",
884
+ "input": {"@container": ["@graph", "@index", "@set"]}
885
+ },
886
+ "input": {
887
+ "g1": [{"value": "x"}]
888
+ }
889
+ })
890
+ },
891
+ "Does not compact graph with @id" => {
892
+ input: %([{
893
+ "http://example.org/input": [{
894
+ "@graph": [{
895
+ "http://example.org/value": [{"@value": "x"}]
896
+ }],
897
+ "@index": "g1",
898
+ "@id": "http://example.org/id"
899
+ }]
900
+ }]),
901
+ context: %({
902
+ "@vocab": "http://example.org/",
903
+ "input": {"@container": ["@graph", "@index"]}
904
+ }),
905
+ output: %({
906
+ "@context": {
907
+ "@vocab": "http://example.org/",
908
+ "input": {"@container": ["@graph", "@index"]}
909
+ },
910
+ "input": {
911
+ "@id": "http://example.org/id",
912
+ "@index": "g1",
913
+ "@graph": [{"value": "x"}]
914
+ }
915
+ })
916
+ },
917
+ }.each_pair do |title, params|
918
+ it(title) {run_compact({processingMode: "json-ld-1.1"}.merge(params))}
919
+ end
920
+ end
921
+
922
+ context "+ @id" do
923
+ {
924
+ "Compacts simple graph" => {
925
+ input: %([{
926
+ "http://example.org/input": [{
927
+ "@graph": [{
928
+ "http://example.org/value": [{"@value": "x"}]
929
+ }]
930
+ }]
931
+ }]),
932
+ context: %({
933
+ "@vocab": "http://example.org/",
934
+ "input": {"@container": ["@graph", "@id"]}
935
+ }),
936
+ output: %({
937
+ "@context": {
938
+ "@vocab": "http://example.org/",
939
+ "input": {"@container": ["@graph", "@id"]}
940
+ },
941
+ "input": {
942
+ "_:b0": {"value": "x"}
943
+ }
944
+ })
945
+ },
946
+ "Compacts simple graph with @set" => {
947
+ input: %([{
948
+ "http://example.org/input": [{
949
+ "@graph": [{
950
+ "http://example.org/value": [{"@value": "x"}]
951
+ }]
952
+ }]
953
+ }]),
954
+ context: %({
955
+ "@vocab": "http://example.org/",
956
+ "input": {"@container": ["@graph", "@id", "@set"]}
957
+ }),
958
+ output: %({
959
+ "@context": {
960
+ "@vocab": "http://example.org/",
961
+ "input": {"@container": ["@graph", "@id", "@set"]}
962
+ },
963
+ "input": {"_:b0": [{"value": "x"}]}
964
+ })
965
+ },
966
+ "Compacts simple graph with @index" => {
967
+ input: %([{
968
+ "http://example.org/input": [{
969
+ "@graph": [{
970
+ "http://example.org/value": [{"@value": "x"}]
971
+ }],
972
+ "@index": "ndx"
973
+ }]
974
+ }]),
975
+ context: %({
976
+ "@vocab": "http://example.org/",
977
+ "input": {"@container": ["@graph", "@id"]}
978
+ }),
979
+ output: %({
980
+ "@context": {
981
+ "@vocab": "http://example.org/",
982
+ "input": {"@container": ["@graph", "@id"]}
983
+ },
984
+ "input": {
985
+ "_:b0": {"value": "x"}
986
+ }
987
+ })
988
+ },
989
+ "Compacts graph with @id" => {
990
+ input: %([{
991
+ "http://example.org/input": [{
992
+ "@graph": [{
993
+ "http://example.org/value": [{"@value": "x"}]
994
+ }],
995
+ "@id": "http://example.org/id"
996
+ }]
997
+ }]),
998
+ context: %({
999
+ "@vocab": "http://example.org/",
1000
+ "input": {"@container": ["@graph", "@id"]}
1001
+ }),
1002
+ output: %({
1003
+ "@context": {
1004
+ "@vocab": "http://example.org/",
1005
+ "input": {"@container": ["@graph", "@id"]}
1006
+ },
1007
+ "input": {
1008
+ "http://example.org/id" : {"value": "x"}
1009
+ }
1010
+ })
1011
+ },
1012
+ "Compacts graph with @id and @set" => {
1013
+ input: %([{
1014
+ "http://example.org/input": [{
1015
+ "@graph": [{
1016
+ "http://example.org/value": [{"@value": "x"}]
1017
+ }],
1018
+ "@id": "http://example.org/id"
1019
+ }]
1020
+ }]),
1021
+ context: %({
1022
+ "@vocab": "http://example.org/",
1023
+ "input": {"@container": ["@graph", "@id", "@set"]}
1024
+ }),
1025
+ output: %({
1026
+ "@context": {
1027
+ "@vocab": "http://example.org/",
1028
+ "input": {"@container": ["@graph", "@id", "@set"]}
1029
+ },
1030
+ "input": {
1031
+ "http://example.org/id" : [{"value": "x"}]
1032
+ }
1033
+ })
1034
+ },
1035
+ }.each_pair do |title, params|
1036
+ it(title) {run_compact({processingMode: "json-ld-1.1"}.merge(params))}
1037
+ end
1038
+ end
1039
+ end
1040
+
747
1041
  context "@nest" do
748
1042
  {
749
1043
  "Indexes to @nest for property with @container: @nest" => {