rdf 3.0.9 → 3.0.10
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/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.
|