rdf 3.0.13 → 3.1.4
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.
- checksums.yaml +4 -4
- data/AUTHORS +1 -1
- data/README.md +46 -11
- data/VERSION +1 -1
- data/etc/doap.nt +74 -80
- data/lib/rdf.rb +32 -23
- data/lib/rdf/changeset.rb +87 -19
- data/lib/rdf/cli.rb +6 -6
- data/lib/rdf/format.rb +17 -10
- data/lib/rdf/mixin/enumerable.rb +3 -2
- data/lib/rdf/mixin/mutable.rb +5 -15
- data/lib/rdf/mixin/queryable.rb +12 -4
- data/lib/rdf/mixin/writable.rb +9 -14
- data/lib/rdf/model/dataset.rb +1 -1
- data/lib/rdf/model/graph.rb +5 -2
- data/lib/rdf/model/list.rb +5 -5
- data/lib/rdf/model/statement.rb +30 -7
- data/lib/rdf/model/uri.rb +42 -23
- data/lib/rdf/nquads.rb +6 -6
- data/lib/rdf/ntriples.rb +6 -4
- data/lib/rdf/ntriples/reader.rb +32 -7
- data/lib/rdf/ntriples/writer.rb +10 -1
- data/lib/rdf/query.rb +27 -35
- data/lib/rdf/query/hash_pattern_normalizer.rb +14 -12
- data/lib/rdf/query/pattern.rb +51 -18
- data/lib/rdf/query/solution.rb +20 -1
- data/lib/rdf/query/solutions.rb +15 -5
- data/lib/rdf/query/variable.rb +17 -5
- data/lib/rdf/reader.rb +76 -25
- data/lib/rdf/repository.rb +32 -18
- data/lib/rdf/transaction.rb +1 -1
- data/lib/rdf/util.rb +6 -5
- data/lib/rdf/util/cache.rb +2 -2
- data/lib/rdf/util/coercions.rb +60 -0
- data/lib/rdf/util/file.rb +2 -2
- data/lib/rdf/util/logger.rb +7 -7
- data/lib/rdf/vocab/owl.rb +401 -86
- data/lib/rdf/vocab/rdfs.rb +81 -18
- data/lib/rdf/vocab/rdfv.rb +147 -1
- data/lib/rdf/vocab/writer.rb +43 -4
- data/lib/rdf/vocab/xsd.rb +203 -2
- data/lib/rdf/vocabulary.rb +108 -14
- data/lib/rdf/writer.rb +32 -10
- metadata +28 -27
@@ -77,21 +77,23 @@ module RDF; class Query
|
|
77
77
|
##
|
78
78
|
# Returns the normalization of the specified `hash_pattern`.
|
79
79
|
#
|
80
|
-
# @
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
|
80
|
+
# @overload normalize!(hash_pattern, **options)
|
81
|
+
# @param [Hash{Symbol => Object}] hash_pattern (Hash.new)
|
82
|
+
# the query pattern as a hash.
|
83
|
+
# @param [Hash{Symbol => Object}] **options
|
84
|
+
# any additional normalization options.
|
85
|
+
# @option options [String] :anonymous_subject_format ("__%s__")
|
86
|
+
# the string format for anonymous subjects.
|
87
|
+
# @return [Hash{Symbol => Object}]
|
88
|
+
# the resulting query pattern as a normalized hash.
|
89
|
+
def normalize!(*args)
|
90
|
+
hash_pattern = args.shift
|
91
|
+
options = args.shift || {}
|
92
|
+
anonymous_subject_format = options.fetch(:anonymous_subject_format, '__%s__')
|
89
93
|
raise ArgumentError, "invalid hash pattern: #{hash_pattern.inspect}" unless hash_pattern.is_a?(Hash)
|
90
94
|
|
91
95
|
counter = RDF::Query::HashPatternNormalizer::Counter.new
|
92
96
|
|
93
|
-
anonymous_subject_format = (options[:anonymous_subject_format] || '__%s__').to_s
|
94
|
-
|
95
97
|
hash_pattern.inject({}) { |acc, pair|
|
96
98
|
subject, predicate_to_object = pair
|
97
99
|
|
@@ -184,7 +186,7 @@ module RDF; class Query
|
|
184
186
|
# the query pattern as a hash.
|
185
187
|
# @return [Hash{Symbol => Object}]
|
186
188
|
# the resulting query pattern as a normalized hash.
|
187
|
-
def normalize!(
|
189
|
+
def normalize!(hash_pattern)
|
188
190
|
self.class.normalize!(hash_pattern, @options)
|
189
191
|
end
|
190
192
|
end # RDF::Query::HashPatternNormalizer
|
data/lib/rdf/query/pattern.rb
CHANGED
@@ -17,7 +17,7 @@ module RDF; class Query
|
|
17
17
|
end
|
18
18
|
|
19
19
|
##
|
20
|
-
# @overload initialize(
|
20
|
+
# @overload initialize(options = {})
|
21
21
|
# @param [Hash{Symbol => Object}] options
|
22
22
|
# @option options [Variable, Resource, Symbol, nil] :subject (nil)
|
23
23
|
# @option options [Variable, URI, Symbol, nil] :predicate (nil)
|
@@ -26,7 +26,7 @@ module RDF; class Query
|
|
26
26
|
# A graph_name of nil matches any graph, a graph_name of false, matches only the default graph.
|
27
27
|
# @option options [Boolean] :optional (false)
|
28
28
|
#
|
29
|
-
# @overload initialize(subject, predicate, object,
|
29
|
+
# @overload initialize(subject, predicate, object, options = {})
|
30
30
|
# @param [Variable, Resource, Symbol, nil] subject
|
31
31
|
# @param [Variable, URI, Symbol, nil] predicate
|
32
32
|
# @param [Variable, Termm, Symbol, nil] object
|
@@ -50,10 +50,12 @@ module RDF; class Query
|
|
50
50
|
|
51
51
|
# Estmate cost positionally, with variables being least expensive as objects, then predicates, then subjects, then graph_names.
|
52
52
|
# XXX does not consider bound variables, which would need to be dynamically calculated.
|
53
|
-
@cost = (@object.nil? || @object.is_a?(Variable) ?
|
54
|
-
(@predicate.nil? || @predicate.is_a?(Variable) ?
|
55
|
-
(@subject.nil? || @subject.is_a?(Variable) ?
|
56
|
-
(@graph_name.is_a?(Variable) ?
|
53
|
+
@cost = (@object.nil? || @object.is_a?(Variable) ? 8 : 0) +
|
54
|
+
(@predicate.nil? || @predicate.is_a?(Variable) ? 4 : 0) +
|
55
|
+
(@subject.nil? || @subject.is_a?(Variable) ? 2 : 0) +
|
56
|
+
(@graph_name.is_a?(Variable) ? 1 : 0) +
|
57
|
+
(@object.is_a?(Pattern) ? (@object.cost * 4) : 0) +
|
58
|
+
(@subject.is_a?(Pattern) ? (@subject.cost * 2) : 0)
|
57
59
|
super
|
58
60
|
end
|
59
61
|
|
@@ -84,10 +86,10 @@ module RDF; class Query
|
|
84
86
|
# @return [Boolean] `true` or `false`
|
85
87
|
# @since 0.3.0
|
86
88
|
def has_variables?
|
87
|
-
subject.
|
88
|
-
|
89
|
-
|
90
|
-
|
89
|
+
subject && subject.variable? ||
|
90
|
+
predicate && predicate.variable? ||
|
91
|
+
object && object.variable? ||
|
92
|
+
graph_name && graph_name.variable?
|
91
93
|
end
|
92
94
|
alias_method :variables?, :has_variables?
|
93
95
|
|
@@ -117,13 +119,33 @@ module RDF; class Query
|
|
117
119
|
false
|
118
120
|
end
|
119
121
|
|
122
|
+
##
|
123
|
+
# Checks pattern equality against a statement, considering nesting.
|
124
|
+
#
|
125
|
+
# * A pattern which has a pattern as a subject or an object, matches
|
126
|
+
# a statement having a statement as a subject or an object using {#eql?}.
|
127
|
+
#
|
128
|
+
# @param [Statement] other
|
129
|
+
# @return [Boolean]
|
130
|
+
#
|
131
|
+
# @see RDF::URI#==
|
132
|
+
# @see RDF::Node#==
|
133
|
+
# @see RDF::Literal#==
|
134
|
+
# @see RDF::Query::Variable#==
|
135
|
+
def eql?(other)
|
136
|
+
return false unless other.is_a?(Statement) && (self.graph_name || false) == (other.graph_name || false)
|
137
|
+
|
138
|
+
predicate == other.predicate &&
|
139
|
+
(subject.is_a?(Pattern) ? subject.eql?(other.subject) : subject == other.subject) &&
|
140
|
+
(object.is_a?(Pattern) ? object.eql?(other.object) : object == other.object)
|
141
|
+
end
|
142
|
+
|
120
143
|
##
|
121
144
|
# Executes this query pattern on the given `queryable` object.
|
122
145
|
#
|
123
146
|
# Values are matched using using Queryable#query_pattern.
|
124
147
|
#
|
125
|
-
# If the optional `bindings` are given, variables will be substituted with their values
|
126
|
-
# when executing the query.
|
148
|
+
# If the optional `bindings` are given, variables will be substituted with their values when executing the query.
|
127
149
|
#
|
128
150
|
# To match triples only in the default graph, set graph_name to `false`.
|
129
151
|
#
|
@@ -198,6 +220,8 @@ module RDF; class Query
|
|
198
220
|
solution[predicate.to_sym] = statement.predicate if predicate.is_a?(Variable)
|
199
221
|
solution[object.to_sym] = statement.object if object.is_a?(Variable)
|
200
222
|
solution[graph_name.to_sym] = statement.graph_name if graph_name.is_a?(Variable)
|
223
|
+
solution.merge!(subject.solution(statement.subject)) if subject.is_a?(Pattern)
|
224
|
+
solution.merge!(object.solution(statement.object)) if object.is_a?(Pattern)
|
201
225
|
end
|
202
226
|
end
|
203
227
|
|
@@ -229,7 +253,8 @@ module RDF; class Query
|
|
229
253
|
# @return [Integer] (0..3)
|
230
254
|
def variable_count
|
231
255
|
[subject, predicate, object, graph_name].inject(0) do |memo, term|
|
232
|
-
memo += (term.is_a?(Variable) ? 1 :
|
256
|
+
memo += (term.is_a?(Variable) ? 1 :
|
257
|
+
(term.is_a?(Pattern) ? term.variable_count : 0))
|
233
258
|
end
|
234
259
|
end
|
235
260
|
alias_method :cardinality, :variable_count
|
@@ -243,7 +268,7 @@ module RDF; class Query
|
|
243
268
|
# @return [Hash{Symbol => Variable}]
|
244
269
|
def variables
|
245
270
|
[subject, predicate, object, graph_name].inject({}) do |memo, term|
|
246
|
-
term.
|
271
|
+
term && term.variable? ? memo.merge(term.variables) : memo
|
247
272
|
end
|
248
273
|
end
|
249
274
|
|
@@ -254,8 +279,10 @@ module RDF; class Query
|
|
254
279
|
# @return [self]
|
255
280
|
def bind(solution)
|
256
281
|
self.to_quad.each_with_index do |term, index|
|
257
|
-
if term
|
282
|
+
if term.is_a?(Variable) && solution[term]
|
258
283
|
self[index] = solution[term]
|
284
|
+
elsif term.is_a?(Pattern)
|
285
|
+
term.bind(solution)
|
259
286
|
end
|
260
287
|
end
|
261
288
|
self
|
@@ -283,9 +310,9 @@ module RDF; class Query
|
|
283
310
|
# @return [Hash{Symbol => RDF::Term}]
|
284
311
|
def bindings
|
285
312
|
bindings = {}
|
286
|
-
bindings.merge!(subject.bindings) if subject.
|
313
|
+
bindings.merge!(subject.bindings) if subject && subject.variable?
|
287
314
|
bindings.merge!(predicate.bindings) if predicate.is_a?(Variable)
|
288
|
-
bindings.merge!(object.bindings) if object.
|
315
|
+
bindings.merge!(object.bindings) if object && object.variable?
|
289
316
|
bindings.merge!(graph_name.bindings) if graph_name.is_a?(Variable)
|
290
317
|
bindings
|
291
318
|
end
|
@@ -330,7 +357,13 @@ module RDF; class Query
|
|
330
357
|
StringIO.open do |buffer| # FIXME in RDF::Statement
|
331
358
|
buffer << 'OPTIONAL ' if optional?
|
332
359
|
buffer << [subject, predicate, object].map do |r|
|
333
|
-
r.is_a?(RDF::Query::Variable)
|
360
|
+
if r.is_a?(RDF::Query::Variable)
|
361
|
+
r.to_s
|
362
|
+
elsif r.is_a?(RDF::Query::Pattern)
|
363
|
+
"<<#{r.to_s[0..-3]}>>"
|
364
|
+
else
|
365
|
+
RDF::NTriples.serialize(r)
|
366
|
+
end
|
334
367
|
end.join(" ")
|
335
368
|
buffer << case graph_name
|
336
369
|
when nil, false then " ."
|
data/lib/rdf/query/solution.rb
CHANGED
@@ -195,12 +195,31 @@ class RDF::Query
|
|
195
195
|
# Merges the bindings from the given `other` query solution into this
|
196
196
|
# one, overwriting any existing ones having the same name.
|
197
197
|
#
|
198
|
+
# ## RDFStar (RDF*)
|
199
|
+
#
|
200
|
+
# If merging a binding for a statement to a pattern,
|
201
|
+
# merge their embedded solutions.
|
202
|
+
#
|
198
203
|
# @param [RDF::Query::Solution, #to_h] other
|
199
204
|
# another query solution or hash bindings
|
200
205
|
# @return [void] self
|
201
206
|
# @since 0.3.0
|
202
207
|
def merge!(other)
|
203
|
-
@bindings.merge!(other.to_h)
|
208
|
+
@bindings.merge!(other.to_h) do |key, v1, v2|
|
209
|
+
# Don't merge a pattern over a statement
|
210
|
+
# This happens because JOIN does a reverse merge,
|
211
|
+
# and a pattern is set in v2.
|
212
|
+
v2.is_a?(Pattern) ? v1 : v2
|
213
|
+
end
|
214
|
+
# Merge bindings from patterns
|
215
|
+
embedded_solutions = []
|
216
|
+
@bindings.each do |k, v|
|
217
|
+
if v.is_a?(Pattern) && other[k].is_a?(RDF::Statement)
|
218
|
+
embedded_solutions << v.solution(other[k])
|
219
|
+
end
|
220
|
+
end
|
221
|
+
# Merge embedded solutions
|
222
|
+
embedded_solutions.each {|soln| merge!(soln)}
|
204
223
|
self
|
205
224
|
end
|
206
225
|
|
data/lib/rdf/query/solutions.rb
CHANGED
@@ -66,13 +66,15 @@ module RDF; class Query
|
|
66
66
|
#
|
67
67
|
# @return [Array<Symbol>]
|
68
68
|
def variable_names
|
69
|
-
|
70
|
-
|
71
|
-
|
69
|
+
@variable_names ||= begin
|
70
|
+
variables = self.inject({}) do |result, solution|
|
71
|
+
solution.each_name do |name|
|
72
|
+
result[name] ||= true
|
73
|
+
end
|
74
|
+
result
|
72
75
|
end
|
73
|
-
|
76
|
+
variables.keys
|
74
77
|
end
|
75
|
-
variables.keys
|
76
78
|
end
|
77
79
|
|
78
80
|
##
|
@@ -136,6 +138,7 @@ module RDF; class Query
|
|
136
138
|
# @yieldreturn [Boolean]
|
137
139
|
# @return [self]
|
138
140
|
def filter(criteria = {})
|
141
|
+
@variable_names = nil
|
139
142
|
if block_given?
|
140
143
|
self.reject! do |solution|
|
141
144
|
!yield(solution.is_a?(Solution) ? solution : Solution.new(solution))
|
@@ -223,6 +226,13 @@ module RDF; class Query
|
|
223
226
|
solution.bindings.delete_if { |k, v| !variables.include?(k.to_sym) }
|
224
227
|
end
|
225
228
|
end
|
229
|
+
|
230
|
+
# Make sure variable_names are ordered by projected variables
|
231
|
+
projected_vars, vars = variables.map(&:to_sym), variable_names
|
232
|
+
vars = variable_names
|
233
|
+
|
234
|
+
# Maintain projected order, and add any non-projected variables
|
235
|
+
@variable_names = (projected_vars & vars) + (vars - projected_vars)
|
226
236
|
self
|
227
237
|
end
|
228
238
|
alias_method :select, :project
|
data/lib/rdf/query/variable.rb
CHANGED
@@ -157,12 +157,24 @@ class RDF::Query
|
|
157
157
|
##
|
158
158
|
# Rebinds this variable to the given `value`.
|
159
159
|
#
|
160
|
-
# @
|
161
|
-
#
|
160
|
+
# @overload bind(value)
|
161
|
+
# @param [RDF::Query::Solution] value
|
162
|
+
# @return [self] the bound variable
|
163
|
+
#
|
164
|
+
# @overload bind(value)
|
165
|
+
# @param [RDF::Term] value
|
166
|
+
# @return [RDF::Term] the previous value, if any.
|
162
167
|
def bind(value)
|
163
|
-
|
164
|
-
|
165
|
-
|
168
|
+
if value.is_a?(RDF::Query::Solution)
|
169
|
+
self.value = value.to_h.fetch(name, self.value)
|
170
|
+
self
|
171
|
+
else
|
172
|
+
warn "[DEPRECATION] RDF::Query::Variable#bind should be used with a solution, not a term.\n" +
|
173
|
+
"Called from #{Gem.location_of_caller.join(':')}"
|
174
|
+
old_value = self.value
|
175
|
+
self.value = value
|
176
|
+
old_value
|
177
|
+
end
|
166
178
|
end
|
167
179
|
alias_method :bind!, :bind
|
168
180
|
|
data/lib/rdf/reader.rb
CHANGED
@@ -88,9 +88,15 @@ module RDF
|
|
88
88
|
# @yieldreturn [String] another way to provide a sample, allows lazy for retrieving the sample.
|
89
89
|
#
|
90
90
|
# @return [Class]
|
91
|
-
def self.for(
|
92
|
-
|
93
|
-
|
91
|
+
def self.for(*arg, &block)
|
92
|
+
case arg.length
|
93
|
+
when 0 then arg = nil
|
94
|
+
when 1 then arg = arg.first
|
95
|
+
else
|
96
|
+
raise ArgumentError, "Format.for accepts zero or one argument, got #{arg.length}."
|
97
|
+
end
|
98
|
+
arg = arg.merge(has_reader: true) if arg.is_a?(Hash)
|
99
|
+
if format = self.format || Format.for(arg, &block)
|
94
100
|
format.reader
|
95
101
|
end
|
96
102
|
end
|
@@ -115,6 +121,12 @@ module RDF
|
|
115
121
|
# @return [Array<RDF::CLI::Option>]
|
116
122
|
def self.options
|
117
123
|
[
|
124
|
+
RDF::CLI::Option.new(
|
125
|
+
symbol: :base_uri,
|
126
|
+
control: :url,
|
127
|
+
datatype: RDF::URI,
|
128
|
+
on: ["--uri URI"],
|
129
|
+
description: "Base URI of input file, defaults to the filename.") {|arg| RDF::URI(arg)},
|
118
130
|
RDF::CLI::Option.new(
|
119
131
|
symbol: :canonicalize,
|
120
132
|
datatype: TrueClass,
|
@@ -147,11 +159,11 @@ module RDF
|
|
147
159
|
end
|
148
160
|
end,
|
149
161
|
RDF::CLI::Option.new(
|
150
|
-
symbol: :
|
151
|
-
control: :
|
152
|
-
datatype:
|
153
|
-
on: ["--
|
154
|
-
description: "
|
162
|
+
symbol: :rdfstar,
|
163
|
+
control: :select,
|
164
|
+
datatype: [:PG, :SA],
|
165
|
+
on: ["--rdf-star MODE"],
|
166
|
+
description: "Parse RDF*, either in Property Graph mode (PG) or Separate Assertions mode (SA).") {|arg| arg.to_sym},
|
155
167
|
RDF::CLI::Option.new(
|
156
168
|
symbol: :validate,
|
157
169
|
datatype: TrueClass,
|
@@ -206,7 +218,7 @@ module RDF
|
|
206
218
|
headers['Accept'] ||= (self.format.accept_type + %w(*/*;q=0.1)).join(", ")
|
207
219
|
end
|
208
220
|
|
209
|
-
Util::File.open_file(filename, options) do |file|
|
221
|
+
Util::File.open_file(filename, **options) do |file|
|
210
222
|
format_options = options.dup
|
211
223
|
format_options[:content_type] ||= file.content_type if
|
212
224
|
file.respond_to?(:content_type) &&
|
@@ -229,7 +241,7 @@ module RDF
|
|
229
241
|
options[:filename] ||= filename
|
230
242
|
|
231
243
|
if reader
|
232
|
-
reader.new(file, options, &block)
|
244
|
+
reader.new(file, **options, &block)
|
233
245
|
else
|
234
246
|
raise FormatError, "unknown RDF format: #{format_options.inspect}#{"\nThis may be resolved with a require of the 'linkeddata' gem." unless Object.const_defined?(:LinkedData)}"
|
235
247
|
end
|
@@ -255,42 +267,48 @@ module RDF
|
|
255
267
|
#
|
256
268
|
# @param [IO, File, String] input
|
257
269
|
# the input stream to read
|
258
|
-
# @param [
|
259
|
-
# the
|
260
|
-
#
|
261
|
-
# whether to validate the parsed statements and values
|
270
|
+
# @param [#to_s] base_uri (nil)
|
271
|
+
# the base URI to use when resolving relative URIs (not supported by
|
272
|
+
# all readers)
|
262
273
|
# @param [Boolean] canonicalize (false)
|
263
274
|
# whether to canonicalize parsed literals
|
275
|
+
# @param [Encoding] encoding (Encoding::UTF_8)
|
276
|
+
# the encoding of the input stream
|
264
277
|
# @param [Boolean] intern (true)
|
265
278
|
# whether to intern all parsed URIs
|
279
|
+
# @param [:PG, :SA] rdfstar (nil)
|
280
|
+
# support parsing RDF* statement resources.
|
281
|
+
# If `:PG`, referenced statements are also emitted.
|
282
|
+
# If `:SA`, referenced statements are not emitted.
|
266
283
|
# @param [Hash] prefixes (Hash.new)
|
267
284
|
# the prefix mappings to use (not supported by all readers)
|
268
|
-
# @param [#to_s] base_uri (nil)
|
269
|
-
# the base URI to use when resolving relative URIs (not supported by
|
270
|
-
# all readers)
|
271
285
|
# @param [Hash{Symbol => Object}] options
|
272
286
|
# any additional options
|
287
|
+
# @param [Boolean] validate (false)
|
288
|
+
# whether to validate the parsed statements and values
|
273
289
|
# @yield [reader] `self`
|
274
290
|
# @yieldparam [RDF::Reader] reader
|
275
291
|
# @yieldreturn [void] ignored
|
276
292
|
def initialize(input = $stdin,
|
277
|
-
|
278
|
-
validate: false,
|
293
|
+
base_uri: nil,
|
279
294
|
canonicalize: false,
|
295
|
+
encoding: Encoding::UTF_8,
|
280
296
|
intern: true,
|
281
297
|
prefixes: Hash.new,
|
282
|
-
|
298
|
+
rdfstar: nil,
|
299
|
+
validate: false,
|
283
300
|
**options,
|
284
301
|
&block)
|
285
302
|
|
286
303
|
base_uri ||= input.base_uri if input.respond_to?(:base_uri)
|
287
304
|
@options = options.merge({
|
288
|
-
|
289
|
-
validate: validate,
|
305
|
+
base_uri: base_uri,
|
290
306
|
canonicalize: canonicalize,
|
307
|
+
encoding: encoding,
|
291
308
|
intern: intern,
|
292
309
|
prefixes: prefixes,
|
293
|
-
|
310
|
+
rdfstar: rdfstar,
|
311
|
+
validate: validate
|
294
312
|
})
|
295
313
|
|
296
314
|
@input = case input
|
@@ -383,6 +401,9 @@ module RDF
|
|
383
401
|
# Statements are yielded in the order that they are read from the input
|
384
402
|
# stream.
|
385
403
|
#
|
404
|
+
# If the `rdfstar` option is `:PG` and triples include
|
405
|
+
# embedded statements, they are also enumerated.
|
406
|
+
#
|
386
407
|
# @overload each_statement
|
387
408
|
# @yield [statement]
|
388
409
|
# each statement
|
@@ -399,7 +420,11 @@ module RDF
|
|
399
420
|
def each_statement(&block)
|
400
421
|
if block_given?
|
401
422
|
begin
|
402
|
-
loop
|
423
|
+
loop do
|
424
|
+
st = read_statement
|
425
|
+
block.call(st)
|
426
|
+
each_pg_statement(st, &block) if options[:rdfstar] == :PG
|
427
|
+
end
|
403
428
|
rescue EOFError
|
404
429
|
rewind rescue nil
|
405
430
|
end
|
@@ -416,6 +441,9 @@ module RDF
|
|
416
441
|
# Triples are yielded in the order that they are read from the input
|
417
442
|
# stream.
|
418
443
|
#
|
444
|
+
# If the `rdfstar` option is `:PG` and triples include
|
445
|
+
# embedded statements, they are also enumerated.
|
446
|
+
#
|
419
447
|
# @overload each_triple
|
420
448
|
# @yield [subject, predicate, object]
|
421
449
|
# each triple
|
@@ -433,7 +461,14 @@ module RDF
|
|
433
461
|
def each_triple(&block)
|
434
462
|
if block_given?
|
435
463
|
begin
|
436
|
-
loop
|
464
|
+
loop do
|
465
|
+
triple = read_triple
|
466
|
+
block.call(*triple)
|
467
|
+
if options[:rdfstar] == :PG
|
468
|
+
block.call(*triple[0].to_a) if triple[0].is_a?(Statement)
|
469
|
+
block.call(*triple[2].to_a) if triple[2].is_a?(Statement)
|
470
|
+
end
|
471
|
+
end
|
437
472
|
rescue EOFError
|
438
473
|
rewind rescue nil
|
439
474
|
end
|
@@ -544,6 +579,22 @@ module RDF
|
|
544
579
|
log_error("Expected object (found: #{current_line.inspect})", lineno: lineno, exception: RDF::ReaderError)
|
545
580
|
end
|
546
581
|
|
582
|
+
##
|
583
|
+
# Recursively emit embedded statements in Property Graph mode
|
584
|
+
#
|
585
|
+
# @param [RDF::Statement] statement
|
586
|
+
def each_pg_statement(statement, &block)
|
587
|
+
if statement.subject.is_a?(Statement)
|
588
|
+
block.call(statement.subject)
|
589
|
+
each_pg_statement(statement.subject, &block)
|
590
|
+
end
|
591
|
+
|
592
|
+
if statement.object.is_a?(Statement)
|
593
|
+
block.call(statement.object)
|
594
|
+
each_pg_statement(statement.object, &block)
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
547
598
|
public
|
548
599
|
##
|
549
600
|
# Returns the encoding of the input stream.
|