rdf 3.0.9 → 3.0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/lib/rdf/model/literal/datetime.rb +14 -4
- data/lib/rdf/model/literal/time.rb +5 -5
- data/lib/rdf/model/statement.rb +3 -0
- data/lib/rdf/model/uri.rb +41 -24
- data/lib/rdf/ntriples/reader.rb +28 -25
- data/lib/rdf/ntriples/writer.rb +5 -7
- data/lib/rdf/query.rb +10 -6
- data/lib/rdf/query/pattern.rb +7 -0
- data/lib/rdf/query/solution.rb +62 -5
- data/lib/rdf/query/solutions.rb +23 -1
- data/lib/rdf/query/variable.rb +38 -4
- data/lib/rdf/repository.rb +11 -10
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3d464dd558551830f9c3948ffc879108e5619ef12384a0df1b0d25c0859a95ab
|
4
|
+
data.tar.gz: a3df4023d8417ffa7f94a95b9c2c2f5cb87eecbdcfb8ab89ae8c9b49d63f3060
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 76afd5faf910dcc364f6367590654d69eabbcc409ee6e3b9892b1871c4c760e70f31aea6958394b80b16b04117868acd0adecf602e64b3030a4bf5fa81eb229f
|
7
|
+
data.tar.gz: 277b395451e0109f442a86ae41da2cebac078d41799beec31194d38e0cea4163092bb756ef8f435ead80d6078c48c27ec2a00b9c569be4044e36e9cd3e4b870b
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.0.
|
1
|
+
3.0.10
|
@@ -7,7 +7,7 @@ module RDF; class Literal
|
|
7
7
|
class DateTime < Literal
|
8
8
|
DATATYPE = RDF::URI("http://www.w3.org/2001/XMLSchema#dateTime")
|
9
9
|
GRAMMAR = %r(\A(-?(?:\d{4}|[1-9]\d{4,})-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?)((?:[\+\-]\d{2}:\d{2})|UTC|GMT|Z)?\Z).freeze
|
10
|
-
FORMAT = '%Y-%m-%dT%H:%M:%S%:z'.freeze
|
10
|
+
FORMAT = '%Y-%m-%dT%H:%M:%S.%L%:z'.freeze
|
11
11
|
|
12
12
|
##
|
13
13
|
# @param [DateTime] value
|
@@ -31,9 +31,9 @@ module RDF; class Literal
|
|
31
31
|
def canonicalize!
|
32
32
|
if self.valid?
|
33
33
|
@string = if has_timezone?
|
34
|
-
@object.new_offset.new_offset.strftime(FORMAT[0..-4] + 'Z')
|
34
|
+
@object.new_offset.new_offset.strftime(FORMAT[0..-4] + 'Z').sub('.000', '')
|
35
35
|
else
|
36
|
-
@object.strftime(FORMAT[0..-4])
|
36
|
+
@object.strftime(FORMAT[0..-4]).sub('.000', '')
|
37
37
|
end
|
38
38
|
end
|
39
39
|
self
|
@@ -80,6 +80,16 @@ module RDF; class Literal
|
|
80
80
|
super && object && value !~ %r(\A0000)
|
81
81
|
end
|
82
82
|
|
83
|
+
##
|
84
|
+
# Does the literal representation include millisectonds?
|
85
|
+
#
|
86
|
+
# @return [Boolean]
|
87
|
+
# @since 1.1.6
|
88
|
+
def has_milliseconds?
|
89
|
+
self.format("%L").to_i > 0
|
90
|
+
end
|
91
|
+
alias_method :has_ms?, :has_milliseconds?
|
92
|
+
|
83
93
|
##
|
84
94
|
# Does the literal representation include a timezone? Note that this is only possible if initialized using a string, or `:lexical` option.
|
85
95
|
#
|
@@ -98,7 +108,7 @@ module RDF; class Literal
|
|
98
108
|
#
|
99
109
|
# @return [String]
|
100
110
|
def to_s
|
101
|
-
@string || @object.strftime(FORMAT).sub("+00:00", 'Z')
|
111
|
+
@string || @object.strftime(FORMAT).sub("+00:00", 'Z').sub('.000', '')
|
102
112
|
end
|
103
113
|
|
104
114
|
##
|
@@ -12,7 +12,7 @@ module RDF; class Literal
|
|
12
12
|
class Time < Literal
|
13
13
|
DATATYPE = RDF::URI("http://www.w3.org/2001/XMLSchema#time")
|
14
14
|
GRAMMAR = %r(\A(\d{2}:\d{2}:\d{2}(?:\.\d+)?)((?:[\+\-]\d{2}:\d{2})|UTC|GMT|Z)?\Z).freeze
|
15
|
-
FORMAT = '%H:%M:%S%:z'.freeze
|
15
|
+
FORMAT = '%H:%M:%S.%L%:z'.freeze
|
16
16
|
|
17
17
|
##
|
18
18
|
# @param [String, DateTime, #to_datetime] value
|
@@ -43,9 +43,9 @@ module RDF; class Literal
|
|
43
43
|
def canonicalize!
|
44
44
|
if self.valid?
|
45
45
|
@string = if has_timezone?
|
46
|
-
@object.new_offset.new_offset.strftime(FORMAT[0..-4] + 'Z')
|
46
|
+
@object.new_offset.new_offset.strftime(FORMAT[0..-4] + 'Z').sub('.000', '')
|
47
47
|
else
|
48
|
-
@object.strftime(FORMAT[0..-4])
|
48
|
+
@object.strftime(FORMAT[0..-4]).sub('.000', '')
|
49
49
|
end
|
50
50
|
end
|
51
51
|
self
|
@@ -91,7 +91,7 @@ module RDF; class Literal
|
|
91
91
|
#
|
92
92
|
# @return [String]
|
93
93
|
def to_s
|
94
|
-
@string || @object.strftime(FORMAT).sub("+00:00", 'Z')
|
94
|
+
@string || @object.strftime(FORMAT).sub("+00:00", 'Z').sub('.000', '')
|
95
95
|
end
|
96
96
|
|
97
97
|
##
|
@@ -122,7 +122,7 @@ module RDF; class Literal
|
|
122
122
|
return super unless other.valid?
|
123
123
|
# Compare as strings, as time includes a date portion, and adjusting for UTC
|
124
124
|
# can create a mismatch in the date portion.
|
125
|
-
self.object.new_offset.strftime('%H%M%S') == other.object.new_offset.strftime('%H%M%S')
|
125
|
+
self.object.new_offset.strftime('%H%M%S.%L') == other.object.new_offset.strftime('%H%M%S.%L')
|
126
126
|
when Literal::DateTime, Literal::Date
|
127
127
|
false
|
128
128
|
else
|
data/lib/rdf/model/statement.rb
CHANGED
data/lib/rdf/model/uri.rb
CHANGED
@@ -169,8 +169,7 @@ module RDF
|
|
169
169
|
def self.normalize_path(path)
|
170
170
|
output, input = "", path.to_s
|
171
171
|
if input.encoding != Encoding::ASCII_8BIT
|
172
|
-
input = input.dup
|
173
|
-
input = input.force_encoding(Encoding::ASCII_8BIT)
|
172
|
+
input = input.dup.force_encoding(Encoding::ASCII_8BIT)
|
174
173
|
end
|
175
174
|
until input.empty?
|
176
175
|
if input.match(RDS_2A)
|
@@ -228,8 +227,7 @@ module RDF
|
|
228
227
|
if uri
|
229
228
|
@value = uri.to_s
|
230
229
|
if @value.encoding != Encoding::UTF_8
|
231
|
-
@value
|
232
|
-
@value.force_encoding(Encoding::UTF_8)
|
230
|
+
@value.dup.force_encoding(Encoding::UTF_8)
|
233
231
|
@value.freeze
|
234
232
|
end
|
235
233
|
else
|
@@ -842,16 +840,16 @@ module RDF
|
|
842
840
|
user, password = userinfo.to_s.split(':', 2)
|
843
841
|
host, port = hostport.to_s.split(':', 2)
|
844
842
|
|
845
|
-
parts[:scheme] = (scheme.force_encoding(Encoding::UTF_8) if scheme)
|
846
|
-
parts[:authority] = (authority.force_encoding(Encoding::UTF_8) if authority)
|
847
|
-
parts[:userinfo] = (userinfo.force_encoding(Encoding::UTF_8) if userinfo)
|
848
|
-
parts[:user] = (user.force_encoding(Encoding::UTF_8) if user)
|
849
|
-
parts[:password] = (password.force_encoding(Encoding::UTF_8) if password)
|
850
|
-
parts[:host] = (host.force_encoding(Encoding::UTF_8) if host)
|
843
|
+
parts[:scheme] = (scheme.dup.force_encoding(Encoding::UTF_8) if scheme)
|
844
|
+
parts[:authority] = (authority.dup.force_encoding(Encoding::UTF_8) if authority)
|
845
|
+
parts[:userinfo] = (userinfo.dup.force_encoding(Encoding::UTF_8) if userinfo)
|
846
|
+
parts[:user] = (user.dup.force_encoding(Encoding::UTF_8) if user)
|
847
|
+
parts[:password] = (password.dup.force_encoding(Encoding::UTF_8) if password)
|
848
|
+
parts[:host] = (host.dup.force_encoding(Encoding::UTF_8) if host)
|
851
849
|
parts[:port] = (::URI.decode(port).to_i if port)
|
852
|
-
parts[:path] = (path.to_s.force_encoding(Encoding::UTF_8) unless path.empty?)
|
853
|
-
parts[:query] = (query[1..-1].force_encoding(Encoding::UTF_8) if query)
|
854
|
-
parts[:fragment] = (fragment[1..-1].force_encoding(Encoding::UTF_8) if fragment)
|
850
|
+
parts[:path] = (path.to_s.dup.force_encoding(Encoding::UTF_8) unless path.empty?)
|
851
|
+
parts[:query] = (query[1..-1].dup.force_encoding(Encoding::UTF_8) if query)
|
852
|
+
parts[:fragment] = (fragment[1..-1].dup.force_encoding(Encoding::UTF_8) if fragment)
|
855
853
|
end
|
856
854
|
|
857
855
|
parts
|
@@ -869,7 +867,7 @@ module RDF
|
|
869
867
|
# @param [String, #to_s] value
|
870
868
|
# @return [RDF::URI] self
|
871
869
|
def scheme=(value)
|
872
|
-
object[:scheme] = (value.to_s.force_encoding(Encoding::UTF_8) if value)
|
870
|
+
object[:scheme] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value)
|
873
871
|
@value = nil
|
874
872
|
self
|
875
873
|
end
|
@@ -893,7 +891,7 @@ module RDF
|
|
893
891
|
# @param [String, #to_s] value
|
894
892
|
# @return [RDF::URI] self
|
895
893
|
def user=(value)
|
896
|
-
object[:user] = (value.to_s.force_encoding(Encoding::UTF_8) if value)
|
894
|
+
object[:user] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value)
|
897
895
|
@object[:userinfo] = format_userinfo("")
|
898
896
|
@object[:authority] = format_authority
|
899
897
|
@value = nil
|
@@ -919,7 +917,7 @@ module RDF
|
|
919
917
|
# @param [String, #to_s] value
|
920
918
|
# @return [RDF::URI] self
|
921
919
|
def password=(value)
|
922
|
-
object[:password] = (value.to_s.force_encoding(Encoding::UTF_8) if value)
|
920
|
+
object[:password] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value)
|
923
921
|
@object[:userinfo] = format_userinfo("")
|
924
922
|
@object[:authority] = format_authority
|
925
923
|
@value = nil
|
@@ -947,7 +945,7 @@ module RDF
|
|
947
945
|
# @param [String, #to_s] value
|
948
946
|
# @return [RDF::URI] self
|
949
947
|
def host=(value)
|
950
|
-
object[:host] = (value.to_s.force_encoding(Encoding::UTF_8) if value)
|
948
|
+
object[:host] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value)
|
951
949
|
@object[:authority] = format_authority
|
952
950
|
@value = nil
|
953
951
|
self
|
@@ -1010,7 +1008,7 @@ module RDF
|
|
1010
1008
|
if value
|
1011
1009
|
# Always lead with a slash
|
1012
1010
|
value = "/#{value}" if host && value.to_s.match?(/^[^\/]/)
|
1013
|
-
object[:path] = value.to_s.force_encoding(Encoding::UTF_8)
|
1011
|
+
object[:path] = value.to_s.dup.force_encoding(Encoding::UTF_8)
|
1014
1012
|
else
|
1015
1013
|
object[:path] = nil
|
1016
1014
|
end
|
@@ -1069,7 +1067,7 @@ module RDF
|
|
1069
1067
|
# @param [String, #to_s] value
|
1070
1068
|
# @return [RDF::URI] self
|
1071
1069
|
def query=(value)
|
1072
|
-
object[:query] = (value.to_s.force_encoding(Encoding::UTF_8) if value)
|
1070
|
+
object[:query] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value)
|
1073
1071
|
@value = nil
|
1074
1072
|
self
|
1075
1073
|
end
|
@@ -1093,7 +1091,7 @@ module RDF
|
|
1093
1091
|
# @param [String, #to_s] value
|
1094
1092
|
# @return [RDF::URI] self
|
1095
1093
|
def fragment=(value)
|
1096
|
-
object[:fragment] = (value.to_s.force_encoding(Encoding::UTF_8) if value)
|
1094
|
+
object[:fragment] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value)
|
1097
1095
|
@value = nil
|
1098
1096
|
self
|
1099
1097
|
end
|
@@ -1118,7 +1116,7 @@ module RDF
|
|
1118
1116
|
# @return [RDF::URI] self
|
1119
1117
|
def authority=(value)
|
1120
1118
|
object.delete_if {|k, v| [:user, :password, :host, :port, :userinfo].include?(k)}
|
1121
|
-
object[:authority] = (value.to_s.force_encoding(Encoding::UTF_8) if value)
|
1119
|
+
object[:authority] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value)
|
1122
1120
|
user; password; userinfo; host; port
|
1123
1121
|
@value = nil
|
1124
1122
|
self
|
@@ -1148,7 +1146,7 @@ module RDF
|
|
1148
1146
|
# @return [RDF::URI] self
|
1149
1147
|
def userinfo=(value)
|
1150
1148
|
object.delete_if {|k, v| [:user, :password, :authority].include?(k)}
|
1151
|
-
object[:userinfo] = (value.to_s.force_encoding(Encoding::UTF_8) if value)
|
1149
|
+
object[:userinfo] = (value.to_s.dup.force_encoding(Encoding::UTF_8) if value)
|
1152
1150
|
user; password; authority
|
1153
1151
|
@value = nil
|
1154
1152
|
self
|
@@ -1263,6 +1261,26 @@ module RDF
|
|
1263
1261
|
return res
|
1264
1262
|
end
|
1265
1263
|
|
1264
|
+
##
|
1265
|
+
# Dump of data needed to reconsitute this object using Marshal.load
|
1266
|
+
# This override is needed to avoid serializing @mutex.
|
1267
|
+
#
|
1268
|
+
# @param [Integer] level The maximum depth of objects to dump.
|
1269
|
+
# @return [String] The dump of data needed to reconsitute this object.
|
1270
|
+
def _dump(level)
|
1271
|
+
value
|
1272
|
+
end
|
1273
|
+
|
1274
|
+
##
|
1275
|
+
# Load dumped data to reconsitute marshaled object
|
1276
|
+
# This override is needed to avoid serializing @mutex.
|
1277
|
+
#
|
1278
|
+
# @param [String] data The dump of data needed to reconsitute this object.
|
1279
|
+
# @return [RDF::URI] The reconsituted object.
|
1280
|
+
def self._load(data)
|
1281
|
+
new(data)
|
1282
|
+
end
|
1283
|
+
|
1266
1284
|
private
|
1267
1285
|
|
1268
1286
|
##
|
@@ -1274,8 +1292,7 @@ module RDF
|
|
1274
1292
|
# @return [String]
|
1275
1293
|
def normalize_segment(value, expr, downcase = false)
|
1276
1294
|
if value
|
1277
|
-
value = value.dup
|
1278
|
-
value = value.force_encoding(Encoding::UTF_8)
|
1295
|
+
value = value.dup.force_encoding(Encoding::UTF_8)
|
1279
1296
|
decoded = ::URI.decode(value)
|
1280
1297
|
decoded.downcase! if downcase
|
1281
1298
|
::URI.encode(decoded, /[^(?:#{expr})]/)
|
data/lib/rdf/ntriples/reader.rb
CHANGED
@@ -32,7 +32,7 @@ module RDF::NTriples
|
|
32
32
|
format RDF::NTriples::Format
|
33
33
|
|
34
34
|
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_strings
|
35
|
-
ESCAPE_CHARS = ["\b", "\f", "\t", "\n", "\r", "\"", "\\"].freeze
|
35
|
+
ESCAPE_CHARS = ["\b", "\f", "\t", "\n", "\r", "\"", "'", "\\"].freeze
|
36
36
|
UCHAR4 = /\\u([0-9A-Fa-f]{4,4})/.freeze
|
37
37
|
UCHAR8 = /\\U([0-9A-Fa-f]{8,8})/.freeze
|
38
38
|
UCHAR = Regexp.union(UCHAR4, UCHAR8).freeze
|
@@ -60,7 +60,7 @@ module RDF::NTriples
|
|
60
60
|
# 166s
|
61
61
|
PN_CHARS = /-|[0-9]|#{PN_CHARS_U}|#{U_CHARS2}/.freeze
|
62
62
|
# 159s
|
63
|
-
ECHAR = /\\[tbnrf\\
|
63
|
+
ECHAR = /\\[tbnrf"'\\]/.freeze
|
64
64
|
# 18
|
65
65
|
IRIREF = /<((?:#{IRI_RANGE}|#{UCHAR})*)>/.freeze
|
66
66
|
# 141s
|
@@ -135,7 +135,6 @@ module RDF::NTriples
|
|
135
135
|
# @return [RDF::URI]
|
136
136
|
def self.parse_uri(input, intern: false, **options)
|
137
137
|
if input =~ URIREF
|
138
|
-
uri_str = unescape($1)
|
139
138
|
RDF::URI.send(intern ? :intern : :new, unescape($1))
|
140
139
|
end
|
141
140
|
end
|
@@ -155,9 +154,16 @@ module RDF::NTriples
|
|
155
154
|
end
|
156
155
|
|
157
156
|
# cache constants to optimize escaping the escape chars in self.unescape
|
158
|
-
ESCAPE_CHARS_ESCAPED =
|
159
|
-
|
160
|
-
|
157
|
+
ESCAPE_CHARS_ESCAPED = {
|
158
|
+
"\\b" => "\b",
|
159
|
+
"\\f" => "\f",
|
160
|
+
"\\t" => "\t",
|
161
|
+
"\\n" => "\n",
|
162
|
+
"\\r" => "\r",
|
163
|
+
"\\\"" => "\"",
|
164
|
+
"\\'" => "'",
|
165
|
+
"\\\\" => "\\"
|
166
|
+
} .freeze
|
161
167
|
ESCAPE_CHARS_ESCAPED_REGEXP = Regexp.union(
|
162
168
|
ESCAPE_CHARS_ESCAPED.keys
|
163
169
|
).freeze
|
@@ -171,26 +177,23 @@ module RDF::NTriples
|
|
171
177
|
def self.unescape(string)
|
172
178
|
# Note: avoiding copying the input string when no escaping is needed
|
173
179
|
# greatly reduces the number of allocations and the processing time.
|
174
|
-
unless string.encoding == Encoding::UTF_8
|
175
|
-
|
176
|
-
end
|
177
|
-
|
178
|
-
has_escape_chars = ESCAPE_CHARS_ESCAPED_REGEXP.match?(string)
|
179
|
-
has_uchar = UCHAR.match?(string)
|
180
|
-
|
181
|
-
string = string.dup if has_escape_chars || has_uchar
|
180
|
+
string = string.dup.force_encoding(Encoding::UTF_8) unless string.encoding == Encoding::UTF_8
|
181
|
+
scanner = StringScanner.new(string)
|
182
182
|
|
183
|
-
|
184
|
-
string.gsub!(ESCAPE_CHARS_ESCAPED_REGEXP) do
|
185
|
-
ESCAPE_CHARS_ESCAPED.fetch($~[0])
|
186
|
-
end if has_escape_chars
|
183
|
+
buffer = ""
|
187
184
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
185
|
+
while !scanner.eos?
|
186
|
+
buffer << if scanner.scan(ESCAPE_CHARS_ESCAPED_REGEXP)
|
187
|
+
ESCAPE_CHARS_ESCAPED[scanner.matched]
|
188
|
+
elsif scanner.scan(UCHAR)
|
189
|
+
scanner.matched.sub(UCHAR) {[($1 || $2).hex].pack('U*')}
|
190
|
+
else
|
191
|
+
# Scan one character
|
192
|
+
scanner.getch
|
193
|
+
end
|
194
|
+
end
|
192
195
|
|
193
|
-
|
196
|
+
buffer
|
194
197
|
end
|
195
198
|
|
196
199
|
##
|
@@ -250,7 +253,7 @@ module RDF::NTriples
|
|
250
253
|
uri.canonicalize! if canonicalize?
|
251
254
|
uri
|
252
255
|
end
|
253
|
-
rescue ArgumentError
|
256
|
+
rescue ArgumentError
|
254
257
|
log_error("Invalid URI (found: \"<#{uri_str}>\")", lineno: lineno, token: "<#{uri_str}>", exception: RDF::ReaderError)
|
255
258
|
end
|
256
259
|
|
@@ -258,7 +261,7 @@ module RDF::NTriples
|
|
258
261
|
# @return [RDF::Node]
|
259
262
|
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_grammar (nodeID)
|
260
263
|
def read_node
|
261
|
-
|
264
|
+
if node_id = match(NODEID)
|
262
265
|
@nodes ||= {}
|
263
266
|
@nodes[node_id] ||= RDF::Node.new(node_id)
|
264
267
|
end
|
data/lib/rdf/ntriples/writer.rb
CHANGED
@@ -42,7 +42,7 @@ module RDF::NTriples
|
|
42
42
|
format RDF::NTriples::Format
|
43
43
|
|
44
44
|
# @see http://www.w3.org/TR/rdf-testcases/#ntrip_strings
|
45
|
-
ESCAPE_PLAIN = /\A[\x20-\x21\x23
|
45
|
+
ESCAPE_PLAIN = /\A[\x20-\x21\x23-\x26\x28#{Regexp.escape '['}#{Regexp.escape ']'}-\x7E]*\z/m.freeze
|
46
46
|
ESCAPE_PLAIN_U = /\A(?:#{Reader::IRI_RANGE}|#{Reader::UCHAR})*\z/.freeze
|
47
47
|
|
48
48
|
##
|
@@ -116,6 +116,8 @@ module RDF::NTriples
|
|
116
116
|
# sequences, otherwise, assume the test-cases escape sequences. Otherwise,
|
117
117
|
# the N-Triples recommendation includes `\b` and `\f` escape sequences.
|
118
118
|
#
|
119
|
+
# Within STRING_LITERAL_QUOTE, only the characters `U+0022`, `U+005C`, `U+000A`, `U+000D` are encoded using `ECHAR`. `ECHAR` must not be used for characters that are allowed directly in STRING_LITERAL_QUOTE.
|
120
|
+
#
|
119
121
|
# @param [Integer, #ord] u
|
120
122
|
# @return [String]
|
121
123
|
# @raise [ArgumentError] if `u` is not a valid Unicode codepoint
|
@@ -124,11 +126,7 @@ module RDF::NTriples
|
|
124
126
|
def self.escape_ascii(u, encoding)
|
125
127
|
case (u = u.ord)
|
126
128
|
when (0x00..0x07) then escape_utf16(u)
|
127
|
-
when (0x08) then (encoding && encoding == Encoding::ASCII ? escape_utf16(u) : "\\b")
|
128
|
-
when (0x09) then "\\t"
|
129
129
|
when (0x0A) then "\\n"
|
130
|
-
when (0x0B) then escape_utf16(u)
|
131
|
-
when (0x0C) then (encoding && encoding == Encoding::ASCII ? escape_utf16(u) : "\\f")
|
132
130
|
when (0x0D) then "\\r"
|
133
131
|
when (0x0E..0x1F) then escape_utf16(u)
|
134
132
|
when (0x22) then "\\\""
|
@@ -264,7 +262,7 @@ module RDF::NTriples
|
|
264
262
|
string.each_char do |u|
|
265
263
|
buffer << case u.ord
|
266
264
|
when (0x00..0x20) then self.class.escape_utf16(u)
|
267
|
-
when 0x22, 0x3c, 0x3e, 0x5c, 0x5e, 0x60, 0x7b, 0x7c, 0x7d #
|
265
|
+
when 0x22, 0x3c, 0x3e, 0x5c, 0x5e, 0x60, 0x7b, 0x7c, 0x7d # "<>\^`{|}
|
268
266
|
self.class.escape_utf16(u)
|
269
267
|
else u
|
270
268
|
end
|
@@ -278,7 +276,7 @@ module RDF::NTriples
|
|
278
276
|
string.each_byte do |u|
|
279
277
|
buffer << case u
|
280
278
|
when (0x00..0x20) then self.class.escape_utf16(u)
|
281
|
-
when 0x22, 0x3c, 0x3e, 0x5c, 0x5e, 0x60, 0x7b, 0x7c, 0x7d #
|
279
|
+
when 0x22, 0x3c, 0x3e, 0x5c, 0x5e, 0x60, 0x7b, 0x7c, 0x7d # "<>\^`{|}
|
282
280
|
self.class.escape_utf16(u)
|
283
281
|
when (0x80..0xFFFF) then self.class.escape_utf16(u)
|
284
282
|
when (0x10000..0x10FFFF) then self.class.escape_utf32(u)
|
data/lib/rdf/query.rb
CHANGED
@@ -171,10 +171,12 @@ module RDF
|
|
171
171
|
# Alias for `:graph_name`.
|
172
172
|
# @param [Hash{Symbol => Object}] options
|
173
173
|
# any additional keyword options
|
174
|
+
# @option options [Boolean] validate (false)
|
175
|
+
# validate patterns
|
174
176
|
# @yield [query]
|
175
177
|
# @yieldparam [RDF::Query] query
|
176
178
|
# @yieldreturn [void] ignored
|
177
|
-
def initialize(*patterns, solutions: nil, graph_name: nil, name: nil, **options, &block)
|
179
|
+
def initialize(*patterns, solutions: nil, graph_name: nil, name: nil, validate: false, **options, &block)
|
178
180
|
@options = options.dup
|
179
181
|
@solutions = Query::Solutions(solutions)
|
180
182
|
graph_name = name if graph_name.nil?
|
@@ -195,6 +197,8 @@ module RDF
|
|
195
197
|
else instance_eval(&block)
|
196
198
|
end
|
197
199
|
end
|
200
|
+
|
201
|
+
validate! if validate
|
198
202
|
end
|
199
203
|
|
200
204
|
##
|
@@ -291,7 +295,6 @@ module RDF
|
|
291
295
|
# @see http://www.holygoat.co.uk/blog/entry/2005-10-25-1
|
292
296
|
# @see http://www.w3.org/TR/sparql11-query/#emptyGroupPattern
|
293
297
|
def execute(queryable, solutions: Solution.new, graph_name: nil, name: nil, **options, &block)
|
294
|
-
validate!
|
295
298
|
options = {bindings: {}}.merge(options)
|
296
299
|
|
297
300
|
# Use provided solutions to allow for query chaining
|
@@ -356,8 +359,9 @@ module RDF
|
|
356
359
|
return @solutions if @solutions.empty?
|
357
360
|
|
358
361
|
if !pattern.optional?
|
359
|
-
# We have no solutions for variables we should have solutions for
|
360
|
-
|
362
|
+
# We have no solutions for variables we should have solutions for
|
363
|
+
# (excludes non-distinguished variables):
|
364
|
+
need_vars = pattern.variables.select {|k,v| v.distinguished?}.keys
|
361
365
|
@solutions.each do |solution|
|
362
366
|
break if need_vars.empty?
|
363
367
|
need_vars -= solution.bindings.keys
|
@@ -516,12 +520,12 @@ module RDF
|
|
516
520
|
end
|
517
521
|
|
518
522
|
##
|
519
|
-
# Determine if the
|
523
|
+
# Determine if the query containts valid patterns
|
520
524
|
#
|
521
525
|
# @return [Boolean] `true` or `false`
|
522
526
|
# @since 0.3.9
|
523
527
|
def valid?
|
524
|
-
!!validate!
|
528
|
+
!!validate! rescue raise false
|
525
529
|
rescue
|
526
530
|
false
|
527
531
|
end
|
data/lib/rdf/query/pattern.rb
CHANGED
@@ -47,6 +47,13 @@ module RDF; class Query
|
|
47
47
|
@subject = Variable.new(@subject) if @subject.is_a?(Symbol)
|
48
48
|
@predicate = Variable.new(@predicate) if @predicate.is_a?(Symbol)
|
49
49
|
@object = Variable.new(@object) if @object.is_a?(Symbol)
|
50
|
+
|
51
|
+
# Estmate cost positionally, with variables being least expensive as objects, then predicates, then subjects, then graph_names.
|
52
|
+
# XXX does not consider bound variables, which would need to be dynamically calculated.
|
53
|
+
@cost = (@object.nil? || @object.is_a?(Variable) ? 1 : 0) +
|
54
|
+
(@predicate.nil? || @predicate.is_a?(Variable) ? 2 : 0) +
|
55
|
+
(@subject.nil? || @subject.is_a?(Variable) ? 4 : 0) +
|
56
|
+
(@graph_name.is_a?(Variable) ? 8 : 0)
|
50
57
|
super
|
51
58
|
end
|
52
59
|
|
data/lib/rdf/query/solution.rb
CHANGED
@@ -22,6 +22,7 @@ class RDF::Query
|
|
22
22
|
#
|
23
23
|
class Solution
|
24
24
|
# Undefine all superfluous instance methods:
|
25
|
+
alias_method :__send, :send
|
25
26
|
undef_method(*instance_methods.
|
26
27
|
map(&:to_s).
|
27
28
|
select {|m| m.match?(/^\w+$/)}.
|
@@ -57,10 +58,20 @@ class RDF::Query
|
|
57
58
|
# @yieldparam [RDF::Term] value
|
58
59
|
# @return [Enumerator]
|
59
60
|
def each_binding(&block)
|
60
|
-
@bindings.each(&block)
|
61
|
+
@bindings.each(&block) if block_given?
|
62
|
+
enum_binding
|
61
63
|
end
|
62
64
|
alias_method :each, :each_binding
|
63
65
|
|
66
|
+
##
|
67
|
+
# Returns an enumerator for {#each_binding}.
|
68
|
+
#
|
69
|
+
# @return [Enumerator<RDF::Resource>]
|
70
|
+
# @see #each_subject
|
71
|
+
def enum_binding
|
72
|
+
enum_for(:each_binding)
|
73
|
+
end
|
74
|
+
|
64
75
|
##
|
65
76
|
# Enumerates over every variable name in this solution.
|
66
77
|
#
|
@@ -68,10 +79,20 @@ class RDF::Query
|
|
68
79
|
# @yieldparam [Symbol] name
|
69
80
|
# @return [Enumerator]
|
70
81
|
def each_name(&block)
|
71
|
-
@bindings.each_key(&block)
|
82
|
+
@bindings.each_key(&block) if block_given?
|
83
|
+
enum_name
|
72
84
|
end
|
73
85
|
alias_method :each_key, :each_name
|
74
86
|
|
87
|
+
##
|
88
|
+
# Returns an enumerator for {#each_name}.
|
89
|
+
#
|
90
|
+
# @return [Enumerator<RDF::Resource>]
|
91
|
+
# @see #each_subject
|
92
|
+
def enum_name
|
93
|
+
enum_for(:each_name)
|
94
|
+
end
|
95
|
+
|
75
96
|
##
|
76
97
|
# Enumerates over every variable value in this solution.
|
77
98
|
#
|
@@ -79,7 +100,17 @@ class RDF::Query
|
|
79
100
|
# @yieldparam [RDF::Term] value
|
80
101
|
# @return [Enumerator]
|
81
102
|
def each_value(&block)
|
82
|
-
@bindings.each_value(&block)
|
103
|
+
@bindings.each_value(&block) if block_given?
|
104
|
+
enum_value
|
105
|
+
end
|
106
|
+
|
107
|
+
##
|
108
|
+
# Returns an enumerator for {#each_value}.
|
109
|
+
#
|
110
|
+
# @return [Enumerator<RDF::Resource>]
|
111
|
+
# @see #each_subject
|
112
|
+
def enum_value
|
113
|
+
enum_for(:each_value)
|
83
114
|
end
|
84
115
|
|
85
116
|
##
|
@@ -101,9 +132,21 @@ class RDF::Query
|
|
101
132
|
# @yieldparam [Variable]
|
102
133
|
# @return [Enumerator]
|
103
134
|
def each_variable
|
104
|
-
|
105
|
-
|
135
|
+
if block_given?
|
136
|
+
@bindings.each do |name, value|
|
137
|
+
yield Variable.new(name, value)
|
138
|
+
end
|
106
139
|
end
|
140
|
+
enum_variable
|
141
|
+
end
|
142
|
+
|
143
|
+
##
|
144
|
+
# Returns an enumerator for {#each_variable}.
|
145
|
+
#
|
146
|
+
# @return [Enumerator<RDF::Resource>]
|
147
|
+
# @see #each_subject
|
148
|
+
def enum_variable
|
149
|
+
enum_for(:each_variable)
|
107
150
|
end
|
108
151
|
|
109
152
|
##
|
@@ -282,5 +325,19 @@ class RDF::Query
|
|
282
325
|
def respond_to_missing?(name, include_private = false)
|
283
326
|
@bindings.has_key?(name.to_sym) || super
|
284
327
|
end
|
328
|
+
|
329
|
+
##
|
330
|
+
# @private
|
331
|
+
# @param [Symbol, #to_sym] method
|
332
|
+
# @return [Enumerator]
|
333
|
+
# @see Object#enum_for
|
334
|
+
def enum_for(method = :each)
|
335
|
+
# Ensure that enumerators are, themselves, queryable
|
336
|
+
this = self
|
337
|
+
Enumerator.new do |yielder|
|
338
|
+
this.__send(method) {|*y| yielder << (y.length > 1 ? y : y.first)}
|
339
|
+
end
|
340
|
+
end
|
341
|
+
alias_method :to_enum, :enum_for
|
285
342
|
end # Solution
|
286
343
|
end # RDF::Query
|
data/lib/rdf/query/solutions.rb
CHANGED
@@ -104,7 +104,29 @@ module RDF; class Query
|
|
104
104
|
end
|
105
105
|
bindings
|
106
106
|
end
|
107
|
-
|
107
|
+
|
108
|
+
##
|
109
|
+
# Duplicates each solution.
|
110
|
+
# @return [RDF::Query::Solutions]
|
111
|
+
def dup
|
112
|
+
RDF::Query::Solutions.new(self.compact.map(&:dup))
|
113
|
+
end
|
114
|
+
|
115
|
+
##
|
116
|
+
# Merge solutions in `other` into a new solutions instance. Each solution in `other` is merged into those solutions in `self` that are compatible.
|
117
|
+
#
|
118
|
+
# @param [RDF::Query::Solutions] other
|
119
|
+
# @return [RDF::Query::Solutions]
|
120
|
+
def merge(other)
|
121
|
+
other ||= RDF::Query::Solutions()
|
122
|
+
return other if self.empty?
|
123
|
+
return self if other.empty?
|
124
|
+
|
125
|
+
RDF::Query::Solutions(self.map do |s1|
|
126
|
+
other.map { |s2| s2.merge(s1) if s2.compatible?(s1) }
|
127
|
+
end.flatten.compact)
|
128
|
+
end
|
129
|
+
|
108
130
|
##
|
109
131
|
# Filters this solution sequence by the given `criteria`.
|
110
132
|
#
|
data/lib/rdf/query/variable.rb
CHANGED
@@ -65,9 +65,25 @@ class RDF::Query
|
|
65
65
|
# the variable name
|
66
66
|
# @param [RDF::Term] value
|
67
67
|
# an optional variable value
|
68
|
-
|
69
|
-
|
68
|
+
# @param [Boolean] distinguished (true) Also interpreted by leading '??' or '$$' in name.
|
69
|
+
# @param [Boolean] existential (true) Also interpreted by leading '$' in name
|
70
|
+
def initialize(name = nil, value = nil, distinguished: nil, existential: nil)
|
71
|
+
name = (name || "g#{__id__.to_i.abs}").to_s
|
72
|
+
if name.start_with?('??')
|
73
|
+
name, dis, ex = name[2..-1], false, false
|
74
|
+
elsif name.start_with?('?')
|
75
|
+
name, dis, ex = name[1..-1], true, false
|
76
|
+
elsif name.start_with?('$$')
|
77
|
+
name, dis, ex = name[2..-1], false, true
|
78
|
+
elsif name.start_with?('$')
|
79
|
+
name, dis, ex = name[1..-1], true, true
|
80
|
+
else
|
81
|
+
dis, ex = true, false
|
82
|
+
end
|
83
|
+
@name = name.to_sym
|
70
84
|
@value = value
|
85
|
+
@distinguished = distinguished.nil? ? dis : distinguished
|
86
|
+
@existential = existential.nil? ? ex : existential
|
71
87
|
end
|
72
88
|
|
73
89
|
##
|
@@ -109,7 +125,7 @@ class RDF::Query
|
|
109
125
|
#
|
110
126
|
# @return [Boolean]
|
111
127
|
def distinguished?
|
112
|
-
@distinguished
|
128
|
+
@distinguished
|
113
129
|
end
|
114
130
|
|
115
131
|
##
|
@@ -121,6 +137,23 @@ class RDF::Query
|
|
121
137
|
@distinguished = value
|
122
138
|
end
|
123
139
|
|
140
|
+
##
|
141
|
+
# Returns `true` if this variable is existential.
|
142
|
+
#
|
143
|
+
# @return [Boolean]
|
144
|
+
def existential?
|
145
|
+
@existential
|
146
|
+
end
|
147
|
+
|
148
|
+
##
|
149
|
+
# Sets if variable is existential or univeresal.
|
150
|
+
# By default, variables are universal
|
151
|
+
#
|
152
|
+
# @return [Boolean]
|
153
|
+
def existential=(value)
|
154
|
+
@existential = value
|
155
|
+
end
|
156
|
+
|
124
157
|
##
|
125
158
|
# Rebinds this variable to the given `value`.
|
126
159
|
#
|
@@ -208,6 +241,7 @@ class RDF::Query
|
|
208
241
|
#
|
209
242
|
# Non-distinguished variables are indicated with a double `??`
|
210
243
|
#
|
244
|
+
# Existential variables are indicated using a single `$`, or with `$$` if also non-distinguished
|
211
245
|
# @example
|
212
246
|
# v = Variable.new("a")
|
213
247
|
# v.to_s => '?a'
|
@@ -216,7 +250,7 @@ class RDF::Query
|
|
216
250
|
#
|
217
251
|
# @return [String]
|
218
252
|
def to_s
|
219
|
-
prefix = distinguished? ? '?' :
|
253
|
+
prefix = distinguished? ? (existential? ? '$' : '?') : (existential? ? '$$' : '??')
|
220
254
|
unbound? ? "#{prefix}#{name}" : "#{prefix}#{name}=#{value}"
|
221
255
|
end
|
222
256
|
end # Variable
|
data/lib/rdf/repository.rb
CHANGED
@@ -206,8 +206,9 @@ module RDF
|
|
206
206
|
# @private
|
207
207
|
# @see RDF::Enumerable#project_graph
|
208
208
|
def project_graph(graph_name, &block)
|
209
|
-
RDF::Graph.new(graph_name: graph_name, data: self)
|
210
|
-
|
209
|
+
graph = RDF::Graph.new(graph_name: graph_name, data: self)
|
210
|
+
graph.each(&block) if block_given?
|
211
|
+
graph
|
211
212
|
end
|
212
213
|
|
213
214
|
##
|
@@ -324,8 +325,8 @@ module RDF
|
|
324
325
|
@data.each do |g, ss|
|
325
326
|
ss.each do |s, ps|
|
326
327
|
ps.each do |p, os|
|
327
|
-
os.each do |o|
|
328
|
-
yield RDF::Statement.new(s, p, o, graph_name: g.equal?(DEFAULT_GRAPH) ? nil : g)
|
328
|
+
os.each do |o, object_options|
|
329
|
+
yield RDF::Statement.new(s, p, o, object_options.merge(graph_name: g.equal?(DEFAULT_GRAPH) ? nil : g))
|
329
330
|
end
|
330
331
|
end
|
331
332
|
end
|
@@ -402,9 +403,9 @@ module RDF
|
|
402
403
|
[]
|
403
404
|
end
|
404
405
|
ps.each do |p, os|
|
405
|
-
os.each do |o|
|
406
|
+
os.each do |o, object_options|
|
406
407
|
next unless object.nil? || object.eql?(o)
|
407
|
-
yield RDF::Statement.new(s, p, o, graph_name: c.equal?(DEFAULT_GRAPH) ? nil : c)
|
408
|
+
yield RDF::Statement.new(s, p, o, object_options.merge(graph_name: c.equal?(DEFAULT_GRAPH) ? nil : c))
|
408
409
|
end
|
409
410
|
end
|
410
411
|
end
|
@@ -461,7 +462,7 @@ module RDF
|
|
461
462
|
data.has_key?(g) &&
|
462
463
|
data[g].has_key?(s) &&
|
463
464
|
data[g][s].has_key?(p) &&
|
464
|
-
data[g][s][p].
|
465
|
+
data[g][s][p].has_key?(o)
|
465
466
|
end
|
466
467
|
|
467
468
|
##
|
@@ -475,9 +476,9 @@ module RDF
|
|
475
476
|
c ||= DEFAULT_GRAPH
|
476
477
|
|
477
478
|
return data.put(c) do |subs|
|
478
|
-
|
479
|
-
|
480
|
-
(objs || Hamster::
|
479
|
+
(subs || Hamster::Hash.new).put(s) do |preds|
|
480
|
+
(preds || Hamster::Hash.new).put(p) do |objs|
|
481
|
+
(objs || Hamster::Hash.new).put(o, statement.options)
|
481
482
|
end
|
482
483
|
end
|
483
484
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rdf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arto Bendiken
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2019-
|
13
|
+
date: 2019-02-11 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: link_header
|
@@ -297,7 +297,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
297
297
|
- !ruby/object:Gem::Version
|
298
298
|
version: '0'
|
299
299
|
requirements: []
|
300
|
-
rubygems_version: 3.0.
|
300
|
+
rubygems_version: 3.0.2
|
301
301
|
signing_key:
|
302
302
|
specification_version: 4
|
303
303
|
summary: A Ruby library for working with Resource Description Framework (RDF) data.
|