json-ld 3.2.3 → 3.2.5

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.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/lib/json/ld/api.rb +807 -764
  4. data/lib/json/ld/compact.rb +304 -304
  5. data/lib/json/ld/conneg.rb +179 -161
  6. data/lib/json/ld/context.rb +2080 -1945
  7. data/lib/json/ld/expand.rb +745 -666
  8. data/lib/json/ld/extensions.rb +14 -13
  9. data/lib/json/ld/flatten.rb +257 -247
  10. data/lib/json/ld/format.rb +202 -194
  11. data/lib/json/ld/frame.rb +525 -502
  12. data/lib/json/ld/from_rdf.rb +224 -166
  13. data/lib/json/ld/html/nokogiri.rb +123 -121
  14. data/lib/json/ld/html/rexml.rb +151 -147
  15. data/lib/json/ld/reader.rb +107 -100
  16. data/lib/json/ld/resource.rb +224 -205
  17. data/lib/json/ld/streaming_reader.rb +574 -507
  18. data/lib/json/ld/streaming_writer.rb +93 -92
  19. data/lib/json/ld/to_rdf.rb +171 -167
  20. data/lib/json/ld/utils.rb +270 -264
  21. data/lib/json/ld/version.rb +24 -14
  22. data/lib/json/ld/writer.rb +334 -311
  23. data/lib/json/ld.rb +103 -96
  24. metadata +78 -209
  25. data/spec/api_spec.rb +0 -132
  26. data/spec/compact_spec.rb +0 -3482
  27. data/spec/conneg_spec.rb +0 -373
  28. data/spec/context_spec.rb +0 -2036
  29. data/spec/expand_spec.rb +0 -4496
  30. data/spec/flatten_spec.rb +0 -1203
  31. data/spec/format_spec.rb +0 -115
  32. data/spec/frame_spec.rb +0 -2498
  33. data/spec/from_rdf_spec.rb +0 -1005
  34. data/spec/matchers.rb +0 -20
  35. data/spec/rdfstar_spec.rb +0 -25
  36. data/spec/reader_spec.rb +0 -883
  37. data/spec/resource_spec.rb +0 -76
  38. data/spec/spec_helper.rb +0 -281
  39. data/spec/streaming_reader_spec.rb +0 -237
  40. data/spec/streaming_writer_spec.rb +0 -145
  41. data/spec/suite_compact_spec.rb +0 -22
  42. data/spec/suite_expand_spec.rb +0 -36
  43. data/spec/suite_flatten_spec.rb +0 -34
  44. data/spec/suite_frame_spec.rb +0 -29
  45. data/spec/suite_from_rdf_spec.rb +0 -22
  46. data/spec/suite_helper.rb +0 -411
  47. data/spec/suite_html_spec.rb +0 -22
  48. data/spec/suite_http_spec.rb +0 -35
  49. data/spec/suite_remote_doc_spec.rb +0 -22
  50. data/spec/suite_to_rdf_spec.rb +0 -30
  51. data/spec/support/extensions.rb +0 -44
  52. data/spec/test-files/test-1-compacted.jsonld +0 -10
  53. data/spec/test-files/test-1-context.jsonld +0 -7
  54. data/spec/test-files/test-1-expanded.jsonld +0 -5
  55. data/spec/test-files/test-1-input.jsonld +0 -10
  56. data/spec/test-files/test-1-rdf.ttl +0 -8
  57. data/spec/test-files/test-2-compacted.jsonld +0 -20
  58. data/spec/test-files/test-2-context.jsonld +0 -7
  59. data/spec/test-files/test-2-expanded.jsonld +0 -16
  60. data/spec/test-files/test-2-input.jsonld +0 -20
  61. data/spec/test-files/test-2-rdf.ttl +0 -14
  62. data/spec/test-files/test-3-compacted.jsonld +0 -11
  63. data/spec/test-files/test-3-context.jsonld +0 -8
  64. data/spec/test-files/test-3-expanded.jsonld +0 -10
  65. data/spec/test-files/test-3-input.jsonld +0 -11
  66. data/spec/test-files/test-3-rdf.ttl +0 -8
  67. data/spec/test-files/test-4-compacted.jsonld +0 -10
  68. data/spec/test-files/test-4-context.jsonld +0 -7
  69. data/spec/test-files/test-4-expanded.jsonld +0 -6
  70. data/spec/test-files/test-4-input.jsonld +0 -10
  71. data/spec/test-files/test-4-rdf.ttl +0 -5
  72. data/spec/test-files/test-5-compacted.jsonld +0 -13
  73. data/spec/test-files/test-5-context.jsonld +0 -7
  74. data/spec/test-files/test-5-expanded.jsonld +0 -9
  75. data/spec/test-files/test-5-input.jsonld +0 -13
  76. data/spec/test-files/test-5-rdf.ttl +0 -7
  77. data/spec/test-files/test-6-compacted.jsonld +0 -10
  78. data/spec/test-files/test-6-context.jsonld +0 -7
  79. data/spec/test-files/test-6-expanded.jsonld +0 -10
  80. data/spec/test-files/test-6-input.jsonld +0 -10
  81. data/spec/test-files/test-6-rdf.ttl +0 -6
  82. data/spec/test-files/test-7-compacted.jsonld +0 -23
  83. data/spec/test-files/test-7-context.jsonld +0 -4
  84. data/spec/test-files/test-7-expanded.jsonld +0 -20
  85. data/spec/test-files/test-7-input.jsonld +0 -23
  86. data/spec/test-files/test-7-rdf.ttl +0 -14
  87. data/spec/test-files/test-8-compacted.jsonld +0 -34
  88. data/spec/test-files/test-8-context.jsonld +0 -11
  89. data/spec/test-files/test-8-expanded.jsonld +0 -24
  90. data/spec/test-files/test-8-frame.jsonld +0 -18
  91. data/spec/test-files/test-8-framed.jsonld +0 -25
  92. data/spec/test-files/test-8-input.jsonld +0 -30
  93. data/spec/test-files/test-8-rdf.ttl +0 -15
  94. data/spec/test-files/test-9-compacted.jsonld +0 -20
  95. data/spec/test-files/test-9-context.jsonld +0 -13
  96. data/spec/test-files/test-9-expanded.jsonld +0 -14
  97. data/spec/test-files/test-9-input.jsonld +0 -12
  98. data/spec/to_rdf_spec.rb +0 -1551
  99. data/spec/writer_spec.rb +0 -427
@@ -1,579 +1,646 @@
1
- # -*- encoding: utf-8 -*-
1
+ # frozen_string_literal: true
2
+
2
3
  require 'json/ld'
3
4
  require 'json/ld/expand'
4
5
  require 'json/ld/to_rdf'
5
6
 
6
- module JSON::LD
7
- ##
8
- # A streaming JSON-LD parser in Ruby.
9
- #
10
- # @see http://json-ld.org/spec/ED/20110507/
11
- # @author [Gregg Kellogg](http://greggkellogg.net/)
12
- module StreamingReader
13
- include Utils
14
- include JSON::LD::ToRDF # For value object conversion
15
-
16
- # The base URI to use when resolving relative URIs
17
- # @return [RDF::URI]
18
- attr_reader :base
19
- attr_reader :namer
7
+ module JSON
8
+ module LD
9
+ ##
10
+ # A streaming JSON-LD parser in Ruby.
11
+ #
12
+ # @see http://json-ld.org/spec/ED/20110507/
13
+ # @author [Gregg Kellogg](http://greggkellogg.net/)
14
+ module StreamingReader
15
+ include Utils
16
+ include JSON::LD::ToRDF # For value object conversion
17
+
18
+ # The base URI to use when resolving relative URIs
19
+ # @return [RDF::URI]
20
+ attr_reader :base
21
+ attr_reader :namer
22
+
23
+ def self.format
24
+ JSON::LD::Format
25
+ end
20
26
 
21
- def self.format; JSON::LD::Format; end
27
+ ##
28
+ # @see RDF::Reader#each_statement
29
+ def stream_statement
30
+ unique_bnodes = @options[:unique_bnodes]
31
+ rename_bnodes = @options.fetch(:rename_bnodes, true)
32
+ # FIXME: document loader doesn't stream
33
+ @base = RDF::URI(@options[:base] || base_uri)
34
+ mj_opts = @options.keep_if { |k, v| k != :adapter || MUTLI_JSON_ADAPTERS.include?(v) }
35
+ value = MultiJson.load(@doc, mj_opts)
36
+ context_ref = @options[:expandContext]
37
+ # context_ref = @options.fetch(:expandContext, remote_doc.contextUrl)
38
+ context = Context.parse(context_ref, **@options)
39
+
40
+ @namer = if unique_bnodes
41
+ BlankNodeUniqer.new
42
+ else
43
+ (rename_bnodes ? BlankNodeNamer.new("b") : BlankNodeMapper.new)
44
+ end
45
+ # Namer for naming provisional nodes, which may be determined later to be actual
46
+ @provisional_namer = BlankNodeNamer.new("p")
22
47
 
23
- ##
24
- # @see RDF::Reader#each_statement
25
- def stream_statement(&block)
26
- unique_bnodes, rename_bnodes = @options[:unique_bnodes], @options.fetch(:rename_bnodes, true)
27
- # FIXME: document loader doesn't stream
28
- @base = RDF::URI(@options[:base] || base_uri)
29
- mj_opts = @options.keep_if {|k,v| k != :adapter || MUTLI_JSON_ADAPTERS.include?(v)}
30
- value = MultiJson.load(@doc, mj_opts)
31
- context_ref = @options[:expandContext]
32
- #context_ref = @options.fetch(:expandContext, remote_doc.contextUrl)
33
- context = Context.parse(context_ref, **@options)
34
-
35
- @namer = unique_bnodes ? BlankNodeUniqer.new : (rename_bnodes ? BlankNodeNamer.new("b") : BlankNodeMapper.new)
36
- # Namer for naming provisional nodes, which may be determined later to be actual
37
- @provisional_namer = BlankNodeNamer.new("p")
38
-
39
- parse_object(value, nil, context, graph_is_named: false) do |st|
40
- # Only output reasonably valid triples
41
- if st.to_a.all? {|r| r.is_a?(RDF::Term) && (r.uri? ? r.valid? : true)}
42
- block.call(st)
48
+ parse_object(value, nil, context, graph_is_named: false) do |st|
49
+ # Only output reasonably valid triples
50
+ yield(st) if st.to_a.all? { |r| r.is_a?(RDF::Term) && (r.uri? ? r.valid? : true) }
43
51
  end
52
+ rescue ::JSON::ParserError, ::JSON::LD::JsonLdError => e
53
+ log_fatal("Failed to parse input document: #{e.message}", exception: RDF::ReaderError)
44
54
  end
45
- rescue ::JSON::ParserError, ::JSON::LD::JsonLdError => e
46
- log_fatal("Failed to parse input document: #{e.message}", exception: RDF::ReaderError)
47
- end
48
55
 
49
- private
56
+ private
57
+
58
+ # Parse a node object, or array of node objects
59
+ #
60
+ # @param [Array, Hash] input
61
+ # @param [String] active_property
62
+ # The unexpanded property referencing this object
63
+ # @param [Context] context
64
+ # @param [RDF::Resource] subject referencing this object
65
+ # @param [RDF::URI] predicate the predicate part of the reference
66
+ # @param [Boolean] from_map
67
+ # Expanding from a map, which could be an `@type` map, so don't clear out context term definitions
68
+ # @param [Boolean] graph_is_named
69
+ # Use of `@graph` implies a named graph; not true at the top-level.
70
+ # @param [RDF::URI] extra_type from a type map
71
+ # @param [String] language from a language map
72
+ # @param [RDF::Resource] node_id from an id map
73
+ # @return [void]
74
+ def parse_object(input, active_property, context,
75
+ subject: nil, predicate: nil, from_map: false,
76
+ extra_type: nil, language: nil, node_id: nil,
77
+ graph_is_named: true, &block)
78
+
79
+ # Skip predicates that look like a BNode
80
+ if predicate.to_s.start_with?('_:')
81
+ warn "[DEPRECATION] Blank Node properties deprecated in JSON-LD 1.1."
82
+ return
83
+ end
50
84
 
51
- # Parse a node object, or array of node objects
52
- #
53
- # @param [Array, Hash] input
54
- # @param [String] active_property
55
- # The unexpanded property referencing this object
56
- # @param [Context] context
57
- # @param [RDF::Resource] subject referencing this object
58
- # @param [RDF::URI] predicate the predicate part of the reference
59
- # @param [Boolean] from_map
60
- # Expanding from a map, which could be an `@type` map, so don't clear out context term definitions
61
- # @param [Boolean] graph_is_named
62
- # Use of `@graph` implies a named graph; not true at the top-level.
63
- # @param [RDF::URI] extra_type from a type map
64
- # @param [String] language from a language map
65
- # @param [RDF::Resource] node_id from an id map
66
- # @return [void]
67
- def parse_object(input, active_property, context,
68
- subject: nil, predicate: nil, from_map: false,
69
- extra_type: nil, language: nil, node_id: nil,
70
- graph_is_named: true, &block)
71
-
72
- # Skip predicates that look like a BNode
73
- if predicate.to_s.start_with?('_:')
74
- warn "[DEPRECATION] Blank Node properties deprecated in JSON-LD 1.1."
75
- return
76
- end
85
+ if input.is_a?(Array)
86
+ input.each do |e|
87
+ parse_object(e, active_property, context, subject: subject, predicate: predicate, from_map: from_map,
88
+ &block)
89
+ end
90
+ return
91
+ end
77
92
 
78
- if input.is_a?(Array)
79
- input.each {|e| parse_object(e, active_property, context, subject: subject, predicate: predicate, from_map: from_map, &block)}
80
- return
81
- end
93
+ # Note that we haven't parsed an @id key, so have no subject
94
+ have_id = false
95
+ node_reference = false
96
+ is_list_or_set = false
97
+ node_id ||= RDF::Node.new(@provisional_namer.get_sym)
98
+ # For keeping statements not yet ready to be emitted
99
+ provisional_statements = []
100
+ value_object = {}
101
+
102
+ # Use a term-specific context, if defined, based on the non-type-scoped context.
103
+ if active_property && context.term_definitions[active_property]
104
+ property_scoped_context = context.term_definitions[active_property].context
105
+ end
82
106
 
83
- # Note that we haven't parsed an @id key, so have no subject
84
- have_id, node_reference, is_list_or_set = false, false, false
85
- node_id ||= RDF::Node.new(@provisional_namer.get_sym)
86
- # For keeping statements not yet ready to be emitted
87
- provisional_statements = []
88
- value_object = {}
89
-
90
- # Use a term-specific context, if defined, based on the non-type-scoped context.
91
- property_scoped_context = context.term_definitions[active_property].context if active_property && context.term_definitions[active_property]
92
-
93
- # Revert any previously type-scoped term definitions, unless this is from a map, a value object or a subject reference
94
- # FIXME
95
- if input.is_a?(Hash) && context.previous_context
96
- expanded_key_map = input.keys.inject({}) do |memo, key|
97
- memo.merge(key => context.expand_iri(key, vocab: true, as_string: true, base: base))
107
+ # Revert any previously type-scoped term definitions, unless this is from a map, a value object or a subject reference
108
+ # FIXME
109
+ if input.is_a?(Hash) && context.previous_context
110
+ expanded_key_map = input.keys.inject({}) do |memo, key|
111
+ memo.merge(key => context.expand_iri(key, vocab: true, as_string: true, base: base))
112
+ end
113
+ revert_context = !from_map &&
114
+ !expanded_key_map.value?('@value') &&
115
+ expanded_key_map.values != ['@id']
116
+ context = context.previous_context if revert_context
98
117
  end
99
- revert_context = !from_map &&
100
- !expanded_key_map.values.include?('@value') &&
101
- !(expanded_key_map.values == ['@id'])
102
- context = context.previous_context if revert_context
103
- end
104
118
 
105
- # Apply property-scoped context after reverting term-scoped context
106
- context = context.parse(property_scoped_context, base: base, override_protected: true) unless
107
- property_scoped_context.nil?
119
+ # Apply property-scoped context after reverting term-scoped context
120
+ context = context.parse(property_scoped_context, base: base, override_protected: true) unless
121
+ property_scoped_context.nil?
108
122
 
109
- # Otherwise, unless the value is a number, expand the value according to the Value Expansion rules, passing active property.
110
- unless input.is_a?(Hash)
111
- input = context.expand_value(active_property, input, base: base)
112
- end
123
+ # Otherwise, unless the value is a number, expand the value according to the Value Expansion rules, passing active property.
124
+ input = context.expand_value(active_property, input, base: base) unless input.is_a?(Hash)
113
125
 
114
- # Output any type provided from a type map
115
- provisional_statements << RDF::Statement(node_id, RDF.type, extra_type) if
116
- extra_type
126
+ # Output any type provided from a type map
127
+ provisional_statements << RDF::Statement(node_id, RDF.type, extra_type) if
128
+ extra_type
117
129
 
118
- # Add statement, either provisionally, or just emit
119
- add_statement = Proc.new do |st|
120
- if have_id || st.to_quad.none? {|r| r == node_id}
121
- block.call(st)
122
- else
123
- provisional_statements << st
130
+ # Add statement, either provisionally, or just emit
131
+ add_statement = proc do |st|
132
+ if have_id || st.to_quad.none?(node_id)
133
+ yield(st)
134
+ else
135
+ provisional_statements << st
136
+ end
124
137
  end
125
- end
126
138
 
127
- # Input is an object (Hash), parse keys in order
128
- state = :await_context
129
- input.each do |key, value|
130
- expanded_key = context.expand_iri(key, base: base, vocab: true)
131
- case expanded_key
132
- when '@context'
133
- raise JsonLdError::InvalidStreamingKeyOrder,
134
- "found #{key} in state #{state}" unless state == :await_context
135
- context = context.parse(value, base: base)
136
- state = :await_type
137
- when '@type'
138
- # Set the type-scoped context to the context on input, for use later
139
- raise JsonLdError::InvalidStreamingKeyOrder,
140
- "found #{key} in state #{state}" unless %i(await_context await_type).include?(state)
141
-
142
- type_scoped_context = context
143
- as_array(value).sort.each do |term|
144
- raise JsonLdError::InvalidTypeValue,
145
- "value of @type must be a string: #{term.inspect}" if !term.is_a?(String)
146
- term_context = type_scoped_context.term_definitions[term].context if type_scoped_context.term_definitions[term]
147
- context = context.parse(term_context, base: base, propagate: false) unless term_context.nil?
148
- type = type_scoped_context.expand_iri(term,
149
- base: base,
150
- documentRelative: true,
151
- vocab: true)
152
-
153
- # Early terminate for @json
154
- type = RDF.JSON if type == '@json'
155
- # Add a provisional statement
156
- provisional_statements << RDF::Statement(node_id, RDF.type, type)
157
- end
158
- state = :await_type
159
- when '@id'
160
- raise JsonLdError::InvalidSetOrListObject,
161
- "found #{key} in state #{state}" if is_list_or_set
162
- raise JsonLdError::CollidingKeywords,
163
- "found #{key} in state #{state}" unless %i(await_context await_type await_id).include?(state)
139
+ # Input is an object (Hash), parse keys in order
140
+ state = :await_context
141
+ input.each do |key, value|
142
+ expanded_key = context.expand_iri(key, base: base, vocab: true)
143
+ case expanded_key
144
+ when '@context'
145
+ unless state == :await_context
146
+ raise JsonLdError::InvalidStreamingKeyOrder,
147
+ "found #{key} in state #{state}"
148
+ end
149
+ context = context.parse(value, base: base)
150
+ state = :await_type
151
+ when '@type'
152
+ # Set the type-scoped context to the context on input, for use later
153
+ unless %i[await_context await_type].include?(state)
154
+ raise JsonLdError::InvalidStreamingKeyOrder,
155
+ "found #{key} in state #{state}"
156
+ end
164
157
 
165
- # Set our actual id, and use for replacing any provisional statements using our existing node_id, which is provisional
166
- raise JsonLdError::InvalidIdValue,
167
- "value of @id must be a string: #{value.inspect}" if !value.is_a?(String)
158
+ type_scoped_context = context
159
+ as_array(value).sort.each do |term|
160
+ unless term.is_a?(String)
161
+ raise JsonLdError::InvalidTypeValue,
162
+ "value of @type must be a string: #{term.inspect}"
163
+ end
164
+ if type_scoped_context.term_definitions[term]
165
+ term_context = type_scoped_context.term_definitions[term].context
166
+ end
167
+ context = context.parse(term_context, base: base, propagate: false) unless term_context.nil?
168
+ type = type_scoped_context.expand_iri(term,
169
+ base: base,
170
+ documentRelative: true,
171
+ vocab: true)
172
+
173
+ # Early terminate for @json
174
+ type = RDF.JSON if type == '@json'
175
+ # Add a provisional statement
176
+ provisional_statements << RDF::Statement(node_id, RDF.type, type)
177
+ end
178
+ state = :await_type
179
+ when '@id'
180
+ if is_list_or_set
181
+ raise JsonLdError::InvalidSetOrListObject,
182
+ "found #{key} in state #{state}"
183
+ end
184
+ unless %i[await_context await_type await_id].include?(state)
185
+ raise JsonLdError::CollidingKeywords,
186
+ "found #{key} in state #{state}"
187
+ end
188
+
189
+ # Set our actual id, and use for replacing any provisional statements using our existing node_id, which is provisional
190
+ unless value.is_a?(String)
191
+ raise JsonLdError::InvalidIdValue,
192
+ "value of @id must be a string: #{value.inspect}"
193
+ end
168
194
  node_reference = input.keys.length == 1
169
- expanded_id = context.expand_iri(value, base: base, documentRelative: true)
170
- next if expanded_id.nil?
171
- new_node_id = as_resource(expanded_id)
172
- # Replace and emit any statements including our provisional id with the newly established node (or graph) id
173
- provisional_statements.each do |st|
174
- st.subject = new_node_id if st.subject == node_id
175
- st.object = new_node_id if st.object == node_id
176
- st.graph_name = new_node_id if st.graph_name == node_id
177
- block.call(st)
178
- end
195
+ expanded_id = context.expand_iri(value, base: base, documentRelative: true)
196
+ next if expanded_id.nil?
197
+
198
+ new_node_id = as_resource(expanded_id)
199
+ # Replace and emit any statements including our provisional id with the newly established node (or graph) id
200
+ provisional_statements.each do |st|
201
+ st.subject = new_node_id if st.subject == node_id
202
+ st.object = new_node_id if st.object == node_id
203
+ st.graph_name = new_node_id if st.graph_name == node_id
204
+ yield(st)
205
+ end
179
206
 
180
- provisional_statements.clear
181
- have_id, node_id = true, new_node_id
207
+ provisional_statements.clear
208
+ have_id = true
209
+ node_id = new_node_id
182
210
 
183
- # if there's a subject & predicate, emit that statement now
184
- if subject && predicate
185
- st = RDF::Statement(subject, predicate, node_id)
186
- block.call(st)
187
- end
188
- state = :properties
189
-
190
- when '@direction'
191
- raise JsonLdError::InvalidStreamingKeyOrder,
192
- "found @direction in state #{state}" if state == :properties
193
- value_object['@direction'] = value
194
- state = :await_id
195
- when '@graph'
196
- # If `@graph` is at the top level (no `subject`) and value contains no keys other than `@graph` and `@context`, add triples to the default graph
197
- # Process all graph statements
198
- parse_object(value, nil, context) do |st|
199
- # If `@graph` is at the top level (`graph_is_named` is `false`) and input contains no keys other than `@graph` and `@context`, add triples to the default graph
200
- relevant_keys = input.keys - ['@context', key]
201
- st.graph_name = node_id unless !graph_is_named && relevant_keys.empty?
202
- if st.graph_name && !st.graph_name.valid?
203
- warn "skipping graph statement within invalid graph name: #{st.inspect}"
211
+ # if there's a subject & predicate, emit that statement now
212
+ if subject && predicate
213
+ st = RDF::Statement(subject, predicate, node_id)
214
+ yield(st)
215
+ end
216
+ state = :properties
217
+
218
+ when '@direction'
219
+ if state == :properties
220
+ raise JsonLdError::InvalidStreamingKeyOrder,
221
+ "found @direction in state #{state}"
222
+ end
223
+ value_object['@direction'] = value
224
+ state = :await_id
225
+ when '@graph'
226
+ # If `@graph` is at the top level (no `subject`) and value contains no keys other than `@graph` and `@context`, add triples to the default graph
227
+ # Process all graph statements
228
+ parse_object(value, nil, context) do |st|
229
+ # If `@graph` is at the top level (`graph_is_named` is `false`) and input contains no keys other than `@graph` and `@context`, add triples to the default graph
230
+ relevant_keys = input.keys - ['@context', key]
231
+ st.graph_name = node_id unless !graph_is_named && relevant_keys.empty?
232
+ if st.graph_name && !st.graph_name.valid?
233
+ warn "skipping graph statement within invalid graph name: #{st.inspect}"
234
+ else
235
+ add_statement.call(st)
236
+ end
237
+ end
238
+ state = :await_id unless state == :properties
239
+ when '@included'
240
+ # Expanded values must be node objects
241
+ have_statements = false
242
+ parse_object(value, active_property, context) do |st|
243
+ have_statements ||= st.subject?
244
+ yield(st)
245
+ end
246
+ unless have_statements
247
+ raise JsonLdError::InvalidIncludedValue,
248
+ "values of @included must expand to node objects"
249
+ end
250
+
251
+ state = :await_id unless state == :properties
252
+ when '@index'
253
+ state = :await_id unless state == :properties
254
+ unless value.is_a?(String)
255
+ raise JsonLdError::InvalidIndexValue,
256
+ "Value of @index is not a string: #{value.inspect}"
257
+ end
258
+ when '@language'
259
+ if state == :properties
260
+ raise JsonLdError::InvalidStreamingKeyOrder,
261
+ "found @language in state #{state}"
262
+ end
263
+ unless value.is_a?(String)
264
+ raise JsonLdError::InvalidLanguageTaggedString,
265
+ "@language value must be a string: #{value.inspect}"
266
+ end
267
+ unless /^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/.match?(value)
268
+ warn "@language must be valid BCP47: #{value.inspect}"
269
+ return
270
+ end
271
+ language = value
272
+ state = :await_id
273
+ when '@list'
274
+ unless %i[await_context await_type await_id].include?(state)
275
+ raise JsonLdError::InvalidSetOrListObject,
276
+ "found #{key} in state #{state}"
277
+ end
278
+ is_list_or_set = true
279
+ node_id = parse_list(value, active_property, context, &block) if subject
280
+ state = :properties
281
+ when '@nest'
282
+ if context.term_definitions[active_property]
283
+ nest_context = context.term_definitions[active_property].context
284
+ end
285
+ nest_context = if nest_context.nil?
286
+ context
204
287
  else
205
- add_statement.call(st)
288
+ context.parse(nest_context, base: base, override_protected: true)
206
289
  end
207
- end
208
- state = :await_id unless state == :properties
209
- when '@included'
210
- # Expanded values must be node objects
211
- have_statements = false
212
- parse_object(value, active_property, context) do |st|
213
- have_statements ||= st.subject?
214
- block.call(st)
215
- end
216
- raise JsonLdError::InvalidIncludedValue, "values of @included must expand to node objects" unless have_statements
217
- state = :await_id unless state == :properties
218
- when '@index'
219
- state = :await_id unless state == :properties
220
- raise JsonLdError::InvalidIndexValue,
221
- "Value of @index is not a string: #{value.inspect}" unless value.is_a?(String)
222
- when '@language'
223
- raise JsonLdError::InvalidStreamingKeyOrder,
224
- "found @language in state #{state}" if state == :properties
225
- raise JsonLdError::InvalidLanguageTaggedString,
226
- "@language value must be a string: #{value.inspect}" if !value.is_a?(String)
227
- if value !~ /^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/
228
- warn "@language must be valid BCP47: #{value.inspect}"
229
- return
230
- end
231
- language = value
232
- state = :await_id
233
- when '@list'
234
- raise JsonLdError::InvalidSetOrListObject,
235
- "found #{key} in state #{state}" if
236
- !%i(await_context await_type await_id).include?(state)
237
- is_list_or_set = true
238
- if subject
239
- node_id = parse_list(value, active_property, context, &block)
240
- end
241
- state = :properties
242
- when '@nest'
243
- nest_context = context.term_definitions[active_property].context if context.term_definitions[active_property]
244
- nest_context = if nest_context.nil?
245
- context
246
- else
247
- context.parse(nest_context, base: base, override_protected: true)
248
- end
249
- as_array(value).each do |v|
250
- raise JsonLdError::InvalidNestValue, v.inspect unless
251
- v.is_a?(Hash) && v.keys.none? {|k| nest_context.expand_iri(k, vocab: true, base: base) == '@value'}
290
+ as_array(value).each do |v|
291
+ raise JsonLdError::InvalidNestValue, v.inspect unless
292
+ v.is_a?(Hash) && v.keys.none? { |k| nest_context.expand_iri(k, vocab: true, base: base) == '@value' }
293
+
252
294
  parse_object(v, active_property, nest_context, node_id: node_id) do |st|
253
295
  add_statement.call(st)
254
296
  end
255
- end
256
- state = :await_id unless state == :properties
257
- when '@reverse'
258
- as_array(value).each do |item|
259
- item = context.expand_value(active_property, item, base: base) unless item.is_a?(Hash)
260
- raise JsonLdError::InvalidReverseValue, item.inspect if value?(item)
261
- raise JsonLdError::InvalidReversePropertyMap, item.inspect if node_reference?(item)
262
- raise JsonLdError::InvalidReversePropertyValue, item.inspect if list?(item)
263
- has_own_subject = false
264
- parse_object(item, active_property, context, node_id: node_id, predicate: predicate) do |st|
265
- if st.subject == node_id
266
- raise JsonLdError::InvalidReversePropertyValue, item.inspect if !st.object.resource?
267
- # Invert sense of statements
268
- st = RDF::Statement(st.object, st.predicate, st.subject)
269
- has_own_subject = true
297
+ end
298
+ state = :await_id unless state == :properties
299
+ when '@reverse'
300
+ as_array(value).each do |item|
301
+ item = context.expand_value(active_property, item, base: base) unless item.is_a?(Hash)
302
+ raise JsonLdError::InvalidReverseValue, item.inspect if value?(item)
303
+ raise JsonLdError::InvalidReversePropertyMap, item.inspect if node_reference?(item)
304
+ raise JsonLdError::InvalidReversePropertyValue, item.inspect if list?(item)
305
+
306
+ has_own_subject = false
307
+ parse_object(item, active_property, context, node_id: node_id, predicate: predicate) do |st|
308
+ if st.subject == node_id
309
+ raise JsonLdError::InvalidReversePropertyValue, item.inspect unless st.object.resource?
310
+
311
+ # Invert sense of statements
312
+ st = RDF::Statement(st.object, st.predicate, st.subject)
313
+ has_own_subject = true
314
+ end
315
+ add_statement.call(st)
270
316
  end
271
- add_statement.call(st)
317
+
318
+ # If the reversed node does not make any claims on this subject, it's an error
319
+ raise JsonLdError::InvalidReversePropertyValue, item.inspect unless has_own_subject
320
+ end
321
+ state = :await_id unless state == :properties
322
+ when '@set'
323
+ unless %i[await_context await_type await_id].include?(state)
324
+ raise JsonLdError::InvalidSetOrListObject,
325
+ "found #{key} in state #{state}"
326
+ end
327
+ is_list_or_set = true
328
+ value = as_array(value).compact
329
+ parse_object(value, active_property, context, subject: subject, predicate: predicate, &block)
330
+ node_id = nil
331
+ state = :properties
332
+ when '@value'
333
+ if state == :properties
334
+ raise JsonLdError::InvalidStreamingKeyOrder,
335
+ "found @value in state #{state}"
272
336
  end
337
+ value_object['@value'] = value
338
+ state = :await_id
339
+ else
340
+ state = :await_id unless state == :properties
341
+ # Skip keys that don't expand to a keyword or absolute IRI
342
+ next if expanded_key.is_a?(RDF::URI) && !expanded_key.absolute?
273
343
 
274
- # If the reversed node does not make any claims on this subject, it's an error
275
- raise JsonLdError::InvalidReversePropertyValue, item.inspect unless has_own_subject
276
- end
277
- state = :await_id unless state == :properties
278
- when '@set'
279
- raise JsonLdError::InvalidSetOrListObject,
280
- "found #{key} in state #{state}" if
281
- !%i(await_context await_type await_id).include?(state)
282
- is_list_or_set = true
283
- value = as_array(value).compact
284
- parse_object(value, active_property, context, subject: subject, predicate: predicate, &block)
285
- node_id = nil
286
- state = :properties
287
- when '@value'
288
- raise JsonLdError::InvalidStreamingKeyOrder,
289
- "found @value in state #{state}" if state == :properties
290
- value_object['@value'] = value
291
- state = :await_id
292
- else
293
- state = :await_id unless state == :properties
294
- # Skip keys that don't expand to a keyword or absolute IRI
295
- next if expanded_key.is_a?(RDF::URI) && !expanded_key.absolute?
296
- parse_property(value, key, context, node_id, expanded_key) do |st|
297
- add_statement.call(st)
344
+ parse_property(value, key, context, node_id, expanded_key) do |st|
345
+ add_statement.call(st)
346
+ end
298
347
  end
299
348
  end
300
- end
301
349
 
302
- # Value object with @id
303
- raise JsonLdError::InvalidValueObject,
304
- "value object has unknown key: @id" if
305
- !value_object.empty? && (have_id || is_list_or_set)
306
-
307
- # Can't have both @id and either @list or @set
308
- raise JsonLdError::InvalidSetOrListObject,
309
- "found @id with @list or @set" if
310
- have_id && is_list_or_set
311
-
312
- type_statements = provisional_statements.select {|ps| ps.predicate == RDF.type && ps.graph_name.nil?}
313
- value_object['@language'] = (@options[:lowercaseLanguage] ? language.downcase : language) if language
314
- if !value_object.empty? &&
315
- (!value_object['@value'].nil? ||
316
- (type_statements.first || RDF::Statement.new).object == RDF.JSON)
317
-
318
- # There can be only one value of @type
319
- case type_statements.length
320
- when 0 then #skip
321
- when 1
322
- raise JsonLdError::InvalidTypedValue,
323
- "value of @type must be an IRI or '@json': #{type_statements.first.object.inspect}" unless
324
- type_statements.first.object.valid?
325
- value_object['@type'] = type_statements.first.object
326
- else
350
+ # Value object with @id
351
+ if !value_object.empty? && (have_id || is_list_or_set)
327
352
  raise JsonLdError::InvalidValueObject,
328
- "value object must not have more than one type"
353
+ "value object has unknown key: @id"
354
+ end
355
+
356
+ # Can't have both @id and either @list or @set
357
+ if have_id && is_list_or_set
358
+ raise JsonLdError::InvalidSetOrListObject,
359
+ "found @id with @list or @set"
329
360
  end
330
361
 
331
- # Check for extra keys
332
- raise JsonLdError::InvalidValueObject,
333
- "value object has unknown keys: #{value_object.inspect}" unless
334
- (value_object.keys - Expand::KEYS_VALUE_LANGUAGE_TYPE_INDEX_DIRECTION).empty?
335
-
336
- # @type is inconsistent with either @language or @direction
337
- raise JsonLdError::InvalidValueObject,
338
- "value object must not include @type with either " +
339
- "@language or @direction: #{value_object.inspect}" if
340
- value_object.keys.include?('@type') && !(value_object.keys & %w(@language @direction)).empty?
341
-
342
- if value_object.key?('@language') && !value_object['@value'].is_a?(String)
343
- raise JsonLdError::InvalidLanguageTaggedValue,
344
- "with @language @value must be a string: #{value_object.inspect}"
345
- elsif value_object['@type'] && value_object['@type'] != RDF.JSON
346
- raise JsonLdError::InvalidTypedValue,
347
- "value of @type must be an IRI or '@json': #{value_object['@type'].inspect}" unless
348
- value_object['@type'].is_a?(RDF::URI)
349
- elsif value_object['@type'] != RDF.JSON
350
- case value_object['@value']
351
- when String, TrueClass, FalseClass, Numeric then # okay
362
+ type_statements = provisional_statements.select { |ps| ps.predicate == RDF.type && ps.graph_name.nil? }
363
+ value_object['@language'] = (@options[:lowercaseLanguage] ? language.downcase : language) if language
364
+ if !value_object.empty? &&
365
+ (!value_object['@value'].nil? ||
366
+ (type_statements.first || RDF::Statement.new).object == RDF.JSON)
367
+
368
+ # There can be only one value of @type
369
+ case type_statements.length
370
+ when 0 # skip
371
+ when 1
372
+ unless type_statements.first.object.valid?
373
+ raise JsonLdError::InvalidTypedValue,
374
+ "value of @type must be an IRI or '@json': #{type_statements.first.object.inspect}"
375
+ end
376
+ value_object['@type'] = type_statements.first.object
352
377
  else
353
- raise JsonLdError::InvalidValueObjectValue,
354
- "@value is: #{value_object['@value'].inspect}"
378
+ raise JsonLdError::InvalidValueObject,
379
+ "value object must not have more than one type"
355
380
  end
356
- end
357
- literal = item_to_rdf(value_object, &block)
358
- st = RDF::Statement(subject, predicate, literal)
359
- block.call(st)
360
- elsif !provisional_statements.empty?
361
- # Emit all provisional statements, as no @id was ever found
362
- provisional_statements.each {|st| block.call(st)}
363
- end
364
381
 
365
- # Use implicit subject to generate the relationship
366
- if value_object.empty? && subject && predicate && !have_id && !node_reference
367
- block.call(RDF::Statement(subject, predicate, node_id))
368
- end
369
- end
382
+ # Check for extra keys
383
+ unless (value_object.keys - Expand::KEYS_VALUE_LANGUAGE_TYPE_INDEX_DIRECTION).empty?
384
+ raise JsonLdError::InvalidValueObject,
385
+ "value object has unknown keys: #{value_object.inspect}"
386
+ end
370
387
 
371
- def parse_property(input, active_property, context, subject, predicate, &block)
372
- container = context.container(active_property)
373
- if container.include?('@language') && input.is_a?(Hash)
374
- input.each do |lang, lang_value|
375
- expanded_lang = context.expand_iri(lang, vocab: true)
376
- if lang !~ /^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/ && expanded_lang != '@none'
377
- warn "@language must be valid BCP47: #{lang.inspect}"
388
+ # @type is inconsistent with either @language or @direction
389
+ if value_object.key?('@type') && !(value_object.keys & %w[@language @direction]).empty?
390
+ raise JsonLdError::InvalidValueObject,
391
+ "value object must not include @type with either " \
392
+ "@language or @direction: #{value_object.inspect}"
378
393
  end
379
394
 
380
- as_array(lang_value).each do |item|
381
- raise JsonLdError::InvalidLanguageMapValue,
382
- "Expected #{item.inspect} to be a string" unless item.nil? || item.is_a?(String)
383
- lang_obj = {'@value' => item}
384
- lang_obj['@language'] = lang unless expanded_lang == '@none'
385
- lang_obj['@direction'] = context.direction(lang) if context.direction(lang)
386
- parse_object(lang_obj, active_property, context, subject: subject, predicate: predicate, &block)
395
+ if value_object.key?('@language') && !value_object['@value'].is_a?(String)
396
+ raise JsonLdError::InvalidLanguageTaggedValue,
397
+ "with @language @value must be a string: #{value_object.inspect}"
398
+ elsif value_object['@type'] && value_object['@type'] != RDF.JSON
399
+ unless value_object['@type'].is_a?(RDF::URI)
400
+ raise JsonLdError::InvalidTypedValue,
401
+ "value of @type must be an IRI or '@json': #{value_object['@type'].inspect}"
402
+ end
403
+ elsif value_object['@type'] != RDF.JSON
404
+ case value_object['@value']
405
+ when String, TrueClass, FalseClass, Numeric # okay
406
+ else
407
+ raise JsonLdError::InvalidValueObjectValue,
408
+ "@value is: #{value_object['@value'].inspect}"
409
+ end
387
410
  end
411
+ literal = item_to_rdf(value_object, &block)
412
+ st = RDF::Statement(subject, predicate, literal)
413
+ yield(st)
414
+ elsif !provisional_statements.empty?
415
+ # Emit all provisional statements, as no @id was ever found
416
+ provisional_statements.each(&block)
388
417
  end
389
- elsif container.include?('@list')
390
- # Handle case where value is a list object
391
- if input.is_a?(Hash) &&
392
- input.keys.map do |k|
393
- context.expand_iri(k, vocab: true, as_string: true, base: base)
394
- end.include?('@list')
395
- parse_object(input, active_property, context,
396
- subject: subject, predicate: predicate, &block)
397
- else
398
- list = parse_list(input, active_property, context, &block)
399
- block.call(RDF::Statement(subject, predicate, list))
400
- end
401
- elsif container.intersect?(JSON::LD::Expand::CONTAINER_INDEX_ID_TYPE) && input.is_a?(Hash)
402
- # Get appropriate context for this container
403
- container_context = if container.include?('@type') && context.previous_context
404
- context.previous_context
405
- elsif container.include?('@id') && context.term_definitions[active_property]
406
- id_context = context.term_definitions[active_property].context if context.term_definitions[active_property]
407
- if id_context.nil?
408
- context
418
+
419
+ # Use implicit subject to generate the relationship
420
+ return unless value_object.empty? && subject && predicate && !have_id && !node_reference
421
+
422
+ yield(RDF::Statement(subject, predicate, node_id))
423
+ end
424
+
425
+ def parse_property(input, active_property, context, subject, predicate, &block)
426
+ container = context.container(active_property)
427
+ if container.include?('@language') && input.is_a?(Hash)
428
+ input.each do |lang, lang_value|
429
+ expanded_lang = context.expand_iri(lang, vocab: true)
430
+ if lang !~ /^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/ && expanded_lang != '@none'
431
+ warn "@language must be valid BCP47: #{lang.inspect}"
432
+ end
433
+
434
+ as_array(lang_value).each do |item|
435
+ unless item.nil? || item.is_a?(String)
436
+ raise JsonLdError::InvalidLanguageMapValue,
437
+ "Expected #{item.inspect} to be a string"
438
+ end
439
+ lang_obj = { '@value' => item }
440
+ lang_obj['@language'] = lang unless expanded_lang == '@none'
441
+ lang_obj['@direction'] = context.direction(lang) if context.direction(lang)
442
+ parse_object(lang_obj, active_property, context, subject: subject, predicate: predicate, &block)
443
+ end
444
+ end
445
+ elsif container.include?('@list')
446
+ # Handle case where value is a list object
447
+ if input.is_a?(Hash) &&
448
+ input.keys.map do |k|
449
+ context.expand_iri(k, vocab: true, as_string: true, base: base)
450
+ end.include?('@list')
451
+ parse_object(input, active_property, context,
452
+ subject: subject, predicate: predicate, &block)
409
453
  else
410
- context.parse(id_context, base: base, propagate: false)
454
+ list = parse_list(input, active_property, context, &block)
455
+ yield(RDF::Statement(subject, predicate, list))
411
456
  end
412
- else
413
- context
414
- end
415
-
416
- input.each do |k, v|
417
- # 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
418
- map_context = container_context.term_definitions[k].context if
419
- container.include?('@type') && container_context.term_definitions[k]
420
- unless map_context.nil?
421
- map_context = container_context.parse(map_context, base: base, propagate: false)
457
+ elsif container.intersect?(JSON::LD::Expand::CONTAINER_INDEX_ID_TYPE) && input.is_a?(Hash)
458
+ # Get appropriate context for this container
459
+ container_context = if container.include?('@type') && context.previous_context
460
+ context.previous_context
461
+ elsif container.include?('@id') && context.term_definitions[active_property]
462
+ id_context = context.term_definitions[active_property].context if context.term_definitions[active_property]
463
+ if id_context.nil?
464
+ context
465
+ else
466
+ context.parse(id_context, base: base, propagate: false)
467
+ end
468
+ else
469
+ context
422
470
  end
423
- map_context ||= container_context
424
-
425
- expanded_k = container_context.expand_iri(k, vocab: true, as_string: true, base: base)
426
- index_key = context.term_definitions[active_property].index || '@index'
427
-
428
- case
429
- when container.include?('@index') && container.include?('@graph')
430
- # Index is ignored
431
- as_array(v).each do |item|
432
- # Each value is in a separate graph
433
- graph_name = RDF::Node.new(namer.get_sym)
434
- parse_object(item, active_property, context) do |st|
435
- st.graph_name ||= graph_name
436
- block.call(st)
437
- end
438
- block.call(RDF::Statement(subject, predicate, graph_name))
439
471
 
440
- # Add a property index, if appropriate
441
- unless index_key == '@index'
472
+ input.each do |k, v|
473
+ # 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
474
+ map_context = container_context.term_definitions[k].context if
475
+ container.include?('@type') && container_context.term_definitions[k]
476
+ map_context = container_context.parse(map_context, base: base, propagate: false) unless map_context.nil?
477
+ map_context ||= container_context
478
+
479
+ expanded_k = container_context.expand_iri(k, vocab: true, as_string: true, base: base)
480
+ index_key = context.term_definitions[active_property].index || '@index'
481
+
482
+ if container.include?('@index') && container.include?('@graph')
483
+ # Index is ignored
484
+ as_array(v).each do |item|
485
+ # Each value is in a separate graph
486
+ graph_name = RDF::Node.new(namer.get_sym)
487
+ parse_object(item, active_property, context) do |st|
488
+ st.graph_name ||= graph_name
489
+ yield(st)
490
+ end
491
+ yield(RDF::Statement(subject, predicate, graph_name))
492
+
493
+ # Add a property index, if appropriate
494
+ next if index_key == '@index'
495
+
442
496
  # Expand key based on term
443
- expanded_k = k == '@none' ?
444
- '@none' :
497
+ expanded_k = if k == '@none'
498
+ '@none'
499
+ else
445
500
  container_context.expand_value(index_key, k, base: base)
501
+ end
446
502
 
447
503
  # Add the index property as a property of the graph name
448
504
  index_property = container_context.expand_iri(index_key, vocab: true, base: base)
449
- emit_object(expanded_k, index_key, map_context, graph_name,
450
- index_property, from_map: true, &block) unless
451
- expanded_k == '@none'
505
+ unless expanded_k == '@none'
506
+ emit_object(expanded_k, index_key, map_context, graph_name,
507
+ index_property, from_map: true, &block)
508
+ end
452
509
  end
453
- end
454
- when container.include?('@index')
455
- if index_key == '@index'
456
- # Index is ignored
457
- emit_object(v, active_property, map_context, subject, predicate, from_map: true, &block)
458
- else
459
- # Expand key based on term
460
- expanded_k = k == '@none' ?
461
- '@none' :
462
- container_context.expand_value(index_key, k, base: base)
463
-
464
- index_property = container_context.expand_iri(index_key, vocab: true, as_string: true, base: base)
465
-
466
- # index_key is a property
467
- as_array(v).each do |item|
468
- item = container_context.expand_value(active_property, item, base: base) if item.is_a?(String)
469
- raise JsonLdError::InvalidValueObject,
470
- "Attempt to add illegal key to value object: #{index_key}" if value?(item)
471
- # add expanded_k as value of index_property in item
472
- item[index_property] = [expanded_k].concat(Array(item[index_property])) unless expanded_k == '@none'
473
- emit_object(item, active_property, map_context, subject, predicate, from_map: true, &block)
510
+ elsif container.include?('@index')
511
+ if index_key == '@index'
512
+ # Index is ignored
513
+ emit_object(v, active_property, map_context, subject, predicate, from_map: true, &block)
514
+ else
515
+ # Expand key based on term
516
+ expanded_k = if k == '@none'
517
+ '@none'
518
+ else
519
+ container_context.expand_value(index_key, k, base: base)
520
+ end
521
+
522
+ index_property = container_context.expand_iri(index_key, vocab: true, as_string: true, base: base)
523
+
524
+ # index_key is a property
525
+ as_array(v).each do |item|
526
+ item = container_context.expand_value(active_property, item, base: base) if item.is_a?(String)
527
+ if value?(item)
528
+ raise JsonLdError::InvalidValueObject,
529
+ "Attempt to add illegal key to value object: #{index_key}"
530
+ end
531
+ # add expanded_k as value of index_property in item
532
+ item[index_property] = [expanded_k].concat(Array(item[index_property])) unless expanded_k == '@none'
533
+ emit_object(item, active_property, map_context, subject, predicate, from_map: true, &block)
534
+ end
535
+ end
536
+ elsif container.include?('@id') && container.include?('@graph')
537
+ graph_name = if expanded_k == '@none'
538
+ RDF::Node.new(namer.get_sym)
539
+ else
540
+ container_context.expand_iri(k, documentRelative: true, base: base)
474
541
  end
542
+ parse_object(v, active_property, context) do |st|
543
+ st.graph_name ||= graph_name
544
+ yield(st)
545
+ end
546
+ yield(RDF::Statement(subject, predicate, graph_name))
547
+ elsif container.include?('@id')
548
+ expanded_k = container_context.expand_iri(k, documentRelative: true, base: base)
549
+ # pass our id
550
+ emit_object(v, active_property, map_context, subject, predicate,
551
+ node_id: (expanded_k unless expanded_k == '@none'),
552
+ from_map: true,
553
+ &block)
554
+ elsif container.include?('@type')
555
+ emit_object(v, active_property, map_context, subject, predicate,
556
+ from_map: true,
557
+ extra_type: as_resource(expanded_k),
558
+ &block)
475
559
  end
476
- when container.include?('@id') && container.include?('@graph')
477
- graph_name = expanded_k == '@none' ?
478
- RDF::Node.new(namer.get_sym) :
479
- container_context.expand_iri(k, documentRelative: true, base: base)
560
+ end
561
+ elsif container.include?('@graph')
562
+ # Index is ignored
563
+ as_array(input).each do |v|
564
+ # Each value is in a separate graph
565
+ graph_name = RDF::Node.new(namer.get_sym)
480
566
  parse_object(v, active_property, context) do |st|
481
567
  st.graph_name ||= graph_name
482
- block.call(st)
483
- end
484
- block.call(RDF::Statement(subject, predicate, graph_name))
485
- when container.include?('@id')
486
- expanded_k = container_context.expand_iri(k, documentRelative: true, base: base)
487
- # pass our id
488
- emit_object(v, active_property, map_context, subject, predicate,
489
- node_id: (expanded_k unless expanded_k == '@none'),
490
- from_map: true,
491
- &block)
492
- when container.include?('@type')
493
- emit_object(v, active_property, map_context, subject, predicate,
494
- from_map: true,
495
- extra_type: as_resource(expanded_k),
496
- &block)
497
- end
498
- end
499
- elsif container.include?('@graph')
500
- # Index is ignored
501
- as_array(input).each do |v|
502
- # Each value is in a separate graph
503
- graph_name = RDF::Node.new(namer.get_sym)
504
- parse_object(v, active_property, context) do |st|
505
- st.graph_name ||= graph_name
506
- block.call(st)
568
+ yield(st)
569
+ end
570
+ yield(RDF::Statement(subject, predicate, graph_name))
507
571
  end
508
- block.call(RDF::Statement(subject, predicate, graph_name))
572
+ else
573
+ emit_object(input, active_property, context, subject, predicate, &block)
509
574
  end
510
- else
511
- emit_object(input, active_property, context, subject, predicate, &block)
512
575
  end
513
- end
514
576
 
515
- # Wrapps parse_object to handle JSON literals and reversed properties
516
- def emit_object(input, active_property, context, subject, predicate, **options, &block)
517
- if context.coerce(active_property) == '@json'
518
- parse_object(context.expand_value(active_property, input), active_property, context,
519
- subject: subject, predicate: predicate, **options, &block)
520
- elsif context.reverse?(active_property)
521
- as_array(input).each do |item|
522
- item = context.expand_value(active_property, item, base: base) unless item.is_a?(Hash)
523
- raise JsonLdError::InvalidReverseValue, item.inspect if value?(item)
524
- raise JsonLdError::InvalidReversePropertyValue, item.inspect if list?(item)
525
- has_own_subject = false
526
- parse_object(item, active_property, context, subject: subject, predicate: predicate, **options) do |st|
527
- if st.subject == subject
528
- raise JsonLdError::InvalidReversePropertyValue, item.inspect if !st.object.resource?
529
- # Invert sense of statements
530
- st = RDF::Statement(st.object, st.predicate, st.subject)
531
- has_own_subject = true
532
- end
533
- block.call(st)
534
- end
577
+ # Wrapps parse_object to handle JSON literals and reversed properties
578
+ def emit_object(input, active_property, context, subject, predicate, **options, &block)
579
+ if context.coerce(active_property) == '@json'
580
+ parse_object(context.expand_value(active_property, input), active_property, context,
581
+ subject: subject, predicate: predicate, **options, &block)
582
+ elsif context.reverse?(active_property)
583
+ as_array(input).each do |item|
584
+ item = context.expand_value(active_property, item, base: base) unless item.is_a?(Hash)
585
+ raise JsonLdError::InvalidReverseValue, item.inspect if value?(item)
586
+ raise JsonLdError::InvalidReversePropertyValue, item.inspect if list?(item)
535
587
 
536
- # If the reversed node does not make any claims on this subject, it's an error
537
- raise JsonLdError::InvalidReversePropertyValue,
538
- "@reverse value must be a node: #{value.inspect}" unless has_own_subject
539
- end
540
- else
541
- as_array(input).flatten.each do |item|
542
- # emit property/value
543
- parse_object(item, active_property, context,
544
- subject: subject, predicate: predicate, **options, &block)
588
+ has_own_subject = false
589
+ parse_object(item, active_property, context, subject: subject, predicate: predicate, **options) do |st|
590
+ if st.subject == subject
591
+ raise JsonLdError::InvalidReversePropertyValue, item.inspect unless st.object.resource?
592
+
593
+ # Invert sense of statements
594
+ st = RDF::Statement(st.object, st.predicate, st.subject)
595
+ has_own_subject = true
596
+ end
597
+ yield(st)
598
+ end
599
+
600
+ # If the reversed node does not make any claims on this subject, it's an error
601
+ unless has_own_subject
602
+ raise JsonLdError::InvalidReversePropertyValue,
603
+ "@reverse value must be a node: #{value.inspect}"
604
+ end
605
+ end
606
+ else
607
+ as_array(input).flatten.each do |item|
608
+ # emit property/value
609
+ parse_object(item, active_property, context,
610
+ subject: subject, predicate: predicate, **options, &block)
611
+ end
545
612
  end
546
613
  end
547
- end
548
614
 
549
- # Process input as an ordered list
550
- # @return [RDF::Resource] the list head
551
- def parse_list(input, active_property, context, &block)
552
- # Transform all entries into their values
553
- # this allows us to eliminate those that don't create any statements
554
- fake_subject = RDF::Node.new
555
- values = as_array(input).map do |entry|
556
- if entry.is_a?(Array)
557
- # recursive list
558
- entry_value = parse_list(entry, active_property, context, &block)
559
- else
560
- entry_value = nil
561
- parse_object(entry, active_property, context, subject: fake_subject, predicate: RDF.first) do |st|
562
- if st.subject == fake_subject
563
- entry_value = st.object
564
- else
565
- block.call(st)
615
+ # Process input as an ordered list
616
+ # @return [RDF::Resource] the list head
617
+ def parse_list(input, active_property, context, &block)
618
+ # Transform all entries into their values
619
+ # this allows us to eliminate those that don't create any statements
620
+ fake_subject = RDF::Node.new
621
+ values = as_array(input).map do |entry|
622
+ if entry.is_a?(Array)
623
+ # recursive list
624
+ entry_value = parse_list(entry, active_property, context, &block)
625
+ else
626
+ entry_value = nil
627
+ parse_object(entry, active_property, context, subject: fake_subject, predicate: RDF.first) do |st|
628
+ if st.subject == fake_subject
629
+ entry_value = st.object
630
+ else
631
+ yield(st)
632
+ end
566
633
  end
634
+ entry_value
567
635
  end
568
- entry_value
569
- end
570
- end.compact
571
- return RDF.nil if values.empty?
636
+ end.compact
637
+ return RDF.nil if values.empty?
572
638
 
573
- # Construct a list from values, and emit list statements, returning the list subject
574
- list = RDF::List(*values)
575
- list.each_statement(&block)
576
- return list.subject
639
+ # Construct a list from values, and emit list statements, returning the list subject
640
+ list = RDF::List(*values)
641
+ list.each_statement(&block)
642
+ list.subject
643
+ end
577
644
  end
578
645
  end
579
- end
646
+ end