rdf 1.1.0p4 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +6 -14
- data/README +33 -33
- data/VERSION +1 -1
- data/lib/rdf.rb +60 -12
- data/lib/rdf/cli.rb +7 -1
- data/lib/rdf/cli/vocab-loader.rb +240 -0
- data/lib/rdf/format.rb +2 -2
- data/lib/rdf/mixin/enumerable.rb +12 -4
- data/lib/rdf/mixin/queryable.rb +13 -13
- data/lib/rdf/model/graph.rb +5 -4
- data/lib/rdf/model/list.rb +15 -4
- data/lib/rdf/model/literal.rb +2 -1
- data/lib/rdf/model/statement.rb +10 -1
- data/lib/rdf/model/term.rb +8 -0
- data/lib/rdf/model/uri.rb +107 -2
- data/lib/rdf/model/value.rb +8 -0
- data/lib/rdf/ntriples/reader.rb +5 -4
- data/lib/rdf/query.rb +47 -12
- data/lib/rdf/query/solutions.rb +29 -29
- data/lib/rdf/reader.rb +13 -3
- data/lib/rdf/repository.rb +1 -0
- data/lib/rdf/util/file.rb +86 -6
- data/lib/rdf/vocab.rb +158 -58
- data/lib/rdf/vocab/cc.rb +28 -11
- data/lib/rdf/vocab/cert.rb +127 -9
- data/lib/rdf/vocab/dc.rb +242 -60
- data/lib/rdf/vocab/dc11.rb +42 -20
- data/lib/rdf/vocab/doap.rb +121 -42
- data/lib/rdf/vocab/exif.rb +540 -165
- data/lib/rdf/vocab/foaf.rb +353 -66
- data/lib/rdf/vocab/geo.rb +40 -10
- data/lib/rdf/vocab/gr.rb +1094 -0
- data/lib/rdf/vocab/http.rb +81 -23
- data/lib/rdf/vocab/ical.rb +361 -0
- data/lib/rdf/vocab/ma.rb +281 -69
- data/lib/rdf/vocab/og.rb +98 -0
- data/lib/rdf/vocab/owl.rb +226 -56
- data/lib/rdf/vocab/prov.rb +489 -0
- data/lib/rdf/vocab/rdfs.rb +38 -14
- data/lib/rdf/vocab/rsa.rb +25 -9
- data/lib/rdf/vocab/rss.rb +29 -11
- data/lib/rdf/vocab/schema.rb +3729 -647
- data/lib/rdf/vocab/sioc.rb +224 -89
- data/lib/rdf/vocab/skos.rb +141 -33
- data/lib/rdf/vocab/skosxl.rb +43 -0
- data/lib/rdf/vocab/v.rb +154 -0
- data/lib/rdf/vocab/vcard.rb +337 -0
- data/lib/rdf/vocab/void.rb +142 -0
- data/lib/rdf/vocab/wdrs.rb +129 -0
- data/lib/rdf/vocab/wot.rb +52 -18
- data/lib/rdf/vocab/xhtml.rb +3 -6
- data/lib/rdf/vocab/xhv.rb +239 -0
- data/lib/rdf/writer.rb +3 -3
- metadata +81 -14
data/lib/rdf/format.rb
CHANGED
@@ -142,8 +142,8 @@ module RDF
|
|
142
142
|
|
143
143
|
# If we have a sample, use that for format detection
|
144
144
|
if sample = (options[:sample] if options.is_a?(Hash)) || (yield if block_given?)
|
145
|
-
sample = sample.to_s
|
146
|
-
sample.force_encoding(Encoding::
|
145
|
+
sample = sample.dup.to_s
|
146
|
+
sample.force_encoding(Encoding::ASCII_8BIT) if sample.respond_to?(:force_encoding)
|
147
147
|
# Given a sample, perform format detection across the appropriate formats, choosing
|
148
148
|
# the first that matches
|
149
149
|
format ||= @@subclasses
|
data/lib/rdf/mixin/enumerable.rb
CHANGED
@@ -67,20 +67,23 @@ module RDF
|
|
67
67
|
# Supported features include:
|
68
68
|
# * `:context` supports statements with a context, allowing multiple contexts
|
69
69
|
# * `:inferrence` supports RDFS inferrence of queryable contents.
|
70
|
+
# * `:validatable` allows a concrete Enumerable implementation to indicate that it does or does not support valididty checking. By default implementations are assumed to support validity checking.
|
70
71
|
#
|
71
72
|
# @param [Symbol, #to_sym] feature
|
72
73
|
# @return [Boolean]
|
73
74
|
# @since 0.3.5
|
74
75
|
def supports?(feature)
|
75
|
-
|
76
|
+
feature == :validity
|
76
77
|
end
|
77
78
|
|
78
79
|
##
|
79
80
|
# Returns `true` if all statements are valid
|
80
81
|
#
|
81
82
|
# @return [Boolean] `true` or `false`
|
83
|
+
# @raise [NotImplementedError] unless enumerable supports validation
|
82
84
|
# @since 0.3.11
|
83
85
|
def valid?
|
86
|
+
raise NotImplementedError, "#{self.class} does not support validation" unless supports?(:validity)
|
84
87
|
each_statement do |s|
|
85
88
|
return false if s.invalid?
|
86
89
|
end
|
@@ -91,6 +94,7 @@ module RDF
|
|
91
94
|
# Returns `true` if value is not valid
|
92
95
|
#
|
93
96
|
# @return [Boolean] `true` or `false`
|
97
|
+
# @raise [NotImplementedError] unless enumerable supports validation
|
94
98
|
# @since 0.2.1
|
95
99
|
def invalid?
|
96
100
|
!valid?
|
@@ -102,7 +106,7 @@ module RDF
|
|
102
106
|
# @raise [ArgumentError] if the value is invalid
|
103
107
|
# @since 0.3.9
|
104
108
|
def validate!
|
105
|
-
raise ArgumentError if invalid?
|
109
|
+
raise ArgumentError if supports?(:validity) && invalid?
|
106
110
|
end
|
107
111
|
alias_method :validate, :validate!
|
108
112
|
|
@@ -226,7 +230,9 @@ module RDF
|
|
226
230
|
# @return [Enumerator]
|
227
231
|
# @see #each_triple
|
228
232
|
def enum_triple
|
229
|
-
|
233
|
+
Countable::Enumerator.new do |yielder|
|
234
|
+
each_triple {|s, p, o| yielder << [s, p, o]}
|
235
|
+
end
|
230
236
|
end
|
231
237
|
alias_method :enum_triples, :enum_triple
|
232
238
|
|
@@ -287,7 +293,9 @@ module RDF
|
|
287
293
|
# @return [Enumerator]
|
288
294
|
# @see #each_quad
|
289
295
|
def enum_quad
|
290
|
-
|
296
|
+
Countable::Enumerator.new do |yielder|
|
297
|
+
each_quad {|s, p, o, c| yielder << [s, p, o, c]}
|
298
|
+
end
|
291
299
|
end
|
292
300
|
alias_method :enum_quads, :enum_quad
|
293
301
|
|
data/lib/rdf/mixin/queryable.rb
CHANGED
@@ -37,7 +37,8 @@ module RDF
|
|
37
37
|
# @yieldparam [RDF::Statement, RDF::Query::Solution] statement
|
38
38
|
# Statement or Solution
|
39
39
|
# @yieldreturn [void] ignored
|
40
|
-
# @return [Enumerator]
|
40
|
+
# @return [Enumerator, Query::Solutions]
|
41
|
+
# Returns an enumerator over statements or query solutions, if passed an {RDF::Query}
|
41
42
|
# @see RDF::Queryable#query_pattern
|
42
43
|
def query(pattern, options = {}, &block)
|
43
44
|
raise TypeError, "#{self} is not readable" if respond_to?(:readable?) && !readable?
|
@@ -45,17 +46,16 @@ module RDF
|
|
45
46
|
case pattern
|
46
47
|
# A basic graph pattern (BGP) query:
|
47
48
|
when Query
|
48
|
-
if
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
query_execute(pattern, options, &block)
|
54
|
-
end
|
55
|
-
after_query(pattern) if respond_to?(:after_query)
|
49
|
+
before_query(pattern) if respond_to?(:before_query)
|
50
|
+
solutions = if method(:query_execute).arity == 1
|
51
|
+
query_execute(pattern, &block)
|
52
|
+
else
|
53
|
+
query_execute(pattern, options, &block)
|
56
54
|
end
|
57
|
-
|
58
|
-
|
55
|
+
after_query(pattern) if respond_to?(:after_query)
|
56
|
+
# Returns the solutions, not an enumerator
|
57
|
+
solutions
|
58
|
+
|
59
59
|
# A simple triple/quad pattern query:
|
60
60
|
else
|
61
61
|
pattern = Query::Pattern.from(pattern)
|
@@ -116,7 +116,7 @@ module RDF
|
|
116
116
|
# query execution by breaking down the query into its constituent
|
117
117
|
# triple patterns and invoking `RDF::Query::Pattern#execute` on each
|
118
118
|
# pattern.
|
119
|
-
query.execute(self, options
|
119
|
+
query.execute(self, options, &block)
|
120
120
|
end
|
121
121
|
protected :query_execute
|
122
122
|
|
@@ -296,7 +296,7 @@ module RDF
|
|
296
296
|
# Ensure that enumerators are, themselves, queryable
|
297
297
|
this = self
|
298
298
|
Queryable::Enumerator.new do |yielder|
|
299
|
-
this.send(method, *args) {
|
299
|
+
this.send(method, *args) {|*y| yielder << (y.length > 1 ? y : y.first)}
|
300
300
|
end
|
301
301
|
end
|
302
302
|
alias_method :to_enum, :enum_for
|
data/lib/rdf/model/graph.rb
CHANGED
@@ -100,10 +100,11 @@ module RDF
|
|
100
100
|
# @option options [RDF::Queryable] :data (RDF::Repository.new)
|
101
101
|
# Storage behind this graph.
|
102
102
|
# @raise [ArgumentError] if a `data` does not support contexts.
|
103
|
-
# @note
|
104
|
-
#
|
105
|
-
#
|
106
|
-
#
|
103
|
+
# @note
|
104
|
+
# Contexts are only useful when used as a projection
|
105
|
+
# on a `:data` which supports contexts. Otherwise, there is no
|
106
|
+
# such thing as a named graph in RDF 1.1, a repository may have
|
107
|
+
# graphs which are named, but the name is not a property of the graph.
|
107
108
|
# @overload initialize(options)
|
108
109
|
# @param [Hash{Symbol => Object}] options
|
109
110
|
# @option options [RDF::Queryable] :data (RDF::Repository.new)
|
data/lib/rdf/model/list.rb
CHANGED
@@ -42,8 +42,8 @@ module RDF
|
|
42
42
|
@subject = subject || RDF.nil
|
43
43
|
@graph = graph || RDF::Graph.new
|
44
44
|
|
45
|
-
unless values.
|
46
|
-
values.reverse_each {|value| self.unshift(value)}
|
45
|
+
unless Array(values).empty?
|
46
|
+
Array(values).reverse_each {|value| self.unshift(value)}
|
47
47
|
end
|
48
48
|
|
49
49
|
if block_given?
|
@@ -761,6 +761,18 @@ module RDF
|
|
761
761
|
each.to_set
|
762
762
|
end
|
763
763
|
|
764
|
+
##
|
765
|
+
# Returns the subject of the list.
|
766
|
+
#
|
767
|
+
# @example
|
768
|
+
# RDF::List[].to_term #=> "RDF[:nil]"
|
769
|
+
# RDF::List[1, 2, 3].to_term #=> "RDF::Node"
|
770
|
+
#
|
771
|
+
# @return [RDF::Resource]
|
772
|
+
def to_term
|
773
|
+
subject
|
774
|
+
end
|
775
|
+
|
764
776
|
##
|
765
777
|
# Returns a string representation of this list.
|
766
778
|
#
|
@@ -784,8 +796,7 @@ module RDF
|
|
784
796
|
if self.equal?(NIL)
|
785
797
|
'RDF::List::NIL'
|
786
798
|
else
|
787
|
-
|
788
|
-
sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, to_s) # FIXME
|
799
|
+
sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, join(', '))
|
789
800
|
end
|
790
801
|
end
|
791
802
|
end
|
data/lib/rdf/model/literal.rb
CHANGED
@@ -164,7 +164,8 @@ module RDF
|
|
164
164
|
@object = value
|
165
165
|
@string = options[:lexical] if options[:lexical]
|
166
166
|
@string = value if !defined?(@string) && value.is_a?(String)
|
167
|
-
@string.encode
|
167
|
+
@string = @string.encode(Encoding::UTF_8) if @string
|
168
|
+
@object = @string if @string && @object.is_a?(String)
|
168
169
|
@language = options[:language].to_s.to_sym if options[:language]
|
169
170
|
@datatype = RDF::URI(options[:datatype]) if options[:datatype]
|
170
171
|
@datatype ||= self.class.const_get(:DATATYPE) if self.class.const_defined?(:DATATYPE)
|
data/lib/rdf/model/statement.rb
CHANGED
@@ -59,6 +59,7 @@ module RDF
|
|
59
59
|
# @option options [RDF::Term] :object (nil)
|
60
60
|
# @option options [RDF::Resource] :context (nil)
|
61
61
|
# Note, in RDF 1.1, a context MUST be an IRI.
|
62
|
+
# @return [RDF::Statement]
|
62
63
|
#
|
63
64
|
# @overload initialize(subject, predicate, object, options = {})
|
64
65
|
# @param [RDF::Resource] subject
|
@@ -66,6 +67,7 @@ module RDF
|
|
66
67
|
# @param [RDF::Term] object
|
67
68
|
# @param [Hash{Symbol => Object}] options
|
68
69
|
# @option options [RDF::Resource] :context (nil)
|
70
|
+
# @return [RDF::Statement]
|
69
71
|
def initialize(subject = nil, predicate = nil, object = nil, options = {})
|
70
72
|
case subject
|
71
73
|
when Hash
|
@@ -88,12 +90,19 @@ module RDF
|
|
88
90
|
# @private
|
89
91
|
def initialize!
|
90
92
|
@context = Node.intern(@context) if @context.is_a?(Symbol)
|
91
|
-
@subject =
|
93
|
+
@subject = case @subject
|
94
|
+
when nil then nil
|
95
|
+
when Symbol then Node.intern(@subject)
|
96
|
+
when Term then @subject
|
97
|
+
when Value then @subject.to_term
|
98
|
+
else raise_error(ArgumentError, "expected subject to be nil or a term, was #{@subject.inspect}")
|
99
|
+
end
|
92
100
|
@predicate = Node.intern(@predicate) if @predicate.is_a?(Symbol)
|
93
101
|
@object = case @object
|
94
102
|
when nil then nil
|
95
103
|
when Symbol then Node.intern(@object)
|
96
104
|
when Term then @object
|
105
|
+
when Value then @object.to_term
|
97
106
|
else Literal.new(@object)
|
98
107
|
end
|
99
108
|
end
|
data/lib/rdf/model/term.rb
CHANGED
data/lib/rdf/model/uri.rb
CHANGED
@@ -1146,15 +1146,120 @@ module RDF
|
|
1146
1146
|
normalized_user + (password ? ":#{normalized_password}" : "") if userinfo
|
1147
1147
|
end
|
1148
1148
|
|
1149
|
+
##
|
1150
|
+
# Converts the query component to a Hash value.
|
1151
|
+
#
|
1152
|
+
# @example
|
1153
|
+
# RDF::URI.new("?one=1&two=2&three=3").query_values
|
1154
|
+
# #=> {"one" => "1", "two" => "2", "three" => "3"}
|
1155
|
+
# RDF::URI.new("?one=two&one=three").query_values(Array)
|
1156
|
+
# #=> [["one", "two"], ["one", "three"]]
|
1157
|
+
# RDF::URI.new("?one=two&one=three").query_values(Hash)
|
1158
|
+
# #=> {"one" => ["two", "three"]}
|
1159
|
+
#
|
1160
|
+
# @param [Class] return_type (Hash)
|
1161
|
+
# The return type desired. Value must be either # `Hash` or `Array`.
|
1162
|
+
# @return [Hash, Array] The query string parsed as a Hash or Array object.
|
1163
|
+
def query_values(return_type=Hash)
|
1164
|
+
raise ArgumentError, "Invalid return type. Must be Hash or Array." unless [Hash, Array].include?(return_type)
|
1165
|
+
return nil if query.nil?
|
1166
|
+
query.to_s.split('&').
|
1167
|
+
inject(return_type == Hash ? {} : []) do |memo,kv|
|
1168
|
+
k,v = kv.to_s.split('=', 2)
|
1169
|
+
next if k.to_s.empty?
|
1170
|
+
k = ::URI.decode(k)
|
1171
|
+
v = ::URI.decode(v) if v
|
1172
|
+
if return_type == Hash
|
1173
|
+
case memo[k]
|
1174
|
+
when nil then memo[k] = v
|
1175
|
+
when Array then memo[k] << v
|
1176
|
+
else memo[k] = [memo[k], v]
|
1177
|
+
end
|
1178
|
+
else
|
1179
|
+
memo << [k, v].compact
|
1180
|
+
end
|
1181
|
+
memo
|
1182
|
+
end
|
1183
|
+
end
|
1184
|
+
|
1185
|
+
##
|
1186
|
+
# Sets the query component for this URI from a Hash object.
|
1187
|
+
# An empty Hash or Array will result in an empty query string.
|
1188
|
+
#
|
1189
|
+
# @example
|
1190
|
+
# uri.query_values = {:a => "a", :b => ["c", "d", "e"]}
|
1191
|
+
# uri.query
|
1192
|
+
# # => "a=a&b=c&b=d&b=e"
|
1193
|
+
# uri.query_values = [['a', 'a'], ['b', 'c'], ['b', 'd'], ['b', 'e']]
|
1194
|
+
# uri.query
|
1195
|
+
# # => "a=a&b=c&b=d&b=e"
|
1196
|
+
# uri.query_values = [['a', 'a'], ['b', ['c', 'd', 'e']]]
|
1197
|
+
# uri.query
|
1198
|
+
# # => "a=a&b=c&b=d&b=e"
|
1199
|
+
# uri.query_values = [['flag'], ['key', 'value']]
|
1200
|
+
# uri.query
|
1201
|
+
# # => "flag&key=value"
|
1202
|
+
#
|
1203
|
+
# @param [Hash, #to_hash, Array] value The new query values.
|
1204
|
+
def query_values=(value)
|
1205
|
+
if value.nil?
|
1206
|
+
self.query = nil
|
1207
|
+
return
|
1208
|
+
end
|
1209
|
+
|
1210
|
+
value = value.to_hash if value.respond_to?(:to_hash)
|
1211
|
+
self.query = case value
|
1212
|
+
when Array
|
1213
|
+
value.map do |(k,v)|
|
1214
|
+
k = normalize_segment(k.to_s, UNRESERVED)
|
1215
|
+
if v.nil?
|
1216
|
+
k
|
1217
|
+
else
|
1218
|
+
"#{k}=#{normalize_segment(v.to_s, UNRESERVED)}"
|
1219
|
+
end
|
1220
|
+
end
|
1221
|
+
when Hash
|
1222
|
+
value.map do |k, v|
|
1223
|
+
k = normalize_segment(k.to_s, UNRESERVED)
|
1224
|
+
if v.nil?
|
1225
|
+
k
|
1226
|
+
else
|
1227
|
+
Array(v).map do |vv|
|
1228
|
+
if vv === TrueClass
|
1229
|
+
k
|
1230
|
+
else
|
1231
|
+
"#{k}=#{normalize_segment(vv.to_s, UNRESERVED)}"
|
1232
|
+
end
|
1233
|
+
end.join("&")
|
1234
|
+
end
|
1235
|
+
end
|
1236
|
+
else
|
1237
|
+
raise TypeError,
|
1238
|
+
"Can't convert #{value.class} into Hash."
|
1239
|
+
end.join("&")
|
1240
|
+
end
|
1241
|
+
|
1242
|
+
##
|
1243
|
+
# The HTTP request URI for this URI. This is the path and the
|
1244
|
+
# query string.
|
1245
|
+
#
|
1246
|
+
# @return [String] The request URI required for an HTTP request.
|
1247
|
+
def request_uri
|
1248
|
+
return nil if absolute? && scheme !~ /^https?$/
|
1249
|
+
res = path.to_s.empty? ? "/" : path
|
1250
|
+
res += "?#{self.query}" if self.query
|
1251
|
+
return res
|
1252
|
+
end
|
1253
|
+
|
1149
1254
|
private
|
1150
1255
|
|
1151
1256
|
##
|
1152
1257
|
# Normalize a segment using a character range
|
1153
1258
|
#
|
1154
|
-
# @param [String]
|
1259
|
+
# @param [String] value
|
1155
1260
|
# @param [Regexp] expr
|
1156
1261
|
# @param [Boolean] downcase
|
1157
|
-
# @
|
1262
|
+
# @return [String]
|
1158
1263
|
def normalize_segment(value, expr, downcase = false)
|
1159
1264
|
if value
|
1160
1265
|
value = value.dup if value.frozen?
|
data/lib/rdf/model/value.rb
CHANGED
@@ -185,6 +185,14 @@ module RDF
|
|
185
185
|
self
|
186
186
|
end
|
187
187
|
|
188
|
+
##
|
189
|
+
# Returns an `RDF::Term` representation of `self`.
|
190
|
+
#
|
191
|
+
# @return [RDF::Value]
|
192
|
+
def to_term
|
193
|
+
raise NotImplementedError, "#{self.class}#read_triple" # override in subclasses
|
194
|
+
end
|
195
|
+
|
188
196
|
##
|
189
197
|
# Returns a developer-friendly representation of `self`.
|
190
198
|
#
|
data/lib/rdf/ntriples/reader.rb
CHANGED
@@ -62,7 +62,7 @@ module RDF::NTriples
|
|
62
62
|
# 166s
|
63
63
|
PN_CHARS = /-|[0-9]|#{PN_CHARS_U}|#{U_CHARS2}/.freeze
|
64
64
|
# 159s
|
65
|
-
ECHAR = /\\[tbnrf\\"
|
65
|
+
ECHAR = /\\[tbnrf\\"]/.freeze
|
66
66
|
# 18
|
67
67
|
IRIREF = /<((?:#{IRI_RANGE}|#{ESCAPE_CHAR})*)>/.freeze
|
68
68
|
# 141s
|
@@ -84,7 +84,7 @@ module RDF::NTriples
|
|
84
84
|
SUBJECT = Regexp.union(URIREF, NODEID).freeze
|
85
85
|
PREDICATE = Regexp.union(URIREF).freeze
|
86
86
|
OBJECT = Regexp.union(URIREF, NODEID, LITERAL).freeze
|
87
|
-
END_OF_STATEMENT = /^\s*\.\s
|
87
|
+
END_OF_STATEMENT = /^\s*\.\s*(?:#.*)?$/.freeze
|
88
88
|
|
89
89
|
##
|
90
90
|
# Reconstructs an RDF value from its serialized N-Triples
|
@@ -159,7 +159,7 @@ module RDF::NTriples
|
|
159
159
|
# @see http://blog.grayproductions.net/articles/understanding_m17n
|
160
160
|
# @see http://yehudakatz.com/2010/05/17/encodings-unabridged/
|
161
161
|
def self.unescape(string)
|
162
|
-
string = string.
|
162
|
+
string = string.dup.force_encoding(Encoding::ASCII_8BIT)
|
163
163
|
|
164
164
|
# Decode \t|\n|\r|\"|\\ character escapes:
|
165
165
|
ESCAPE_CHARS.each { |escape| string.gsub!(escape.inspect[1...-1], escape) }
|
@@ -169,7 +169,7 @@ module RDF::NTriples
|
|
169
169
|
(string.sub!(ESCAPE_SURROGATE) do
|
170
170
|
if ESCAPE_SURROGATE1.include?($1.hex) && ESCAPE_SURROGATE2.include?($2.hex)
|
171
171
|
s = [$1, $2].pack('H*H*')
|
172
|
-
s
|
172
|
+
s.force_encoding(Encoding::UTF_16BE).encode!(Encoding::UTF_8)
|
173
173
|
else
|
174
174
|
s = [$1.hex].pack('U*') << '\u' << $2
|
175
175
|
end
|
@@ -210,6 +210,7 @@ module RDF::NTriples
|
|
210
210
|
subject = read_uriref || read_node || fail_subject
|
211
211
|
predicate = read_uriref(:intern => true) || fail_predicate
|
212
212
|
object = read_uriref || read_node || read_literal || fail_object
|
213
|
+
|
213
214
|
if validate? && !read_eos
|
214
215
|
raise RDF::ReaderError, "expected end of statement in line #{lineno}: #{current_line.inspect}"
|
215
216
|
end
|
data/lib/rdf/query.rb
CHANGED
@@ -93,6 +93,27 @@ module RDF
|
|
93
93
|
self.new(patterns, options, &block).execute(queryable, options)
|
94
94
|
end
|
95
95
|
|
96
|
+
##
|
97
|
+
# Cast values as Solutions
|
98
|
+
# @overload Solutions()
|
99
|
+
# @return [Solutions] returns Solutions.new()
|
100
|
+
#
|
101
|
+
# @overload Solutions(solutions)
|
102
|
+
# @return [Solutions] returns the argument
|
103
|
+
#
|
104
|
+
# @overload Solutions(array)
|
105
|
+
# @param [Array] array
|
106
|
+
# @return [Solutions] returns the array extended with solutions
|
107
|
+
#
|
108
|
+
# @overload Solutions(*args)
|
109
|
+
# @param [Array<Solution>] args
|
110
|
+
# @return [Solutions] returns new solutions including the arguments, which must each be a {Solution}
|
111
|
+
def self.Solutions(*args)
|
112
|
+
return args.first if args.length == 1 && args.first.is_a?(Solutions)
|
113
|
+
args = args.first if args.first.is_a?(Array) && args.length == 1
|
114
|
+
return Solutions.new(args)
|
115
|
+
end
|
116
|
+
|
96
117
|
##
|
97
118
|
# The variables used in this query.
|
98
119
|
#
|
@@ -160,7 +181,7 @@ module RDF
|
|
160
181
|
@options = patterns.last.is_a?(Hash) ? patterns.pop.dup : {}
|
161
182
|
patterns << @options if patterns.empty?
|
162
183
|
@variables = {}
|
163
|
-
@solutions = @options.delete(:solutions)
|
184
|
+
@solutions = Query::Solutions(@options.delete(:solutions))
|
164
185
|
context = @options.fetch(:context, @options.fetch(:name, nil))
|
165
186
|
@options.delete(:context)
|
166
187
|
@options.delete(:name)
|
@@ -257,13 +278,17 @@ module RDF
|
|
257
278
|
# overrides default context defined on query.
|
258
279
|
# @option options [RDF::Resource, RDF::Query::Variable, false] name (nil)
|
259
280
|
# Alias for `:context`.
|
260
|
-
# @option options [
|
281
|
+
# @option options [RDF::Query::Solutions] solutions
|
261
282
|
# optional initial solutions for chained queries
|
283
|
+
# @yield [solution]
|
284
|
+
# each matching solution
|
285
|
+
# @yieldparam [RDF::Query::Solution] solution
|
286
|
+
# @yieldreturn [void] ignored
|
262
287
|
# @return [RDF::Query::Solutions]
|
263
288
|
# the resulting solution sequence
|
264
289
|
# @see http://www.holygoat.co.uk/blog/entry/2005-10-25-1
|
265
290
|
# @see http://www.w3.org/TR/sparql11-query/#emptyGroupPattern
|
266
|
-
def execute(queryable, options = {})
|
291
|
+
def execute(queryable, options = {}, &block)
|
267
292
|
validate!
|
268
293
|
options = options.dup
|
269
294
|
|
@@ -273,10 +298,13 @@ module RDF
|
|
273
298
|
# Use provided solutions to allow for query chaining
|
274
299
|
# Otherwise, a quick empty solution simplifies the logic below; no special case for
|
275
300
|
# the first pattern
|
276
|
-
@solutions = options[:solutions] ||
|
301
|
+
@solutions = Query::Solutions(options[:solutions] || Solution.new)
|
277
302
|
|
278
303
|
# If there are no patterns, just return the empty solution
|
279
|
-
|
304
|
+
if empty?
|
305
|
+
@solutions.each(&block) if block_given?
|
306
|
+
return @solutions
|
307
|
+
end
|
280
308
|
|
281
309
|
patterns = @patterns
|
282
310
|
context = options.fetch(:context, options.fetch(:name, self.context))
|
@@ -289,14 +317,14 @@ module RDF
|
|
289
317
|
patterns.first.context = context
|
290
318
|
end
|
291
319
|
end
|
292
|
-
|
320
|
+
|
293
321
|
patterns.each do |pattern|
|
294
322
|
|
295
|
-
old_solutions, @solutions = @solutions, Solutions
|
323
|
+
old_solutions, @solutions = @solutions, Query::Solutions()
|
296
324
|
|
297
325
|
options[:bindings].keys.each do |variable|
|
298
326
|
if pattern.variables.include?(variable)
|
299
|
-
unbound_solutions, old_solutions = old_solutions, Solutions
|
327
|
+
unbound_solutions, old_solutions = old_solutions, Query::Solutions()
|
300
328
|
options[:bindings][variable].each do |binding|
|
301
329
|
unbound_solutions.each do |solution|
|
302
330
|
old_solutions << solution.merge(variable => binding)
|
@@ -325,11 +353,18 @@ module RDF
|
|
325
353
|
# that can have constraints are often broad without them.
|
326
354
|
# We have no solutions at all:
|
327
355
|
return @solutions if @solutions.empty?
|
328
|
-
|
329
|
-
if !pattern.optional?
|
330
|
-
|
356
|
+
|
357
|
+
if !pattern.optional?
|
358
|
+
# We have no solutions for variables we should have solutions for:
|
359
|
+
need_vars = pattern.variables.keys
|
360
|
+
@solutions.each do |solution|
|
361
|
+
break if need_vars.empty?
|
362
|
+
need_vars -= solution.bindings.keys
|
363
|
+
end
|
364
|
+
return Query::Solutions() unless need_vars.empty?
|
331
365
|
end
|
332
366
|
end
|
367
|
+
@solutions.each(&block) if block_given?
|
333
368
|
@solutions
|
334
369
|
end
|
335
370
|
|
@@ -354,7 +389,7 @@ module RDF
|
|
354
389
|
# @return [Boolean]
|
355
390
|
# @see #failed?
|
356
391
|
def matched?
|
357
|
-
|
392
|
+
!failed?
|
358
393
|
end
|
359
394
|
|
360
395
|
# Add patterns from another query to form a new Query
|