rdf 3.1.1 → 3.1.2
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 +43 -9
- data/VERSION +1 -1
- data/etc/doap.nt +74 -80
- data/lib/rdf.rb +13 -11
- data/lib/rdf/mixin/enumerable.rb +1 -0
- data/lib/rdf/mixin/queryable.rb +8 -0
- data/lib/rdf/mixin/writable.rb +7 -0
- data/lib/rdf/model/dataset.rb +1 -1
- data/lib/rdf/model/graph.rb +3 -0
- data/lib/rdf/model/statement.rb +30 -7
- data/lib/rdf/model/uri.rb +2 -2
- data/lib/rdf/nquads.rb +2 -2
- data/lib/rdf/ntriples.rb +6 -4
- data/lib/rdf/ntriples/reader.rb +25 -3
- data/lib/rdf/ntriples/writer.rb +9 -0
- data/lib/rdf/query.rb +4 -1
- data/lib/rdf/query/hash_pattern_normalizer.rb +9 -8
- data/lib/rdf/query/pattern.rb +49 -16
- 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 +65 -20
- data/lib/rdf/repository.rb +19 -12
- data/lib/rdf/util/cache.rb +2 -2
- data/lib/rdf/vocab/owl.rb +401 -86
- data/lib/rdf/vocab/rdfs.rb +81 -18
- data/lib/rdf/vocab/rdfv.rb +122 -1
- data/lib/rdf/vocab/writer.rb +41 -3
- data/lib/rdf/vocab/xsd.rb +202 -1
- data/lib/rdf/vocabulary.rb +69 -11
- data/lib/rdf/writer.rb +21 -5
- metadata +4 -4
data/lib/rdf/model/uri.rb
CHANGED
@@ -400,7 +400,7 @@ module RDF
|
|
400
400
|
# @example Joining two URIs
|
401
401
|
# RDF::URI.new('http://example.org/foo/bar').join('/foo')
|
402
402
|
# #=> RDF::URI('http://example.org/foo')
|
403
|
-
# @see <
|
403
|
+
# @see <https://github.com/ruby-rdf/rdf-spec/blob/master/lib/rdf/spec/uri.rb>
|
404
404
|
# @see <http://tools.ietf.org/html/rfc3986#section-5.2>
|
405
405
|
# @see RDF::URI#/
|
406
406
|
# @see RDF::URI#+
|
@@ -471,7 +471,7 @@ module RDF
|
|
471
471
|
# @see RDF::URI#+
|
472
472
|
# @see RDF::URI#join
|
473
473
|
# @see <http://tools.ietf.org/html/rfc3986#section-5.2>
|
474
|
-
# @see <
|
474
|
+
# @see <https://github.com/ruby-rdf/rdf-spec/blob/master/lib/rdf/spec/uri.rb>
|
475
475
|
# @example Building a HTTP URL
|
476
476
|
# RDF::URI.new('http://example.org') / 'jhacker' / 'foaf.ttl'
|
477
477
|
# #=> RDF::URI('http://example.org/jhacker/foaf.ttl')
|
data/lib/rdf/nquads.rb
CHANGED
@@ -69,9 +69,9 @@ module RDF
|
|
69
69
|
|
70
70
|
begin
|
71
71
|
unless blank? || read_comment
|
72
|
-
subject = read_uriref || read_node || fail_subject
|
72
|
+
subject = read_uriref || read_node || read_rdfstar || fail_subject
|
73
73
|
predicate = read_uriref(intern: true) || fail_predicate
|
74
|
-
object = read_uriref || read_node || read_literal || fail_object
|
74
|
+
object = read_uriref || read_node || read_literal || read_rdfstar || fail_object
|
75
75
|
graph_name = read_uriref || read_node
|
76
76
|
if validate? && !read_eos
|
77
77
|
log_error("Expected end of statement (found: #{current_line.inspect})", lineno: lineno, exception: RDF::ReaderError)
|
data/lib/rdf/ntriples.rb
CHANGED
@@ -15,15 +15,17 @@ module RDF
|
|
15
15
|
#
|
16
16
|
# <https://rubygems.org/gems/rdf> <http://purl.org/dc/terms/title> "rdf" .
|
17
17
|
#
|
18
|
-
#
|
19
|
-
#
|
18
|
+
# ## RDFStar (RDF*)
|
19
|
+
#
|
20
|
+
# Supports statements as resources using `<<s p o>>`.
|
21
|
+
#
|
22
|
+
# ## Installation
|
20
23
|
#
|
21
24
|
# This is the only RDF serialization format that is directly supported by
|
22
25
|
# RDF.rb. Support for other formats is available in the form of add-on
|
23
26
|
# gems, e.g. 'rdf-xml' or 'rdf-json'.
|
24
27
|
#
|
25
|
-
# Documentation
|
26
|
-
# -------------
|
28
|
+
# ## Documentation
|
27
29
|
#
|
28
30
|
# * {RDF::NTriples::Format}
|
29
31
|
# * {RDF::NTriples::Reader}
|
data/lib/rdf/ntriples/reader.rb
CHANGED
@@ -25,6 +25,10 @@ module RDF::NTriples
|
|
25
25
|
# end
|
26
26
|
# end
|
27
27
|
#
|
28
|
+
# ** RDFStar (RDF*)
|
29
|
+
#
|
30
|
+
# Supports statements as resources using `<<s p o>>`.
|
31
|
+
#
|
28
32
|
# @see http://www.w3.org/TR/rdf-testcases/#ntriples
|
29
33
|
# @see http://www.w3.org/TR/n-triples/
|
30
34
|
class Reader < RDF::Reader
|
@@ -70,6 +74,10 @@ module RDF::NTriples
|
|
70
74
|
# 22
|
71
75
|
STRING_LITERAL_QUOTE = /"((?:[^\"\\\n\r]|#{ECHAR}|#{UCHAR})*)"/.freeze
|
72
76
|
|
77
|
+
# RDF*
|
78
|
+
ST_START = /^<</.freeze
|
79
|
+
ST_END = /^\s*>>/.freeze
|
80
|
+
|
73
81
|
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar
|
74
82
|
COMMENT = /^#\s*(.*)$/.freeze
|
75
83
|
NODEID = /^#{BLANK_NODE_LABEL}/.freeze
|
@@ -202,7 +210,7 @@ module RDF::NTriples
|
|
202
210
|
begin
|
203
211
|
read_statement
|
204
212
|
rescue RDF::ReaderError
|
205
|
-
value = read_uriref || read_node || read_literal
|
213
|
+
value = read_uriref || read_node || read_literal || read_rdfstar
|
206
214
|
log_recover
|
207
215
|
value
|
208
216
|
end
|
@@ -218,9 +226,9 @@ module RDF::NTriples
|
|
218
226
|
|
219
227
|
begin
|
220
228
|
unless blank? || read_comment
|
221
|
-
subject = read_uriref || read_node || fail_subject
|
229
|
+
subject = read_uriref || read_node || read_rdfstar || fail_subject
|
222
230
|
predicate = read_uriref(intern: true) || fail_predicate
|
223
|
-
object = read_uriref || read_node || read_literal || fail_object
|
231
|
+
object = read_uriref || read_node || read_literal || read_rdfstar || fail_object
|
224
232
|
|
225
233
|
if validate? && !read_eos
|
226
234
|
log_error("Expected end of statement (found: #{current_line.inspect})", lineno: lineno, exception: RDF::ReaderError)
|
@@ -234,6 +242,20 @@ module RDF::NTriples
|
|
234
242
|
end
|
235
243
|
end
|
236
244
|
|
245
|
+
##
|
246
|
+
# @return [RDF::Statement]
|
247
|
+
def read_rdfstar
|
248
|
+
if @options[:rdfstar] && match(ST_START)
|
249
|
+
subject = read_uriref || read_node || read_rdfstar || fail_subject
|
250
|
+
predicate = read_uriref(intern: true) || fail_predicate
|
251
|
+
object = read_uriref || read_node || read_literal || read_rdfstar || fail_object
|
252
|
+
if !match(ST_END)
|
253
|
+
log_error("Expected end of statement (found: #{current_line.inspect})", lineno: lineno, exception: RDF::ReaderError)
|
254
|
+
end
|
255
|
+
RDF::Statement.new(subject, predicate, object)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
237
259
|
##
|
238
260
|
# @return [Boolean]
|
239
261
|
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar (comment)
|
data/lib/rdf/ntriples/writer.rb
CHANGED
@@ -221,6 +221,15 @@ module RDF::NTriples
|
|
221
221
|
format_triple(*statement.to_triple, **options)
|
222
222
|
end
|
223
223
|
|
224
|
+
##
|
225
|
+
# Returns the N-Triples representation of an RDF* reified statement.
|
226
|
+
#
|
227
|
+
# @param [RDF::Statement] statement
|
228
|
+
# @param [Hash{Symbol => Object}] options ({})
|
229
|
+
# @return [String]
|
230
|
+
def format_rdfstar(statement, **options)
|
231
|
+
"<<%s %s %s>>" % statement.to_a.map { |value| format_term(value, **options) }
|
232
|
+
end
|
224
233
|
##
|
225
234
|
# Returns the N-Triples representation of a triple.
|
226
235
|
#
|
data/lib/rdf/query.rb
CHANGED
@@ -289,6 +289,8 @@ module RDF
|
|
289
289
|
# any additional keyword options
|
290
290
|
# @option options [Hash{Symbol => RDF::Term}] bindings
|
291
291
|
# optional variable bindings to use
|
292
|
+
# @option options [Boolean] :optimize
|
293
|
+
# Optimize query before execution.
|
292
294
|
# @option options [RDF::Query::Solutions] solutions
|
293
295
|
# optional initial solutions for chained queries
|
294
296
|
# @yield [solution]
|
@@ -311,6 +313,7 @@ module RDF
|
|
311
313
|
return @solutions
|
312
314
|
end
|
313
315
|
|
316
|
+
self.optimize! if options[:optimize]
|
314
317
|
patterns = @patterns
|
315
318
|
graph_name = name if graph_name.nil?
|
316
319
|
@graph_name = graph_name unless graph_name.nil?
|
@@ -505,7 +508,7 @@ module RDF
|
|
505
508
|
# @return [RDF::Query]
|
506
509
|
def dup
|
507
510
|
patterns = @patterns.map {|p| p.dup}
|
508
|
-
Query.new(patterns, solutions: @solutions.dup, **options)
|
511
|
+
Query.new(patterns, graph_name: graph_name, solutions: @solutions.dup, **options)
|
509
512
|
end
|
510
513
|
|
511
514
|
##
|
@@ -77,14 +77,15 @@ 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
|
-
#
|
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.
|
88
89
|
def normalize!(*args)
|
89
90
|
hash_pattern = args.shift
|
90
91
|
options = args.shift || {}
|
data/lib/rdf/query/pattern.rb
CHANGED
@@ -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
@@ -121,6 +121,12 @@ module RDF
|
|
121
121
|
# @return [Array<RDF::CLI::Option>]
|
122
122
|
def self.options
|
123
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)},
|
124
130
|
RDF::CLI::Option.new(
|
125
131
|
symbol: :canonicalize,
|
126
132
|
datatype: TrueClass,
|
@@ -153,11 +159,11 @@ module RDF
|
|
153
159
|
end
|
154
160
|
end,
|
155
161
|
RDF::CLI::Option.new(
|
156
|
-
symbol: :
|
157
|
-
control: :
|
158
|
-
datatype:
|
159
|
-
on: ["--
|
160
|
-
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},
|
161
167
|
RDF::CLI::Option.new(
|
162
168
|
symbol: :validate,
|
163
169
|
datatype: TrueClass,
|
@@ -261,42 +267,48 @@ module RDF
|
|
261
267
|
#
|
262
268
|
# @param [IO, File, String] input
|
263
269
|
# the input stream to read
|
264
|
-
# @param [
|
265
|
-
# the
|
266
|
-
#
|
267
|
-
# 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)
|
268
273
|
# @param [Boolean] canonicalize (false)
|
269
274
|
# whether to canonicalize parsed literals
|
275
|
+
# @param [Encoding] encoding (Encoding::UTF_8)
|
276
|
+
# the encoding of the input stream
|
270
277
|
# @param [Boolean] intern (true)
|
271
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.
|
272
283
|
# @param [Hash] prefixes (Hash.new)
|
273
284
|
# the prefix mappings to use (not supported by all readers)
|
274
|
-
# @param [#to_s] base_uri (nil)
|
275
|
-
# the base URI to use when resolving relative URIs (not supported by
|
276
|
-
# all readers)
|
277
285
|
# @param [Hash{Symbol => Object}] options
|
278
286
|
# any additional options
|
287
|
+
# @param [Boolean] validate (false)
|
288
|
+
# whether to validate the parsed statements and values
|
279
289
|
# @yield [reader] `self`
|
280
290
|
# @yieldparam [RDF::Reader] reader
|
281
291
|
# @yieldreturn [void] ignored
|
282
292
|
def initialize(input = $stdin,
|
283
|
-
|
284
|
-
validate: false,
|
293
|
+
base_uri: nil,
|
285
294
|
canonicalize: false,
|
295
|
+
encoding: Encoding::UTF_8,
|
286
296
|
intern: true,
|
287
297
|
prefixes: Hash.new,
|
288
|
-
|
298
|
+
rdfstar: nil,
|
299
|
+
validate: false,
|
289
300
|
**options,
|
290
301
|
&block)
|
291
302
|
|
292
303
|
base_uri ||= input.base_uri if input.respond_to?(:base_uri)
|
293
304
|
@options = options.merge({
|
294
|
-
|
295
|
-
validate: validate,
|
305
|
+
base_uri: base_uri,
|
296
306
|
canonicalize: canonicalize,
|
307
|
+
encoding: encoding,
|
297
308
|
intern: intern,
|
298
309
|
prefixes: prefixes,
|
299
|
-
|
310
|
+
rdfstar: rdfstar,
|
311
|
+
validate: validate
|
300
312
|
})
|
301
313
|
|
302
314
|
@input = case input
|
@@ -389,6 +401,9 @@ module RDF
|
|
389
401
|
# Statements are yielded in the order that they are read from the input
|
390
402
|
# stream.
|
391
403
|
#
|
404
|
+
# If the `rdfstar` option is `:PG` and triples include
|
405
|
+
# embedded statements, they are also enumerated.
|
406
|
+
#
|
392
407
|
# @overload each_statement
|
393
408
|
# @yield [statement]
|
394
409
|
# each statement
|
@@ -405,7 +420,11 @@ module RDF
|
|
405
420
|
def each_statement(&block)
|
406
421
|
if block_given?
|
407
422
|
begin
|
408
|
-
loop
|
423
|
+
loop do
|
424
|
+
st = read_statement
|
425
|
+
block.call(st)
|
426
|
+
each_pg_statement(st, &block) if options[:rdfstar] == :PG
|
427
|
+
end
|
409
428
|
rescue EOFError
|
410
429
|
rewind rescue nil
|
411
430
|
end
|
@@ -422,6 +441,9 @@ module RDF
|
|
422
441
|
# Triples are yielded in the order that they are read from the input
|
423
442
|
# stream.
|
424
443
|
#
|
444
|
+
# If the `rdfstar` option is `:PG` and triples include
|
445
|
+
# embedded statements, they are also enumerated.
|
446
|
+
#
|
425
447
|
# @overload each_triple
|
426
448
|
# @yield [subject, predicate, object]
|
427
449
|
# each triple
|
@@ -439,7 +461,14 @@ module RDF
|
|
439
461
|
def each_triple(&block)
|
440
462
|
if block_given?
|
441
463
|
begin
|
442
|
-
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
|
443
472
|
rescue EOFError
|
444
473
|
rewind rescue nil
|
445
474
|
end
|
@@ -550,6 +579,22 @@ module RDF
|
|
550
579
|
log_error("Expected object (found: #{current_line.inspect})", lineno: lineno, exception: RDF::ReaderError)
|
551
580
|
end
|
552
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
|
+
|
553
598
|
public
|
554
599
|
##
|
555
600
|
# Returns the encoding of the input stream.
|