rdf 1.1.0p4 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|