json-ld 3.2.4 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -4
  3. data/VERSION +1 -1
  4. data/lib/json/ld/api.rb +807 -771
  5. data/lib/json/ld/compact.rb +304 -304
  6. data/lib/json/ld/conneg.rb +179 -161
  7. data/lib/json/ld/context.rb +2079 -1913
  8. data/lib/json/ld/expand.rb +745 -666
  9. data/lib/json/ld/extensions.rb +14 -13
  10. data/lib/json/ld/flatten.rb +257 -247
  11. data/lib/json/ld/format.rb +202 -194
  12. data/lib/json/ld/frame.rb +525 -502
  13. data/lib/json/ld/from_rdf.rb +223 -204
  14. data/lib/json/ld/html/nokogiri.rb +123 -121
  15. data/lib/json/ld/html/rexml.rb +151 -147
  16. data/lib/json/ld/reader.rb +107 -100
  17. data/lib/json/ld/resource.rb +224 -205
  18. data/lib/json/ld/streaming_reader.rb +574 -507
  19. data/lib/json/ld/streaming_writer.rb +93 -92
  20. data/lib/json/ld/to_rdf.rb +171 -169
  21. data/lib/json/ld/utils.rb +270 -264
  22. data/lib/json/ld/version.rb +24 -14
  23. data/lib/json/ld/writer.rb +334 -311
  24. data/lib/json/ld.rb +103 -96
  25. metadata +56 -225
  26. data/spec/api_spec.rb +0 -132
  27. data/spec/compact_spec.rb +0 -3482
  28. data/spec/conneg_spec.rb +0 -373
  29. data/spec/context_spec.rb +0 -2056
  30. data/spec/expand_spec.rb +0 -4496
  31. data/spec/flatten_spec.rb +0 -1203
  32. data/spec/format_spec.rb +0 -115
  33. data/spec/frame_spec.rb +0 -2541
  34. data/spec/from_rdf_spec.rb +0 -1072
  35. data/spec/matchers.rb +0 -20
  36. data/spec/rdfstar_spec.rb +0 -25
  37. data/spec/reader_spec.rb +0 -883
  38. data/spec/resource_spec.rb +0 -76
  39. data/spec/spec_helper.rb +0 -281
  40. data/spec/streaming_reader_spec.rb +0 -237
  41. data/spec/streaming_writer_spec.rb +0 -145
  42. data/spec/suite_compact_spec.rb +0 -22
  43. data/spec/suite_expand_spec.rb +0 -36
  44. data/spec/suite_flatten_spec.rb +0 -34
  45. data/spec/suite_frame_spec.rb +0 -29
  46. data/spec/suite_from_rdf_spec.rb +0 -22
  47. data/spec/suite_helper.rb +0 -411
  48. data/spec/suite_html_spec.rb +0 -22
  49. data/spec/suite_http_spec.rb +0 -35
  50. data/spec/suite_remote_doc_spec.rb +0 -22
  51. data/spec/suite_to_rdf_spec.rb +0 -30
  52. data/spec/support/extensions.rb +0 -44
  53. data/spec/test-files/test-1-compacted.jsonld +0 -10
  54. data/spec/test-files/test-1-context.jsonld +0 -7
  55. data/spec/test-files/test-1-expanded.jsonld +0 -5
  56. data/spec/test-files/test-1-input.jsonld +0 -10
  57. data/spec/test-files/test-1-rdf.ttl +0 -8
  58. data/spec/test-files/test-2-compacted.jsonld +0 -20
  59. data/spec/test-files/test-2-context.jsonld +0 -7
  60. data/spec/test-files/test-2-expanded.jsonld +0 -16
  61. data/spec/test-files/test-2-input.jsonld +0 -20
  62. data/spec/test-files/test-2-rdf.ttl +0 -14
  63. data/spec/test-files/test-3-compacted.jsonld +0 -11
  64. data/spec/test-files/test-3-context.jsonld +0 -8
  65. data/spec/test-files/test-3-expanded.jsonld +0 -10
  66. data/spec/test-files/test-3-input.jsonld +0 -11
  67. data/spec/test-files/test-3-rdf.ttl +0 -8
  68. data/spec/test-files/test-4-compacted.jsonld +0 -10
  69. data/spec/test-files/test-4-context.jsonld +0 -7
  70. data/spec/test-files/test-4-expanded.jsonld +0 -6
  71. data/spec/test-files/test-4-input.jsonld +0 -10
  72. data/spec/test-files/test-4-rdf.ttl +0 -5
  73. data/spec/test-files/test-5-compacted.jsonld +0 -13
  74. data/spec/test-files/test-5-context.jsonld +0 -7
  75. data/spec/test-files/test-5-expanded.jsonld +0 -9
  76. data/spec/test-files/test-5-input.jsonld +0 -13
  77. data/spec/test-files/test-5-rdf.ttl +0 -7
  78. data/spec/test-files/test-6-compacted.jsonld +0 -10
  79. data/spec/test-files/test-6-context.jsonld +0 -7
  80. data/spec/test-files/test-6-expanded.jsonld +0 -10
  81. data/spec/test-files/test-6-input.jsonld +0 -10
  82. data/spec/test-files/test-6-rdf.ttl +0 -6
  83. data/spec/test-files/test-7-compacted.jsonld +0 -23
  84. data/spec/test-files/test-7-context.jsonld +0 -4
  85. data/spec/test-files/test-7-expanded.jsonld +0 -20
  86. data/spec/test-files/test-7-input.jsonld +0 -23
  87. data/spec/test-files/test-7-rdf.ttl +0 -14
  88. data/spec/test-files/test-8-compacted.jsonld +0 -34
  89. data/spec/test-files/test-8-context.jsonld +0 -11
  90. data/spec/test-files/test-8-expanded.jsonld +0 -24
  91. data/spec/test-files/test-8-frame.jsonld +0 -18
  92. data/spec/test-files/test-8-framed.jsonld +0 -25
  93. data/spec/test-files/test-8-input.jsonld +0 -30
  94. data/spec/test-files/test-8-rdf.ttl +0 -15
  95. data/spec/test-files/test-9-compacted.jsonld +0 -20
  96. data/spec/test-files/test-9-context.jsonld +0 -13
  97. data/spec/test-files/test-9-expanded.jsonld +0 -14
  98. data/spec/test-files/test-9-input.jsonld +0 -12
  99. data/spec/to_rdf_spec.rb +0 -1684
  100. 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