json-ld 2.2.1 → 3.0.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.
@@ -10,23 +10,23 @@ module JSON::LD
10
10
  # Generate a JSON-LD array representation from an array of `RDF::Statement`.
11
11
  # Representation is in expanded form
12
12
  #
13
- # @param [Array<RDF::Statement>, RDF::Enumerable] input
13
+ # @param [Array<RDF::Statement>, RDF::Enumerable] dataset
14
14
  # @param [Boolean] useRdfType (false)
15
15
  # If set to `true`, the JSON-LD processor will treat `rdf:type` like a normal property instead of using `@type`.
16
16
  # @param [Boolean] useNativeTypes (false) use native representations
17
17
  # @return [Array<Hash>] the JSON-LD document in normalized form
18
- def from_statements(input, useRdfType: false, useNativeTypes: false)
18
+ def from_statements(dataset, useRdfType: false, useNativeTypes: false)
19
19
  default_graph = {}
20
20
  graph_map = {'@default' => default_graph}
21
- node_usages_map = {}
21
+ referenced_once = {}
22
22
 
23
23
  value = nil
24
24
  ec = Context.new
25
25
 
26
26
  # Create a map for node to object representation
27
27
 
28
- # For each triple in input
29
- input.each do |statement|
28
+ # For each statement in dataset
29
+ dataset.each do |statement|
30
30
  #log_debug("statement") { statement.to_nquads.chomp}
31
31
 
32
32
  name = statement.graph_name ? ec.expand_iri(statement.graph_name).to_s : '@default'
@@ -53,16 +53,23 @@ module JSON::LD
53
53
 
54
54
  merge_value(node, statement.predicate.to_s, value)
55
55
 
56
- # If object is a blank node identifier or IRI, it might represent the a list node:
57
- if statement.object.resource?
56
+ # If object is a blank node identifier or rdf:nil, it might represent the a list node:
57
+ if statement.object == RDF.nil
58
58
  # Append a new JSON object consisting of three members, node, property, and value to the usages array. The node member is set to a reference to node, property to predicate, and value to a reference to value.
59
- merge_value(node_map[statement.object.to_s], :usages, {
59
+ object = node_map[statement.object.to_s]
60
+ merge_value(object, :usages, {
60
61
  node: node,
61
62
  property: statement.predicate.to_s,
62
63
  value: value
63
64
  })
64
-
65
- (node_usages_map[statement.object.to_s] ||= []) << node['@id']
65
+ elsif referenced_once.has_key?(statement.object.to_s)
66
+ referenced_once[statement.object.to_s] = false
67
+ elsif statement.object.node?
68
+ referenced_once[statement.object.to_s] = {
69
+ node: node,
70
+ property: statement.predicate.to_s,
71
+ value: value
72
+ }
66
73
  end
67
74
  end
68
75
 
@@ -78,30 +85,31 @@ module JSON::LD
78
85
  # If property equals rdf:rest, the value associated to the usages member of node has exactly 1 entry, node has a rdf:first and rdf:rest property, both of which have as value an array consisting of a single element, and node has no other members apart from an optional @type member whose value is an array with a single item equal to rdf:List, node represents a well-formed list node. Continue with the following steps:
79
86
  #log_debug("list element?") {node.to_json(JSON_STATE) rescue 'malformed json'}
80
87
  while property == RDF.rest.to_s &&
81
- Array(node_usages_map[node['@id']]).uniq.length == 1 &&
82
88
  blank_node?(node) &&
89
+ referenced_once[node['@id']] &&
83
90
  node.keys.none? {|k| !["@id", '@type', :usages, RDF.first.to_s, RDF.rest.to_s].include?(k)} &&
84
- Array(node[:usages]).length == 1 &&
85
91
  (f = node[RDF.first.to_s]).is_a?(Array) && f.length == 1 &&
86
92
  (r = node[RDF.rest.to_s]).is_a?(Array) && r.length == 1 &&
87
93
  ((t = node['@type']).nil? || t == [RDF.List.to_s])
88
94
  list << Array(node[RDF.first.to_s]).first
89
95
  list_nodes << node['@id']
90
- node_usage = Array(node[:usages]).first
96
+
97
+ # get next node, moving backwards through list
98
+ node_usage = referenced_once[node['@id']]
91
99
  node, property, head = node_usage[:node], node_usage[:property], node_usage[:value]
92
100
  end
93
101
 
94
102
  # If property equals rdf:first, i.e., the detected list is nested inside another list
95
- if property == RDF.first.to_s
96
- # and the value of the @id of node equals rdf:nil, i.e., the detected list is empty, continue with the next usage item. The rdf:nil node cannot be converted to a list object as it would result in a list of lists, which isn't supported.
97
- next if node['@id'] == RDF.nil.to_s
98
-
99
- # Otherwise, the list consists of at least one item. We preserve the head node and transform the rest of the linked list to a list object
100
- head_id = head['@id']
101
- head = graph_object[head_id]
102
- head = Array(head[RDF.rest.to_s]).first
103
- list.pop; list_nodes.pop
104
- end
103
+ #if property == RDF.first.to_s
104
+ # # and the value of the @id of node equals rdf:nil, i.e., the detected list is empty, continue with the next usage item. The rdf:nil node cannot be converted to a list object as it would result in a list of lists, which isn't supported.
105
+ # next if node['@id'] == RDF.nil.to_s
106
+ #
107
+ # # Otherwise, the list consists of at least one item. We preserve the head node and transform the rest of the linked list to a list object
108
+ # head_id = head['@id']
109
+ # head = graph_object[head_id]
110
+ # head = Array(head[RDF.rest.to_s]).first
111
+ # list.pop; list_nodes.pop
112
+ #end
105
113
 
106
114
  head.delete('@id')
107
115
  head['@list'] = list.reverse
@@ -76,7 +76,7 @@ module JSON::LD
76
76
  # @option options [Boolean] :stub (false)
77
77
  # This is a stand-in for another resource that has
78
78
  # not yet been retrieved (or created) from Mongo
79
- def initialize(node_definition, options = {})
79
+ def initialize(node_definition, **options)
80
80
  @context = options[:context]
81
81
  @clean = options.fetch(:clean, false)
82
82
  @new = options.fetch(:new, true)
@@ -37,6 +37,9 @@ module JSON::LD
37
37
  # Initialize literal as an RDF literal using value and datatype. If element has the key @language and datatype is xsd:string, then add the value associated with the @language key as the language of the object.
38
38
  language = item.fetch('@language', nil)
39
39
  return RDF::Literal.new(value, datatype: datatype, language: language)
40
+ elsif list?(item)
41
+ # If item is a list object, initialize list_results as an empty array, and object to the result of the List Conversion algorithm, passing the value associated with the @list key from item and list_results.
42
+ return parse_list(item['@list'], graph_name: graph_name, &block)
40
43
  end
41
44
 
42
45
  subject = item['@id'] ? as_resource(item['@id']) : node
@@ -64,8 +67,7 @@ module JSON::LD
64
67
  vv.each do |v|
65
68
  if list?(v)
66
69
  #log_debug("item_to_rdf") {"list: #{v.inspect}"}
67
- # If item is a list object, initialize list_results as an empty array, and object to the result of the List Conversion algorithm, passing the value associated with the @list key from item and list_results.
68
- object = parse_list(v['@list'], graph_name: graph_name, &block)
70
+ object = item_to_rdf(v, graph_name: graph_name, &block)
69
71
 
70
72
  # Append a triple composed of object, prediate, and object to results and add all triples from list_results to results.
71
73
  yield RDF::Statement(object, predicate, subject, graph_name: graph_name)
@@ -110,6 +110,14 @@ module JSON::LD
110
110
  end
111
111
  end
112
112
 
113
+ ##
114
+ # Represent as an array
115
+ # @param [Object] object
116
+ # @return [Array<Object>]
117
+ def as_array(object)
118
+ object.is_a?(Array) ? object : [object]
119
+ end
120
+
113
121
  ##
114
122
  # Compares two JSON-LD values for equality. Two JSON-LD values will be
115
123
  # considered equal if:
@@ -142,30 +150,33 @@ module JSON::LD
142
150
  # @param [Hash] subject the hash to add the value to.
143
151
  # @param [String] property the property that relates the value to the subject.
144
152
  # @param [Object] value the value to add.
145
- # @param [Hash{Symbol => Object}] options
146
- # @option options [Boolean] :property_is_array
147
- # true if the property is always (false)
148
- # an array, false if not.
149
- # @option options [Boolean] :allow_duplicate (true)
153
+ # @param [Boolean] property_is_array (false)
154
+ # true if the property is always an array, false if not.
155
+ # @param [Boolean] value_is_array (false)
156
+ # true if the value to be added should be preserved as an array (lists)
157
+ # @param [Boolean] allow_duplicate (true)
150
158
  # true to allow duplicates, false not to (uses
151
159
  # a simple shallow comparison of subject ID or value).
152
- def add_value(subject, property, value, options = {})
153
- options = {property_is_array: false, allow_duplicate: true}.merge!(options)
154
-
155
- if value.is_a?(Array)
156
- subject[property] = [] if value.empty? && options[:property_is_array]
157
- value.each {|v| add_value(subject, property, v, options)}
160
+ def add_value(subject, property, value, property_is_array: false, value_is_array: false, allow_duplicate: true)
161
+ if value_is_array
162
+ subject[property] = value
163
+ elsif value.is_a?(Array)
164
+ subject[property] = [] if value.empty? && property_is_array
165
+ value.each do |v|
166
+ add_value(subject, property, v,
167
+ property_is_array: property_is_array, allow_duplicate: allow_duplicate)
168
+ end
158
169
  elsif subject[property]
159
170
  # check if subject already has value if duplicates not allowed
160
- _has_value = !options[:allow_duplicate] && has_value(subject, property, value)
171
+ _has_value = !allow_duplicate && has_value(subject, property, value)
161
172
 
162
173
  # make property an array if value not present or always an array
163
- if !subject[property].is_a?(Array) && (!_has_value || options[:property_is_array])
174
+ if !subject[property].is_a?(Array) && (!_has_value || property_is_array)
164
175
  subject[property] = [subject[property]]
165
176
  end
166
177
  subject[property] << value unless _has_value
167
178
  else
168
- subject[property] = options[:property_is_array] ? [value] : value
179
+ subject[property] = property_is_array ? [value] : value
169
180
  end
170
181
  end
171
182
 
@@ -219,27 +230,6 @@ module JSON::LD
219
230
  values << value
220
231
  end
221
232
  end
222
-
223
- # Merge values into compacted results, creating arrays if necessary
224
- def merge_compacted_value(hash, key, value)
225
- return unless hash
226
- case hash[key]
227
- when nil then hash[key] = value
228
- when Array
229
- if value.is_a?(Array)
230
- hash[key].concat(value)
231
- else
232
- hash[key] << value
233
- end
234
- else
235
- hash[key] = [hash[key]]
236
- if value.is_a?(Array)
237
- hash[key].concat(value)
238
- else
239
- hash[key] << value
240
- end
241
- end
242
- end
243
233
  end
244
234
 
245
235
  ##
@@ -90,12 +90,36 @@ module JSON::LD
90
90
  control: :url2,
91
91
  on: ["--context CONTEXT"],
92
92
  description: "Context to use when compacting.") {|arg| RDF::URI(arg)},
93
+ RDF::CLI::Option.new(
94
+ symbol: :embed,
95
+ datatype: %w(@always @last @never),
96
+ control: :select,
97
+ on: ["--embed EMBED"],
98
+ description: "How to embed matched objects (@last).") {|arg| RDF::URI(arg)},
99
+ RDF::CLI::Option.new(
100
+ symbol: :explicit,
101
+ datatype: TrueClass,
102
+ control: :checkbox,
103
+ on: ["--explicit"],
104
+ description: "Only include explicitly declared properties in output (false)") {|arg| RDF::URI(arg)},
105
+ RDF::CLI::Option.new(
106
+ symbol: :omitDefault,
107
+ datatype: TrueClass,
108
+ control: :checkbox,
109
+ on: ["--omitDefault"],
110
+ description: "Omit missing properties from output (false)") {|arg| RDF::URI(arg)},
93
111
  RDF::CLI::Option.new(
94
112
  symbol: :processing_mode,
95
113
  datatype: %w(json-ld-1.0 json-ld-1.1),
96
114
  control: :radio,
97
115
  on: ["--processingMode MODE", %w(json-ld-1.0 json-ld-1.1)],
98
116
  description: "Set Processing Mode (json-ld-1.0 or json-ld-1.1)"),
117
+ RDF::CLI::Option.new(
118
+ symbol: :requireAll,
119
+ datatype: TrueClass,
120
+ control: :checkbox,
121
+ on: ["--requireAll"],
122
+ description: "Require all properties to match (true)") {|arg| RDF::URI(arg)},
99
123
  RDF::CLI::Option.new(
100
124
  symbol: :stream,
101
125
  datatype: TrueClass,
@@ -139,7 +163,7 @@ module JSON::LD
139
163
  # @yieldreturn [void]
140
164
  # @yield [writer]
141
165
  # @yieldparam [RDF::Writer] writer
142
- def initialize(output = $stdout, options = {}, &block)
166
+ def initialize(output = $stdout, **options, &block)
143
167
  options[:base_uri] ||= options[:base] if options.has_key?(:base)
144
168
  options[:base] ||= options[:base_uri] if options.has_key?(:base_uri)
145
169
  super do
@@ -3,6 +3,7 @@ require_relative 'spec_helper'
3
3
 
4
4
  describe JSON::LD::API do
5
5
  let(:logger) {RDF::Spec.logger}
6
+ before {JSON::LD::Context::PRELOADED.clear}
6
7
 
7
8
  describe "#initialize" do
8
9
  context "with string input" do
@@ -292,7 +292,47 @@ describe JSON::LD::API do
292
292
  },
293
293
  "http://example.org/vocab#contains": "this-is-not-an-IRI"
294
294
  })
295
- }
295
+ },
296
+ "Language map term with language value" => {
297
+ input: %([{"http://example/t": {"@value": "foo", "@language": "en"}}]),
298
+ context: %({"t": {"@id": "http://example/t", "@container": "@language"}}),
299
+ output: %({
300
+ "@context": {
301
+ "t": {"@id": "http://example/t", "@container": "@language"}
302
+ },
303
+ "t": {"en": "foo"}
304
+ })
305
+ },
306
+ "Datatyped term with datatyped value" => {
307
+ input: %([{"http://example/t": {"@value": "foo", "@type": "http:/example/type"}}]),
308
+ context: %({"t": {"@id": "http://example/t", "@type": "http:/example/type"}}),
309
+ output: %({
310
+ "@context": {
311
+ "t": {"@id": "http://example/t", "@type": "http:/example/type"}
312
+ },
313
+ "t": "foo"
314
+ })
315
+ },
316
+ "Datatyped term with simple value" => {
317
+ input: %([{"http://example/t": {"@value": "foo"}}]),
318
+ context: %({"t": {"@id": "http://example/t", "@type": "http:/example/type"}}),
319
+ output: %({
320
+ "@context": {
321
+ "t": {"@id": "http://example/t", "@type": "http:/example/type"}
322
+ },
323
+ "http://example/t": "foo"
324
+ })
325
+ },
326
+ "Datatyped term with object value" => {
327
+ input: %([{"http://example/t": {"@id": "http://example/id"}}]),
328
+ context: %({"t": {"@id": "http://example/t", "@type": "http:/example/type"}}),
329
+ output: %({
330
+ "@context": {
331
+ "t": {"@id": "http://example/t", "@type": "http:/example/type"}
332
+ },
333
+ "http://example/t": {"@id": "http://example/id"}
334
+ })
335
+ },
296
336
  }.each_pair do |title, params|
297
337
  it(title) {run_compact(params)}
298
338
  end
@@ -487,6 +527,72 @@ describe JSON::LD::API do
487
527
  "foo_de": ["de"]
488
528
  })
489
529
  },
530
+ "coerced @list containing an empty list" => {
531
+ input: %([{
532
+ "http://example.com/foo": [{"@list": [{"@list": []}]}]
533
+ }]),
534
+ context: %({
535
+ "foo": {"@id": "http://example.com/foo", "@container": "@list"}
536
+ }),
537
+ output: %({
538
+ "@context": {"foo": {"@id": "http://example.com/foo", "@container": "@list"}},
539
+ "foo": [[]]
540
+ }),
541
+ },
542
+ "coerced @list containing a list" => {
543
+ input: %([{
544
+ "http://example.com/foo": [{"@list": [{"@list": [{"@value": "baz"}]}]}]
545
+ }]),
546
+ context: %({
547
+ "foo": {"@id": "http://example.com/foo", "@container": "@list"}
548
+ }),
549
+ output: %({
550
+ "@context": {"foo": {"@id": "http://example.com/foo", "@container": "@list"}},
551
+ "foo": [["baz"]]
552
+ }),
553
+ },
554
+ "coerced @list containing an deep list" => {
555
+ input: %([{
556
+ "http://example.com/foo": [{"@list": [{"@list": [{"@list": [{"@value": "baz"}]}]}]}]
557
+ }]),
558
+ context: %({
559
+ "foo": {"@id": "http://example.com/foo", "@container": "@list"}
560
+ }),
561
+ output: %({
562
+ "@context": {"foo": {"@id": "http://example.com/foo", "@container": "@list"}},
563
+ "foo": [[["baz"]]]
564
+ }),
565
+ },
566
+ "coerced @list containing multiple lists" => {
567
+ input: %([{
568
+ "http://example.com/foo": [{"@list": [
569
+ {"@list": [{"@value": "a"}]},
570
+ {"@list": [{"@value": "b"}]}
571
+ ]}]
572
+ }]),
573
+ context: %({
574
+ "foo": {"@id": "http://example.com/foo", "@container": "@list"}
575
+ }),
576
+ output: %({
577
+ "@context": {"foo": {"@id": "http://example.com/foo", "@container": "@list"}},
578
+ "foo": [["a"], ["b"]]
579
+ }),
580
+ },
581
+ "coerced @list containing mixed list values" => {
582
+ input: %([{
583
+ "http://example.com/foo": [{"@list": [
584
+ {"@list": [{"@value": "a"}]},
585
+ {"@value": "b"}
586
+ ]}]
587
+ }]),
588
+ context: %({
589
+ "foo": {"@id": "http://example.com/foo", "@container": "@list"}
590
+ }),
591
+ output: %({
592
+ "@context": {"foo": {"@id": "http://example.com/foo", "@container": "@list"}},
593
+ "foo": [["a"], "b"]
594
+ }),
595
+ },
490
596
  }.each_pair do |title, params|
491
597
  it(title) {run_compact(params)}
492
598
  end
@@ -526,6 +632,96 @@ describe JSON::LD::API do
526
632
  }
527
633
  })
528
634
  },
635
+ "simple map with @none node definition" => {
636
+ input: %([{
637
+ "@id": "http://example.com/article",
638
+ "http://example.com/vocab/author": [{
639
+ "@id": "http://example.org/person/1",
640
+ "@index": "regular"
641
+ }, {
642
+ "@id": "http://example.org/guest/cd24f329aa"
643
+ }]
644
+ }]),
645
+ context: %({
646
+ "author": {"@id": "http://example.com/vocab/author", "@container": "@index" }
647
+ }),
648
+ output: %({
649
+ "@context": {
650
+ "author": {
651
+ "@id": "http://example.com/vocab/author",
652
+ "@container": "@index"
653
+ }
654
+ },
655
+ "@id": "http://example.com/article",
656
+ "author": {
657
+ "regular": {
658
+ "@id": "http://example.org/person/1"
659
+ },
660
+ "@none": {
661
+ "@id": "http://example.org/guest/cd24f329aa"
662
+ }
663
+ }
664
+ }),
665
+ processingMode: 'json-ld-1.1'
666
+ },
667
+ "simple map with @none value" => {
668
+ input: %([{
669
+ "@id": "http://example.com/article",
670
+ "http://example.com/vocab/author": [{
671
+ "@value": "Gregg",
672
+ "@index": "regular"
673
+ }, {
674
+ "@value": "Manu"
675
+ }]
676
+ }]),
677
+ context: %({
678
+ "author": {"@id": "http://example.com/vocab/author", "@container": "@index" }
679
+ }),
680
+ output: %({
681
+ "@context": {
682
+ "author": {
683
+ "@id": "http://example.com/vocab/author",
684
+ "@container": "@index"
685
+ }
686
+ },
687
+ "@id": "http://example.com/article",
688
+ "author": {
689
+ "regular": "Gregg",
690
+ "@none": "Manu"
691
+ }
692
+ }),
693
+ processingMode: 'json-ld-1.1'
694
+ },
695
+ "simple map with @none value using alias of @none" => {
696
+ input: %([{
697
+ "@id": "http://example.com/article",
698
+ "http://example.com/vocab/author": [{
699
+ "@value": "Gregg",
700
+ "@index": "regular"
701
+ }, {
702
+ "@value": "Manu"
703
+ }]
704
+ }]),
705
+ context: %({
706
+ "author": {"@id": "http://example.com/vocab/author", "@container": "@index" },
707
+ "none": "@none"
708
+ }),
709
+ output: %({
710
+ "@context": {
711
+ "author": {
712
+ "@id": "http://example.com/vocab/author",
713
+ "@container": "@index"
714
+ },
715
+ "none": "@none"
716
+ },
717
+ "@id": "http://example.com/article",
718
+ "author": {
719
+ "regular": "Gregg",
720
+ "none": "Manu"
721
+ }
722
+ }),
723
+ processingMode: 'json-ld-1.1'
724
+ },
529
725
  }.each_pair do |title, params|
530
726
  it(title) {run_compact(params)}
531
727
  end
@@ -560,6 +756,66 @@ describe JSON::LD::API do
560
756
  }
561
757
  })
562
758
  },
759
+ "with no @language" => {
760
+ input: %([
761
+ {
762
+ "@id": "http://example.com/queen",
763
+ "http://example.com/vocab/label": [
764
+ {"@value": "The Queen", "@language": "en"},
765
+ {"@value": "Die Königin", "@language": "de"},
766
+ {"@value": "Ihre Majestät"}
767
+ ]
768
+ }
769
+ ]),
770
+ context: %({
771
+ "vocab": "http://example.com/vocab/",
772
+ "label": {"@id": "vocab:label", "@container": "@language"}
773
+ }),
774
+ output: %({
775
+ "@context": {
776
+ "vocab": "http://example.com/vocab/",
777
+ "label": {"@id": "vocab:label", "@container": "@language"}
778
+ },
779
+ "@id": "http://example.com/queen",
780
+ "label": {
781
+ "en": "The Queen",
782
+ "de": "Die Königin",
783
+ "@none": "Ihre Majestät"
784
+ }
785
+ }),
786
+ processingMode: "json-ld-1.1"
787
+ },
788
+ "with no @language using alias of @none" => {
789
+ input: %([
790
+ {
791
+ "@id": "http://example.com/queen",
792
+ "http://example.com/vocab/label": [
793
+ {"@value": "The Queen", "@language": "en"},
794
+ {"@value": "Die Königin", "@language": "de"},
795
+ {"@value": "Ihre Majestät"}
796
+ ]
797
+ }
798
+ ]),
799
+ context: %({
800
+ "vocab": "http://example.com/vocab/",
801
+ "label": {"@id": "vocab:label", "@container": "@language"},
802
+ "none": "@none"
803
+ }),
804
+ output: %({
805
+ "@context": {
806
+ "vocab": "http://example.com/vocab/",
807
+ "label": {"@id": "vocab:label", "@container": "@language"},
808
+ "none": "@none"
809
+ },
810
+ "@id": "http://example.com/queen",
811
+ "label": {
812
+ "en": "The Queen",
813
+ "de": "Die Königin",
814
+ "none": "Ihre Majestät"
815
+ }
816
+ }),
817
+ processingMode: "json-ld-1.1"
818
+ },
563
819
  }.each_pair do |title, params|
564
820
  it(title) {run_compact(params)}
565
821
  end
@@ -633,6 +889,52 @@ describe JSON::LD::API do
633
889
  }
634
890
  })
635
891
  },
892
+ "Indexes using @none" => {
893
+ input: %([{
894
+ "http://example/idmap": [
895
+ {"http://example/label": [{"@value": "Object with no @id"}]}
896
+ ]
897
+ }]),
898
+ context: %({
899
+ "@vocab": "http://example/",
900
+ "ex": "http://example.org/",
901
+ "idmap": {"@container": "@id"}
902
+ }),
903
+ output: %({
904
+ "@context": {
905
+ "@vocab": "http://example/",
906
+ "ex": "http://example.org/",
907
+ "idmap": {"@container": "@id"}
908
+ },
909
+ "idmap": {
910
+ "@none": {"label": "Object with no @id"}
911
+ }
912
+ })
913
+ },
914
+ "Indexes using @none with alias" => {
915
+ input: %([{
916
+ "http://example/idmap": [
917
+ {"http://example/label": [{"@value": "Object with no @id"}]}
918
+ ]
919
+ }]),
920
+ context: %({
921
+ "@vocab": "http://example/",
922
+ "ex": "http://example.org/",
923
+ "idmap": {"@container": "@id"},
924
+ "none": "@none"
925
+ }),
926
+ output: %({
927
+ "@context": {
928
+ "@vocab": "http://example/",
929
+ "ex": "http://example.org/",
930
+ "idmap": {"@container": "@id"},
931
+ "none": "@none"
932
+ },
933
+ "idmap": {
934
+ "none": {"label": "Object with no @id"}
935
+ }
936
+ })
937
+ },
636
938
  }.each_pair do |title, params|
637
939
  it(title) {run_compact({processingMode: "json-ld-1.1"}.merge(params))}
638
940
  end
@@ -738,6 +1040,52 @@ describe JSON::LD::API do
738
1040
  }
739
1041
  })
740
1042
  },
1043
+ "Indexes using @none" => {
1044
+ input: %([{
1045
+ "http://example/typemap": [
1046
+ {"http://example/label": [{"@value": "Object with no @type"}]}
1047
+ ]
1048
+ }]),
1049
+ context: %({
1050
+ "@vocab": "http://example/",
1051
+ "ex": "http://example.org/",
1052
+ "typemap": {"@container": "@type"}
1053
+ }),
1054
+ output: %({
1055
+ "@context": {
1056
+ "@vocab": "http://example/",
1057
+ "ex": "http://example.org/",
1058
+ "typemap": {"@container": "@type"}
1059
+ },
1060
+ "typemap": {
1061
+ "@none": {"label": "Object with no @type"}
1062
+ }
1063
+ })
1064
+ },
1065
+ "Indexes using @none with alias" => {
1066
+ input: %([{
1067
+ "http://example/typemap": [
1068
+ {"http://example/label": [{"@value": "Object with no @id"}]}
1069
+ ]
1070
+ }]),
1071
+ context: %({
1072
+ "@vocab": "http://example/",
1073
+ "ex": "http://example.org/",
1074
+ "typemap": {"@container": "@type"},
1075
+ "none": "@none"
1076
+ }),
1077
+ output: %({
1078
+ "@context": {
1079
+ "@vocab": "http://example/",
1080
+ "ex": "http://example.org/",
1081
+ "typemap": {"@container": "@type"},
1082
+ "none": "@none"
1083
+ },
1084
+ "typemap": {
1085
+ "none": {"label": "Object with no @id"}
1086
+ }
1087
+ })
1088
+ },
741
1089
  }.each_pair do |title, params|
742
1090
  it(title) {run_compact({processingMode: "json-ld-1.1"}.merge(params))}
743
1091
  end
@@ -832,10 +1180,57 @@ describe JSON::LD::API do
832
1180
  },
833
1181
  "input": {
834
1182
  "@id": "http://example.org/id",
835
- "@graph": [{"value": "x"}]
1183
+ "@graph": {"value": "x"}
836
1184
  }
837
1185
  })
838
1186
  },
1187
+ "Odd framing test" => {
1188
+ input: %([
1189
+ {
1190
+ "http://example.org/claim": [
1191
+ {
1192
+ "@graph": [
1193
+ {
1194
+ "@id": "http://example.org/1",
1195
+ "https://example.com#test": [
1196
+ {
1197
+ "@value": "foo"
1198
+ }
1199
+ ]
1200
+ }
1201
+ ]
1202
+ }
1203
+ ]
1204
+ }
1205
+ ]
1206
+ ),
1207
+ context: %( {
1208
+ "@version": 1.1,
1209
+ "@vocab": "https://example.com#",
1210
+ "ex": "http://example.org/",
1211
+ "claim": {
1212
+ "@id": "ex:claim",
1213
+ "@container": "@graph"
1214
+ },
1215
+ "id": "@id"
1216
+ }),
1217
+ output: %({
1218
+ "@context": {
1219
+ "@version": 1.1,
1220
+ "@vocab": "https://example.com#",
1221
+ "ex": "http://example.org/",
1222
+ "claim": {
1223
+ "@id": "ex:claim",
1224
+ "@container": "@graph"
1225
+ },
1226
+ "id": "@id"
1227
+ },
1228
+ "claim": {
1229
+ "id": "ex:1",
1230
+ "test": "foo"
1231
+ }
1232
+ })
1233
+ }
839
1234
  }.each_pair do |title, params|
840
1235
  it(title) {run_compact({processingMode: "json-ld-1.1"}.merge(params))}
841
1236
  end
@@ -888,6 +1283,28 @@ describe JSON::LD::API do
888
1283
  }
889
1284
  })
890
1285
  },
1286
+ "Compacts simple graph with no @index" => {
1287
+ input: %([{
1288
+ "http://example.org/input": [{
1289
+ "@graph": [{
1290
+ "http://example.org/value": [{"@value": "x"}]
1291
+ }]
1292
+ }]
1293
+ }]),
1294
+ context: %({
1295
+ "@vocab": "http://example.org/",
1296
+ "input": {"@container": ["@graph", "@index", "@set"]}
1297
+ }),
1298
+ output: %({
1299
+ "@context": {
1300
+ "@vocab": "http://example.org/",
1301
+ "input": {"@container": ["@graph", "@index", "@set"]}
1302
+ },
1303
+ "input": {
1304
+ "@none": [{"value": "x"}]
1305
+ }
1306
+ })
1307
+ },
891
1308
  "Does not compact graph with @id" => {
892
1309
  input: %([{
893
1310
  "http://example.org/input": [{
@@ -910,7 +1327,7 @@ describe JSON::LD::API do
910
1327
  "input": {
911
1328
  "@id": "http://example.org/id",
912
1329
  "@index": "g1",
913
- "@graph": [{"value": "x"}]
1330
+ "@graph": {"value": "x"}
914
1331
  }
915
1332
  })
916
1333
  },
@@ -939,7 +1356,7 @@ describe JSON::LD::API do
939
1356
  "input": {"@container": ["@graph", "@id"]}
940
1357
  },
941
1358
  "input": {
942
- "_:b0": {"value": "x"}
1359
+ "@none": {"value": "x"}
943
1360
  }
944
1361
  })
945
1362
  },
@@ -960,7 +1377,7 @@ describe JSON::LD::API do
960
1377
  "@vocab": "http://example.org/",
961
1378
  "input": {"@container": ["@graph", "@id", "@set"]}
962
1379
  },
963
- "input": {"_:b0": [{"value": "x"}]}
1380
+ "input": {"@none": [{"value": "x"}]}
964
1381
  })
965
1382
  },
966
1383
  "Compacts simple graph with @index" => {
@@ -982,7 +1399,7 @@ describe JSON::LD::API do
982
1399
  "input": {"@container": ["@graph", "@id"]}
983
1400
  },
984
1401
  "input": {
985
- "_:b0": {"value": "x"}
1402
+ "@none": {"value": "x"}
986
1403
  }
987
1404
  })
988
1405
  },
@@ -1032,6 +1449,52 @@ describe JSON::LD::API do
1032
1449
  }
1033
1450
  })
1034
1451
  },
1452
+ "Compacts graph without @id" => {
1453
+ input: %([{
1454
+ "http://example.org/input": [{
1455
+ "@graph": [{
1456
+ "http://example.org/value": [{"@value": "x"}]
1457
+ }]
1458
+ }]
1459
+ }]),
1460
+ context: %({
1461
+ "@vocab": "http://example.org/",
1462
+ "input": {"@container": ["@graph", "@id"]}
1463
+ }),
1464
+ output: %({
1465
+ "@context": {
1466
+ "@vocab": "http://example.org/",
1467
+ "input": {"@container": ["@graph", "@id"]}
1468
+ },
1469
+ "input": {
1470
+ "@none" : {"value": "x"}
1471
+ }
1472
+ })
1473
+ },
1474
+ "Compacts graph without @id using alias of @none" => {
1475
+ input: %([{
1476
+ "http://example.org/input": [{
1477
+ "@graph": [{
1478
+ "http://example.org/value": [{"@value": "x"}]
1479
+ }]
1480
+ }]
1481
+ }]),
1482
+ context: %({
1483
+ "@vocab": "http://example.org/",
1484
+ "input": {"@container": ["@graph", "@id"]},
1485
+ "none": "@none"
1486
+ }),
1487
+ output: %({
1488
+ "@context": {
1489
+ "@vocab": "http://example.org/",
1490
+ "input": {"@container": ["@graph", "@id"]},
1491
+ "none": "@none"
1492
+ },
1493
+ "input": {
1494
+ "none" : {"value": "x"}
1495
+ }
1496
+ })
1497
+ },
1035
1498
  }.each_pair do |title, params|
1036
1499
  it(title) {run_compact({processingMode: "json-ld-1.1"}.merge(params))}
1037
1500
  end
@@ -1040,7 +1503,7 @@ describe JSON::LD::API do
1040
1503
 
1041
1504
  context "@nest" do
1042
1505
  {
1043
- "Indexes to @nest for property with @container: @nest" => {
1506
+ "Indexes to @nest for property with @nest" => {
1044
1507
  input: %([{
1045
1508
  "http://example.org/p1": [{"@value": "v1"}],
1046
1509
  "http://example.org/p2": [{"@value": "v2"}]
@@ -1060,7 +1523,7 @@ describe JSON::LD::API do
1060
1523
  }
1061
1524
  })
1062
1525
  },
1063
- "Indexes to @nest for all properties with @container: @nest" => {
1526
+ "Indexes to @nest for all properties with @nest" => {
1064
1527
  input: %([{
1065
1528
  "http://example.org/p1": [{"@value": "v1"}],
1066
1529
  "http://example.org/p2": [{"@value": "v2"}]
@@ -1566,6 +2029,28 @@ describe JSON::LD::API do
1566
2029
  "c": "C in example"
1567
2030
  }),
1568
2031
  },
2032
+ "orders lexicographically" => {
2033
+ input: %([{
2034
+ "@type": ["http://example/t2", "http://example/t1"],
2035
+ "http://example.org/foo": [
2036
+ {"@id": "urn:bar"}
2037
+ ]
2038
+ }]),
2039
+ context: %({
2040
+ "@vocab": "http://example/",
2041
+ "t1": {"@context": {"foo": {"@id": "http://example.com/foo"}}},
2042
+ "t2": {"@context": {"foo": {"@id": "http://example.org/foo", "@type": "@id"}}}
2043
+ }),
2044
+ output: %({
2045
+ "@context": {
2046
+ "@vocab": "http://example/",
2047
+ "t1": {"@context": {"foo": {"@id": "http://example.com/foo"}}},
2048
+ "t2": {"@context": {"foo": {"@id": "http://example.org/foo", "@type": "@id"}}}
2049
+ },
2050
+ "@type": ["t2", "t1"],
2051
+ "foo": "urn:bar"
2052
+ }),
2053
+ },
1569
2054
  "with @container: @type" => {
1570
2055
  input: %([{
1571
2056
  "http://example/typemap": [
@@ -1605,41 +2090,53 @@ describe JSON::LD::API do
1605
2090
  validate: true,
1606
2091
  exception: JSON::LD::JsonLdError::InvalidTermDefinition
1607
2092
  },
1608
- }.each_pair do |title, params|
1609
- it(title) {run_compact({processingMode: "json-ld-1.1"}.merge(params))}
1610
- end
1611
- end
1612
-
1613
- context "exceptions" do
1614
- {
1615
- "@list containing @list" => {
1616
- input: %({
1617
- "http://example.org/foo": {"@list": [{"@list": ["baz"]}]}
2093
+ "applies context for all values" => {
2094
+ input: %([
2095
+ {
2096
+ "@id": "http://example.org/id",
2097
+ "@type": ["http://example/type"],
2098
+ "http://example/a": [{
2099
+ "@id": "http://example.org/Foo",
2100
+ "@type": ["http://example/Foo"],
2101
+ "http://example/bar": [{"@id": "http://example.org/baz"}]
2102
+ }]
2103
+ }
2104
+ ]),
2105
+ context: %({
2106
+ "@vocab": "http://example/",
2107
+ "id": "@id",
2108
+ "type": "@type",
2109
+ "Foo": {"@context": {"id": null, "type": null}}
1618
2110
  }),
1619
- context: {},
1620
- exception: JSON::LD::JsonLdError::ListOfLists
1621
- },
1622
- "@list containing @list (with coercion)" => {
1623
- input: %({
1624
- "@context": {"http://example.org/foo": {"@container": "@list"}},
1625
- "http://example.org/foo": [{"@list": ["baz"]}]
2111
+ output: %({
2112
+ "@context": {
2113
+ "@vocab": "http://example/",
2114
+ "id": "@id",
2115
+ "type": "@type",
2116
+ "Foo": {"@context": {"id": null, "type": null}}
2117
+ },
2118
+ "id": "http://example.org/id",
2119
+ "type": "http://example/type",
2120
+ "a": {
2121
+ "@id": "http://example.org/Foo",
2122
+ "@type": "Foo",
2123
+ "bar": {"@id": "http://example.org/baz"}
2124
+ }
1626
2125
  }),
1627
- context: {},
1628
- exception: JSON::LD::JsonLdError::ListOfLists
1629
2126
  },
1630
- }.each do |title, params|
1631
- it(title) {run_compact(params)}
2127
+ }.each_pair do |title, params|
2128
+ it(title) {run_compact({processingMode: "json-ld-1.1"}.merge(params))}
1632
2129
  end
1633
2130
  end
1634
2131
 
1635
2132
  context "compact IRI selection" do
1636
2133
  {
1637
- "compacts using expanded term in 1.0" => {
2134
+ "does not compact using expanded term in 1.0" => {
1638
2135
  input: %({"http://example.org/foo": "term"}),
1639
2136
  context: %({"ex": {"@id": "http://example.org/"}}),
1640
2137
  output: %({
1641
2138
  "@context": {"ex": {"@id": "http://example.org/"}},
1642
- "ex:foo": "term"
2139
+ "http://example.org/foo": "term"
1643
2140
  }),
1644
2141
  processingMode: "json-ld-1.0"
1645
2142
  },
@@ -1716,6 +2213,14 @@ describe JSON::LD::API do
1716
2213
  "ex:bar": "term"
1717
2214
  })
1718
2215
  },
2216
+ "compacts using base if @vocab: relative" => {
2217
+ input: %({"http://example.org/foo/bar": "term"}),
2218
+ context: %({"@base": "http://example.org/foo/", "@vocab": ""}),
2219
+ output: %({
2220
+ "@context": {"@base": "http://example.org/foo/", "@vocab": ""},
2221
+ "bar": "term"
2222
+ })
2223
+ },
1719
2224
  }.each do |title, params|
1720
2225
  it(title) {run_compact(params)}
1721
2226
  end