rdf-sesame 0.3.0 → 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 +7 -0
- data/README +2 -1
- data/VERSION +1 -1
- data/lib/rdf/sesame.rb +8 -4
- data/lib/rdf/sesame/connection.rb +78 -68
- data/lib/rdf/sesame/exceptions.rb +5 -0
- data/lib/rdf/sesame/repository.rb +385 -108
- data/lib/rdf/sesame/server.rb +83 -58
- data/lib/rdf/sesame/version.rb +2 -2
- metadata +96 -98
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e40256223226ea6aca7ac314a678ebe951af0782
|
4
|
+
data.tar.gz: 9f1e93a0eb52901a3881c50fd93f9a03c27ad715
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a92457e1ba0067058fa2c23480c66d692adfdba488cabcfb2a5bb4c1025acc8835fe56ee38e56f8b0d47d743656d7bde37f58a398fdfe64a091326c9a7bd1eef
|
7
|
+
data.tar.gz: 3fcf0943353f42ef7981ca80416e93c7014a8412123cd9eb2bf8de2028f138055f9aed76488be131e92e8e95d8b40a7bc8ae47ccb4d3807668b1733b0afb6df0
|
data/README
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
1.1.0
|
data/lib/rdf/sesame.rb
CHANGED
@@ -1,4 +1,12 @@
|
|
1
1
|
require 'rdf'
|
2
|
+
require 'rdf/sesame/exceptions'
|
3
|
+
require 'rdf/sesame/connection'
|
4
|
+
require 'rdf/sesame/repository'
|
5
|
+
require 'rdf/sesame/server'
|
6
|
+
require 'rdf/sesame/version'
|
7
|
+
|
8
|
+
require 'pathname'
|
9
|
+
require 'addressable/uri'
|
2
10
|
|
3
11
|
module RDF
|
4
12
|
##
|
@@ -13,9 +21,5 @@ module RDF
|
|
13
21
|
#
|
14
22
|
# @author [Arto Bendiken](http://ar.to/)
|
15
23
|
module Sesame
|
16
|
-
autoload :Connection, 'rdf/sesame/connection'
|
17
|
-
autoload :Repository, 'rdf/sesame/repository'
|
18
|
-
autoload :Server, 'rdf/sesame/server'
|
19
|
-
autoload :VERSION, 'rdf/sesame/version'
|
20
24
|
end
|
21
25
|
end
|
@@ -21,13 +21,13 @@ module RDF::Sesame
|
|
21
21
|
# call {#close} explicitly.
|
22
22
|
#
|
23
23
|
# @example Opening a connection to a Sesame server (1)
|
24
|
-
# url =
|
24
|
+
# url = URI.parse("http://localhost:8080/openrdf-sesame")
|
25
25
|
# conn = RDF::Sesame::Connection.open(url)
|
26
26
|
# ...
|
27
27
|
# conn.close
|
28
28
|
#
|
29
29
|
# @example Opening a connection to a Sesame server (2)
|
30
|
-
# url =
|
30
|
+
# url = URI.parse("http://localhost:8080/openrdf-sesame")
|
31
31
|
# RDF::Sesame::Connection.open(url) do |conn|
|
32
32
|
# ...
|
33
33
|
# end
|
@@ -41,8 +41,17 @@ module RDF::Sesame
|
|
41
41
|
#
|
42
42
|
# @see http://ruby-doc.org/core/classes/Net/HTTP.html
|
43
43
|
class Connection
|
44
|
-
# @return [
|
45
|
-
attr_reader :
|
44
|
+
# @return [String]
|
45
|
+
attr_reader :user
|
46
|
+
|
47
|
+
# @return [String]
|
48
|
+
attr_reader :pass
|
49
|
+
|
50
|
+
# @return [String]
|
51
|
+
attr_reader :proxy_host
|
52
|
+
|
53
|
+
# @return [Number]
|
54
|
+
attr_reader :proxy_port
|
46
55
|
|
47
56
|
# @return [Hash{Symbol => Object}]
|
48
57
|
attr_reader :options
|
@@ -67,7 +76,7 @@ module RDF::Sesame
|
|
67
76
|
self.new(url, options) do |conn|
|
68
77
|
if conn.open(options) && block_given?
|
69
78
|
case block.arity
|
70
|
-
when 1 then
|
79
|
+
when 1 then yield conn
|
71
80
|
else conn.instance_eval(&block)
|
72
81
|
end
|
73
82
|
else
|
@@ -83,24 +92,27 @@ module RDF::Sesame
|
|
83
92
|
# @param [Hash{Symbol => Object}] options
|
84
93
|
# @yield [connection]
|
85
94
|
# @yieldparam [Connection]
|
86
|
-
def initialize(url, options = {}, &block)
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
95
|
+
def initialize(url = nil, options = {}, &block)
|
96
|
+
url ||= "http://localhost:8080/openrdf-sesame"
|
97
|
+
parsed = URI.parse(url.to_s)
|
98
|
+
|
99
|
+
@user = options.delete(:user) || parsed.user || nil
|
100
|
+
@pass = options.delete(:pass) || parsed.password || nil
|
92
101
|
|
93
102
|
# Preserve only those URI components that we actually require for
|
94
103
|
# establishing a connection to the HTTP server in question:
|
95
|
-
|
104
|
+
parsed.user = parsed.password = nil
|
105
|
+
@url = parsed
|
96
106
|
|
107
|
+
@proxy_host = options.delete(:proxy_host) || nil
|
108
|
+
@proxy_port = options.delete(:proxy_port) || nil
|
97
109
|
@headers = options.delete(:headers) || {}
|
98
110
|
@options = options
|
99
111
|
@connected = false
|
100
112
|
|
101
113
|
if block_given?
|
102
114
|
case block.arity
|
103
|
-
when 1 then
|
115
|
+
when 1 then yield self
|
104
116
|
else instance_eval(&block)
|
105
117
|
end
|
106
118
|
end
|
@@ -128,7 +140,7 @@ module RDF::Sesame
|
|
128
140
|
#
|
129
141
|
# @return [Symbol]
|
130
142
|
def scheme
|
131
|
-
url.scheme.to_s.to_sym
|
143
|
+
@url.scheme.to_s.to_sym
|
132
144
|
end
|
133
145
|
|
134
146
|
##
|
@@ -137,7 +149,7 @@ module RDF::Sesame
|
|
137
149
|
#
|
138
150
|
# @return [Boolean]
|
139
151
|
def userinfo?
|
140
|
-
|
152
|
+
!@url.userinfo.nil?
|
141
153
|
end
|
142
154
|
|
143
155
|
##
|
@@ -145,7 +157,7 @@ module RDF::Sesame
|
|
145
157
|
#
|
146
158
|
# @return [String] "username:password"
|
147
159
|
def userinfo
|
148
|
-
url.userinfo
|
160
|
+
@url.userinfo
|
149
161
|
end
|
150
162
|
|
151
163
|
##
|
@@ -153,15 +165,7 @@ module RDF::Sesame
|
|
153
165
|
#
|
154
166
|
# @return [Boolean]
|
155
167
|
def user?
|
156
|
-
!
|
157
|
-
end
|
158
|
-
|
159
|
-
##
|
160
|
-
# Returns any user name information for this connection.
|
161
|
-
#
|
162
|
-
# @return [String]
|
163
|
-
def user
|
164
|
-
url.user
|
168
|
+
!user.nil?
|
165
169
|
end
|
166
170
|
|
167
171
|
##
|
@@ -169,15 +173,7 @@ module RDF::Sesame
|
|
169
173
|
#
|
170
174
|
# @return [Boolean]
|
171
175
|
def password?
|
172
|
-
!
|
173
|
-
end
|
174
|
-
|
175
|
-
##
|
176
|
-
# Returns any password information for this connection.
|
177
|
-
#
|
178
|
-
# @return [String]
|
179
|
-
def password
|
180
|
-
url.password
|
176
|
+
!password.nil?
|
181
177
|
end
|
182
178
|
|
183
179
|
##
|
@@ -185,7 +181,7 @@ module RDF::Sesame
|
|
185
181
|
#
|
186
182
|
# @return [String]
|
187
183
|
def host
|
188
|
-
url.host.to_s
|
184
|
+
@url.host.to_s
|
189
185
|
end
|
190
186
|
|
191
187
|
alias_method :hostname, :host
|
@@ -196,7 +192,7 @@ module RDF::Sesame
|
|
196
192
|
#
|
197
193
|
# @return [Boolean]
|
198
194
|
def port?
|
199
|
-
|
195
|
+
!@url.port.nil? && @url.port != (insecure? ? 80 : 443)
|
200
196
|
end
|
201
197
|
|
202
198
|
##
|
@@ -204,20 +200,7 @@ module RDF::Sesame
|
|
204
200
|
#
|
205
201
|
# @return [Integer]
|
206
202
|
def port
|
207
|
-
url.port
|
208
|
-
end
|
209
|
-
|
210
|
-
##
|
211
|
-
# Returns a `Hash` representation of this connection.
|
212
|
-
#
|
213
|
-
# @return [Hash{Symbol => Object}]
|
214
|
-
def to_hash
|
215
|
-
{
|
216
|
-
:scheme => url.scheme,
|
217
|
-
:userinfo => url.userinfo,
|
218
|
-
:host => url.host,
|
219
|
-
:port => url.port,
|
220
|
-
}
|
203
|
+
@url.port
|
221
204
|
end
|
222
205
|
|
223
206
|
##
|
@@ -225,7 +208,7 @@ module RDF::Sesame
|
|
225
208
|
#
|
226
209
|
# @return [RDF::URI]
|
227
210
|
def to_uri
|
228
|
-
url
|
211
|
+
@url
|
229
212
|
end
|
230
213
|
|
231
214
|
##
|
@@ -233,7 +216,7 @@ module RDF::Sesame
|
|
233
216
|
#
|
234
217
|
# @return [String]
|
235
218
|
def to_s
|
236
|
-
url.to_s
|
219
|
+
@url.to_s
|
237
220
|
end
|
238
221
|
|
239
222
|
##
|
@@ -252,14 +235,14 @@ module RDF::Sesame
|
|
252
235
|
# @yieldparam [Connection] connection
|
253
236
|
# @raise [TimeoutError] if the connection could not be opened
|
254
237
|
# @return [Connection]
|
255
|
-
def open(options = {}
|
238
|
+
def open(options = {})
|
256
239
|
unless connected?
|
257
240
|
# TODO: support persistent connections
|
258
241
|
@connected = true
|
259
242
|
end
|
260
243
|
|
261
244
|
if block_given?
|
262
|
-
result =
|
245
|
+
result = yield self
|
263
246
|
close
|
264
247
|
result
|
265
248
|
else
|
@@ -292,11 +275,13 @@ module RDF::Sesame
|
|
292
275
|
# @yield [response]
|
293
276
|
# @yieldparam [Net::HTTPResponse] response
|
294
277
|
# @return [Net::HTTPResponse]
|
295
|
-
def get(path, headers = {}
|
296
|
-
Net::HTTP.start(host, port) do |http|
|
297
|
-
|
278
|
+
def get(path, headers = {})
|
279
|
+
Net::HTTP::Proxy(@proxy_host, @proxy_port).start(host, port, :use_ssl => self.secure?) do |http|
|
280
|
+
request = Net::HTTP::Get.new(url(path.to_s), @headers.merge(headers))
|
281
|
+
request.basic_auth @user, @pass unless @user.nil? || @pass.nil?
|
282
|
+
response = http.request(request)
|
298
283
|
if block_given?
|
299
|
-
|
284
|
+
yield response
|
300
285
|
else
|
301
286
|
response
|
302
287
|
end
|
@@ -312,11 +297,14 @@ module RDF::Sesame
|
|
312
297
|
# @yield [response]
|
313
298
|
# @yieldparam [Net::HTTPResponse] response
|
314
299
|
# @return [Net::HTTPResponse]
|
315
|
-
def post(path, data, headers = {}
|
316
|
-
|
317
|
-
|
300
|
+
def post(path, data, headers = {})
|
301
|
+
Net::HTTP::Proxy(@proxy_host, @proxy_port).start(host, port, :use_ssl => self.secure?) do |http|
|
302
|
+
request = Net::HTTP::Post.new(url(path.to_s), @headers.merge(headers))
|
303
|
+
request.body = data.to_s
|
304
|
+
request.basic_auth @user, @pass unless @user.nil? || @pass.nil?
|
305
|
+
response = http.request(request)
|
318
306
|
if block_given?
|
319
|
-
|
307
|
+
yield response
|
320
308
|
else
|
321
309
|
response
|
322
310
|
end
|
@@ -331,8 +319,20 @@ module RDF::Sesame
|
|
331
319
|
# @yield [response]
|
332
320
|
# @yieldparam [Net::HTTPResponse] response
|
333
321
|
# @return [Net::HTTPResponse]
|
334
|
-
def put(path, headers = {}
|
335
|
-
|
322
|
+
def put(path, data, headers = {})
|
323
|
+
Net::HTTP::Proxy(@proxy_host, @proxy_port).start(host, port, :use_ssl => self.secure?) do |http|
|
324
|
+
request = Net::HTTP::Put.new(url(path.to_s), @headers.merge(headers))
|
325
|
+
request.body = data.to_s
|
326
|
+
request.basic_auth @user, @pass unless @user.nil? || @pass.nil?
|
327
|
+
response = http.request(request)
|
328
|
+
http.request(request) do |response|
|
329
|
+
if block_given?
|
330
|
+
yield response
|
331
|
+
else
|
332
|
+
response
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
336
|
end
|
337
337
|
|
338
338
|
##
|
@@ -343,15 +343,25 @@ module RDF::Sesame
|
|
343
343
|
# @yield [response]
|
344
344
|
# @yieldparam [Net::HTTPResponse] response
|
345
345
|
# @return [Net::HTTPResponse]
|
346
|
-
def delete(path, headers = {}
|
347
|
-
Net::HTTP.start(host, port) do |http|
|
348
|
-
|
346
|
+
def delete(path, headers = {})
|
347
|
+
Net::HTTP::Proxy(@proxy_host, @proxy_port).start(host, port, :use_ssl => self.secure?) do |http|
|
348
|
+
request = Net::HTTP::Delete.new(url(path.to_s), @headers.merge(headers))
|
349
|
+
request.basic_auth @user, @pass unless @user.nil? || @pass.nil?
|
350
|
+
response = http.request(request)
|
349
351
|
if block_given?
|
350
|
-
|
352
|
+
yield response
|
351
353
|
else
|
352
354
|
response
|
353
355
|
end
|
354
356
|
end
|
355
357
|
end
|
358
|
+
|
359
|
+
def url(path)
|
360
|
+
if path
|
361
|
+
"#{@url}/#{path}"
|
362
|
+
else
|
363
|
+
@url.to_s
|
364
|
+
end
|
365
|
+
end
|
356
366
|
end # class Connection
|
357
367
|
end # module RDF::Sesame
|
@@ -26,10 +26,6 @@ module RDF::Sesame
|
|
26
26
|
# @see http://www.openrdf.org/doc/sesame2/system/ch08.html
|
27
27
|
# @see http://rdf.rubyforge.org/RDF/Repository.html
|
28
28
|
class Repository < RDF::Repository
|
29
|
-
# @return [RDF::URI]
|
30
|
-
attr_reader :url
|
31
|
-
alias_method :uri, :url
|
32
|
-
|
33
29
|
# @return [String]
|
34
30
|
attr_reader :id
|
35
31
|
|
@@ -39,11 +35,20 @@ module RDF::Sesame
|
|
39
35
|
# @return [Server]
|
40
36
|
attr_reader :server
|
41
37
|
|
38
|
+
# @return [String]
|
39
|
+
attr_reader :readable
|
40
|
+
|
41
|
+
# @return [String]
|
42
|
+
attr_reader :writeble
|
43
|
+
|
44
|
+
# @return [String,Array]
|
45
|
+
attr_reader :context
|
46
|
+
|
42
47
|
##
|
43
48
|
# Initializes this `Repository` instance.
|
44
49
|
#
|
45
50
|
# @overload initialize(url)
|
46
|
-
# @param [String, RDF::URI] url
|
51
|
+
# @param [String, URI, RDF::URI] url
|
47
52
|
# @yield [repository]
|
48
53
|
# @yieldparam [Repository]
|
49
54
|
#
|
@@ -56,73 +61,80 @@ module RDF::Sesame
|
|
56
61
|
# @yieldparam [Repository]
|
57
62
|
#
|
58
63
|
def initialize(url_or_options, &block)
|
64
|
+
options = {}
|
59
65
|
case url_or_options
|
60
|
-
when String
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
require 'pathname' unless defined?(Pathname)
|
65
|
-
@uri = url_or_options
|
66
|
-
@server = Server.new(RDF::URI.new({
|
67
|
-
:scheme => @uri.scheme,
|
68
|
-
:userinfo => @uri.userinfo,
|
69
|
-
:host => @uri.host,
|
70
|
-
:port => @uri.port,
|
71
|
-
:path => Pathname.new(@uri.path).parent.parent.to_s, # + '../..'
|
72
|
-
}))
|
73
|
-
@options = {}
|
66
|
+
when String, RDF::URI, URI
|
67
|
+
pathname = Pathname.new(url_or_options.to_s)
|
68
|
+
@server = Server.new(pathname.parent.parent.to_s)
|
69
|
+
@id = pathname.basename.to_s
|
74
70
|
|
75
71
|
when Hash
|
76
72
|
raise ArgumentError, "missing options[:server]" unless url_or_options.has_key?(:server)
|
77
73
|
raise ArgumentError, "missing options[:id]" unless url_or_options.has_key?(:id)
|
78
|
-
|
79
|
-
@server
|
80
|
-
@id
|
81
|
-
@
|
82
|
-
@
|
74
|
+
options = url_or_options.dup
|
75
|
+
@server = options.delete(:server)
|
76
|
+
@id = options.delete(:id)
|
77
|
+
@readable = options.delete(:readable)
|
78
|
+
@writable = options.delete(:writable)
|
83
79
|
|
84
80
|
else
|
85
81
|
raise ArgumentError, "expected String, RDF::URI or Hash, but got #{url_or_options.inspect}"
|
86
82
|
end
|
87
83
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
84
|
+
super(options)
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
# Returns the URL for the given server-relative `path`.
|
89
|
+
#
|
90
|
+
# @example Getting a Sesame server's URL
|
91
|
+
# server.url #=> "http://localhost:8080/openrdf-sesame"
|
92
|
+
|
93
|
+
# @example Getting a Sesame server's protocol URL
|
94
|
+
# server.url(:protocol) #=> "http://localhost:8080/openrdf-sesame/protocol"
|
95
|
+
#
|
96
|
+
# @param [String, #to_s] path
|
97
|
+
# @return [String]
|
98
|
+
def url(relative_path = nil)
|
99
|
+
self.server.url(path(relative_path))
|
94
100
|
end
|
95
101
|
|
96
102
|
##
|
97
|
-
# Returns the
|
103
|
+
# Returns the server-relative path for the given repository-relative `path`.
|
98
104
|
#
|
99
105
|
# @param [String, #to_s] path
|
100
106
|
# @param [Hash, RDF::Statement] query
|
101
|
-
# @return [
|
102
|
-
def
|
103
|
-
url =
|
107
|
+
# @return [String]
|
108
|
+
def path(path = nil, query = {})
|
109
|
+
url = RDF::URI.new(path ? "repositories/#{@id}/#{path}" : "repositories/#{@id}")
|
104
110
|
unless query.nil?
|
105
111
|
case query
|
106
112
|
when RDF::Statement
|
107
113
|
writer = RDF::NTriples::Writer.new
|
108
|
-
|
114
|
+
q = {
|
109
115
|
:subj => writer.format_value(query.subject),
|
110
116
|
:pred => writer.format_value(query.predicate),
|
111
|
-
:obj => writer.format_value(query.object)
|
112
|
-
:context => query.has_context? ? writer.format_value(query.context) : 'null',
|
117
|
+
:obj => writer.format_value(query.object)
|
113
118
|
}
|
114
|
-
|
119
|
+
q.merge!(:context => writer.format_value(query.context)) if query.has_context?
|
120
|
+
url.query_values = q
|
115
121
|
when Hash
|
116
122
|
url.query_values = query unless query.empty?
|
117
123
|
end
|
118
124
|
end
|
119
|
-
return url
|
125
|
+
return url.to_s
|
120
126
|
end
|
121
127
|
|
122
128
|
alias_method :uri, :url
|
123
129
|
|
124
130
|
##
|
131
|
+
# A mapping of blank node results for this client
|
125
132
|
# @private
|
133
|
+
def nodes
|
134
|
+
@nodes ||= {}
|
135
|
+
end
|
136
|
+
|
137
|
+
##
|
126
138
|
# @see RDF::Repository#supports?
|
127
139
|
def supports?(feature)
|
128
140
|
case feature.to_sym
|
@@ -132,80 +144,94 @@ module RDF::Sesame
|
|
132
144
|
end
|
133
145
|
|
134
146
|
##
|
135
|
-
# @private
|
136
147
|
# @see RDF::Durable#durable?
|
137
148
|
def durable?
|
138
149
|
true # TODO: would need to query the SYSTEM repository for this information
|
139
150
|
end
|
140
151
|
|
141
152
|
##
|
142
|
-
# @private
|
143
153
|
# @see RDF::Countable#empty?
|
144
154
|
def empty?
|
145
155
|
count.zero?
|
146
156
|
end
|
147
157
|
|
148
158
|
##
|
149
|
-
# @private
|
150
159
|
# @see RDF::Countable#count
|
151
160
|
# @see http://www.openrdf.org/doc/sesame2/system/ch08.html#d0e569
|
152
161
|
def count
|
153
|
-
server.get(
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
end
|
162
|
+
response = server.get(path(:size, statements_options))
|
163
|
+
begin
|
164
|
+
size = response.body
|
165
|
+
size.to_i
|
166
|
+
rescue
|
167
|
+
0
|
160
168
|
end
|
161
169
|
end
|
162
170
|
|
163
171
|
##
|
164
|
-
# @private
|
165
172
|
# @see RDF::Enumerable#has_triple?
|
166
173
|
def has_triple?(triple)
|
167
174
|
has_statement?(RDF::Statement.from(triple))
|
168
175
|
end
|
169
176
|
|
170
177
|
##
|
171
|
-
# @private
|
172
178
|
# @see RDF::Enumerable#has_quad?
|
173
179
|
def has_quad?(quad)
|
174
180
|
has_statement?(RDF::Statement.new(quad[0], quad[1], quad[2], :context => quad[3]))
|
175
181
|
end
|
176
182
|
|
177
183
|
##
|
178
|
-
# @private
|
179
184
|
# @see RDF::Enumerable#has_statement?
|
180
185
|
# @see http://www.openrdf.org/doc/sesame2/system/ch08.html#d0e304
|
181
186
|
def has_statement?(statement)
|
182
|
-
server.get(
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
187
|
+
response = server.get(path(:statements, statement), 'Accept' => 'text/plain')
|
188
|
+
!response.body.empty?
|
189
|
+
end
|
190
|
+
|
191
|
+
##
|
192
|
+
# Returns `true` if `self` contains the given RDF subject term.
|
193
|
+
#
|
194
|
+
# @param [RDF::Resource] value
|
195
|
+
# @return [Boolean]
|
196
|
+
def has_subject?(value)
|
197
|
+
!first([value, nil, nil]).nil?
|
198
|
+
end
|
199
|
+
|
200
|
+
##
|
201
|
+
# Returns `true` if `self` contains the given RDF predicate term.
|
202
|
+
#
|
203
|
+
# @param [RDF::URI] value
|
204
|
+
# @return [Boolean]
|
205
|
+
def has_predicate?(value)
|
206
|
+
!first([nil, value, nil]).nil?
|
207
|
+
end
|
208
|
+
|
209
|
+
##
|
210
|
+
# Returns `true` if `self` contains the given RDF object term.
|
211
|
+
#
|
212
|
+
# @param [RDF::Term] value
|
213
|
+
# @return [Boolean]
|
214
|
+
def has_object?(value)
|
215
|
+
!first([nil, nil, value]).nil?
|
189
216
|
end
|
190
217
|
|
191
218
|
##
|
192
|
-
# @private
|
193
219
|
# @see RDF::Enumerable#each_statement
|
194
220
|
# @see http://www.openrdf.org/doc/sesame2/system/ch08.html#d0e304
|
195
|
-
def each_statement
|
221
|
+
def each_statement
|
196
222
|
return enum_statement unless block_given?
|
197
223
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
224
|
+
# Each context will trigger a query that will be parsed with a NTriples::Reader.
|
225
|
+
# This is for performance. Otherwise only one query with TriG::Reader will be
|
226
|
+
# necessary
|
227
|
+
|
228
|
+
['null', *enum_context].uniq.each do |context|
|
229
|
+
query = {}
|
230
|
+
query.merge!(:context => serialize_context(context)) if context
|
231
|
+
response = server.get(path(:statements, query), 'Accept' => 'text/plain')
|
232
|
+
RDF::NTriples::Reader.new(response.body).each_statement do |statement|
|
233
|
+
statement.context = context
|
234
|
+
yield statement
|
209
235
|
end
|
210
236
|
end
|
211
237
|
end
|
@@ -213,25 +239,120 @@ module RDF::Sesame
|
|
213
239
|
alias_method :each, :each_statement
|
214
240
|
|
215
241
|
##
|
216
|
-
# @private
|
217
242
|
# @see RDF::Enumerable#each_context
|
218
|
-
def each_context
|
243
|
+
def each_context
|
219
244
|
return enum_context unless block_given?
|
220
245
|
|
221
246
|
require 'json' unless defined?(::JSON)
|
222
|
-
server.get(
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
247
|
+
response = server.get(path(:contexts), Server::ACCEPT_JSON)
|
248
|
+
json = ::JSON.parse(response.body)
|
249
|
+
json['results']['bindings'].map { |binding| binding['contextID'] }.each do |context_id|
|
250
|
+
context = case context_id['type'].to_s.to_sym
|
251
|
+
when :bnode then RDF::Node.new(context_id['value'])
|
252
|
+
when :uri then RDF::URI.new(context_id['value'])
|
253
|
+
else
|
254
|
+
nil
|
255
|
+
end
|
256
|
+
yield context if context
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# Run a raw SPARQL query.
|
261
|
+
#
|
262
|
+
# @overload sparql_query(query) {|solution| ... }
|
263
|
+
# @yield solution
|
264
|
+
# @yieldparam [RDF::Query::Solution] solution
|
265
|
+
# @yieldreturn [void]
|
266
|
+
# @return [void]
|
267
|
+
#
|
268
|
+
# @overload sparql_query(pattern)
|
269
|
+
# @return [Enumerator<RDF::Query::Solution>]
|
270
|
+
#
|
271
|
+
# @param [String] query The query to run.
|
272
|
+
# @param [Hash{Symbol => Object}] options
|
273
|
+
# The query options (see build_query).
|
274
|
+
# @return [void]
|
275
|
+
#
|
276
|
+
# @see #build_query
|
277
|
+
def sparql_query(query, options={}, &block)
|
278
|
+
raw_query(query, 'sparql', options, &block)
|
279
|
+
end
|
280
|
+
|
281
|
+
##
|
282
|
+
# Returns all statements of the given query.
|
283
|
+
#
|
284
|
+
# @private
|
285
|
+
# @param [String, #to_s] query
|
286
|
+
# @param [String, #to_s] queryLn
|
287
|
+
# @return [RDF::Enumerator]
|
288
|
+
def raw_query(query, queryLn = 'sparql', options={}, &block)
|
289
|
+
options = { infer: true }.merge(options)
|
290
|
+
|
291
|
+
response = if query =~ /\b(delete|insert)\b/i
|
292
|
+
write_query(query, queryLn, options)
|
293
|
+
else
|
294
|
+
read_query(query, queryLn, options)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
def read_query(query, queryLn, options)
|
299
|
+
if queryLn == 'sparql' and options[:format].nil? and query =~ /\bconstruct\b/i
|
300
|
+
options[:format] = Server::ACCEPT_NTRIPLES
|
301
|
+
end
|
302
|
+
|
303
|
+
options[:format] = Server::ACCEPT_JSON unless options[:format]
|
304
|
+
|
305
|
+
params = Addressable::URI.form_encode({ :query => query, :queryLn => queryLn, :infer => options[:infer] }).gsub("+", "%20").to_s
|
306
|
+
url = Addressable::URI.parse(path)
|
307
|
+
unless url.normalize.query.nil?
|
308
|
+
url.query = [url.query, params].compact.join('&')
|
309
|
+
else
|
310
|
+
url.query = [url.query, params].compact.join('?')
|
311
|
+
end
|
312
|
+
response = server.get(url, options[:format])
|
313
|
+
|
314
|
+
results = parse_response(response)
|
315
|
+
if block_given?
|
316
|
+
results.each {|s| yield s }
|
317
|
+
else
|
318
|
+
results
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
def write_query(query, queryLn, options)
|
323
|
+
parameters = {}
|
324
|
+
parameters[:update] = query
|
325
|
+
response = server.post(path(:statements), Addressable::URI.form_encode(parameters), 'Content-Type' => 'application/x-www-form-urlencoded')
|
326
|
+
response.code == "204"
|
327
|
+
end
|
328
|
+
|
329
|
+
# Set a global context that will be used for any statements request
|
330
|
+
#
|
331
|
+
# @param context the context(s) to use
|
332
|
+
def set_context(*context)
|
333
|
+
options||={}
|
334
|
+
@context = serialize_context(context)
|
335
|
+
end
|
336
|
+
|
337
|
+
##
|
338
|
+
# # Clear all statements from the repository.
|
339
|
+
# @see RDF::Mutable#clear
|
340
|
+
# @see http://www.openrdf.org/doc/sesame2/system/ch08.html#d0e304
|
341
|
+
#
|
342
|
+
# @param [Hash] options
|
343
|
+
# @option options [String] :subject Match a specific subject
|
344
|
+
# @option options [String] :predicate Match a specific predicate
|
345
|
+
# @option options [String] :object Match a specific object
|
346
|
+
# @option options [String] :context Match a specific graph name.
|
347
|
+
# @return [void]
|
348
|
+
def clear(options={})
|
349
|
+
parameters = {}
|
350
|
+
{ :subject => :subj, :predicate => :pred, :object => :obj, :context => :context }.each do |option_key, parameter_key|
|
351
|
+
value = options[option_key]
|
352
|
+
parameters.merge! parameter_key => RDF::NTriples.serialize(RDF::URI.new(value)) if value
|
234
353
|
end
|
354
|
+
response = server.delete(path(:statements, statements_options.merge(parameters)))
|
355
|
+
response.code == "204"
|
235
356
|
end
|
236
357
|
|
237
358
|
protected
|
@@ -239,23 +360,39 @@ module RDF::Sesame
|
|
239
360
|
##
|
240
361
|
# @private
|
241
362
|
# @see RDF::Queryable#query
|
242
|
-
def query_pattern(pattern
|
243
|
-
|
363
|
+
def query_pattern(pattern)
|
364
|
+
writer = RDF::NTriples::Writer.new
|
365
|
+
query = {}
|
366
|
+
query.merge!(:context => writer.format_value(pattern.context)) if pattern.has_context?
|
367
|
+
query.merge!(:subj => writer.format_value(pattern.subject)) unless pattern.subject.is_a?(RDF::Query::Variable) || pattern.subject.nil?
|
368
|
+
query.merge!(:pred => writer.format_value(pattern.predicate)) unless pattern.predicate.is_a?(RDF::Query::Variable) || pattern.predicate.nil?
|
369
|
+
query.merge!(:obj => writer.format_value(pattern.object)) unless pattern.object.is_a?(RDF::Query::Variable) || pattern.object.nil?
|
370
|
+
response = server.get(path(:statements, query), Server::ACCEPT_NTRIPLES)
|
371
|
+
RDF::NTriples::Reader.new(response.body).each_statement do |statement|
|
372
|
+
statement.context = pattern.context
|
373
|
+
yield statement
|
374
|
+
end
|
244
375
|
end
|
245
376
|
|
377
|
+
#--------------------------------------------------------------------
|
378
|
+
# @group RDF::Mutable methods
|
379
|
+
|
246
380
|
##
|
247
381
|
# @private
|
248
382
|
# @see RDF::Mutable#insert
|
249
383
|
# @see http://www.openrdf.org/doc/sesame2/system/ch08.html#d0e304
|
250
384
|
def insert_statement(statement)
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
385
|
+
insert_statements([statement])
|
386
|
+
end
|
387
|
+
|
388
|
+
##
|
389
|
+
# @private
|
390
|
+
# @see RDF::Mutable#insert
|
391
|
+
# @see http://www.openrdf.org/doc/sesame2/system/ch08.html#d0e304
|
392
|
+
def insert_statements(statements)
|
393
|
+
data = statements_to_text_plain(statements)
|
394
|
+
response = server.post(path(:statements, statements_options), data, 'Content-Type' => 'text/plain')
|
395
|
+
response.code == "204"
|
259
396
|
end
|
260
397
|
|
261
398
|
##
|
@@ -263,25 +400,165 @@ module RDF::Sesame
|
|
263
400
|
# @see RDF::Mutable#delete
|
264
401
|
# @see http://www.openrdf.org/doc/sesame2/system/ch08.html#d0e304
|
265
402
|
def delete_statement(statement)
|
266
|
-
server.delete(
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
403
|
+
response = server.delete(path(:statements, statement))
|
404
|
+
response.code == "204"
|
405
|
+
end
|
406
|
+
|
407
|
+
private
|
408
|
+
|
409
|
+
# Convert a list of statements to a text-plain-compatible text.
|
410
|
+
def statements_to_text_plain(statements)
|
411
|
+
graph = RDF::Repository.new
|
412
|
+
statements.each do |s|
|
413
|
+
graph << s
|
271
414
|
end
|
415
|
+
RDF::NTriples::Writer.dump(graph, nil, :encoding => Encoding::ASCII)
|
272
416
|
end
|
273
417
|
|
274
418
|
##
|
419
|
+
# @param [Net::HTTPSuccess] response
|
420
|
+
# @param [Hash{Symbol => Object}] options
|
421
|
+
# @return [Object]
|
422
|
+
def parse_response(response, options = {})
|
423
|
+
case content_type = options[:content_type] || response.content_type
|
424
|
+
when Server::RESULT_BOOL
|
425
|
+
response.body == 'true'
|
426
|
+
when Server::RESULT_JSON
|
427
|
+
self.class.parse_json_bindings(response.body, nodes)
|
428
|
+
when Server::RESULT_XML
|
429
|
+
self.class.parse_xml_bindings(response.body, nodes)
|
430
|
+
else
|
431
|
+
parse_rdf_serialization(response, options)
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
##
|
436
|
+
# @param [String, Hash] json
|
437
|
+
# @return [<RDF::Query::Solutions>]
|
438
|
+
# @see http://www.w3.org/TR/rdf-sparql-json-res/#results
|
439
|
+
def self.parse_json_bindings(json, nodes = {})
|
440
|
+
require 'json' unless defined?(::JSON)
|
441
|
+
json = JSON.parse(json.to_s) unless json.is_a?(Hash)
|
442
|
+
|
443
|
+
case
|
444
|
+
when json['boolean']
|
445
|
+
json['boolean']
|
446
|
+
when json['results']
|
447
|
+
solutions = RDF::Query::Solutions()
|
448
|
+
json['results']['bindings'].each do |row|
|
449
|
+
row = row.inject({}) do |cols, (name, value)|
|
450
|
+
cols.merge(name.to_sym => parse_json_value(value))
|
451
|
+
end
|
452
|
+
solutions << RDF::Query::Solution.new(row)
|
453
|
+
end
|
454
|
+
solutions
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
##
|
459
|
+
# @param [Hash{String => String}] value
|
460
|
+
# @return [RDF::Value]
|
461
|
+
# @see http://www.w3.org/TR/rdf-sparql-json-res/#variable-binding-results
|
462
|
+
def self.parse_json_value(value, nodes = {})
|
463
|
+
case value['type'].to_sym
|
464
|
+
when :bnode
|
465
|
+
nodes[id = value['value']] ||= RDF::Node.new(id)
|
466
|
+
when :uri
|
467
|
+
RDF::URI.new(value['value'])
|
468
|
+
when :literal
|
469
|
+
RDF::Literal.new(value['value'], :language => value['xml:lang'])
|
470
|
+
when :'typed-literal'
|
471
|
+
RDF::Literal.new(value['value'], :datatype => value['datatype'])
|
472
|
+
else nil
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
##
|
477
|
+
# @param [String, REXML::Element] xml
|
478
|
+
# @return [<RDF::Query::Solutions>]
|
479
|
+
# @see http://www.w3.org/TR/rdf-sparql-json-res/#results
|
480
|
+
def self.parse_xml_bindings(xml, nodes = {})
|
481
|
+
xml.force_encoding(::Encoding::UTF_8) if xml.respond_to?(:force_encoding)
|
482
|
+
require 'rexml/document' unless defined?(::REXML::Document)
|
483
|
+
xml = REXML::Document.new(xml).root unless xml.is_a?(REXML::Element)
|
484
|
+
|
485
|
+
case
|
486
|
+
when boolean = xml.elements['boolean']
|
487
|
+
boolean.text == 'true'
|
488
|
+
when results = xml.elements['results']
|
489
|
+
solutions = RDF::Query::Solutions()
|
490
|
+
results.elements.each do |result|
|
491
|
+
row = {}
|
492
|
+
result.elements.each do |binding|
|
493
|
+
name = binding.attributes['name'].to_sym
|
494
|
+
value = binding.select { |node| node.kind_of?(::REXML::Element) }.first
|
495
|
+
row[name] = parse_xml_value(value, nodes)
|
496
|
+
end
|
497
|
+
solutions << RDF::Query::Solution.new(row)
|
498
|
+
end
|
499
|
+
solutions
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
##
|
504
|
+
# @param [REXML::Element] value
|
505
|
+
# @return [RDF::Value]
|
506
|
+
# @see http://www.w3.org/TR/rdf-sparql-json-res/#variable-binding-results
|
507
|
+
def self.parse_xml_value(value, nodes = {})
|
508
|
+
case value.name.to_sym
|
509
|
+
when :bnode
|
510
|
+
nodes[id = value.text] ||= RDF::Node.new(id)
|
511
|
+
when :uri
|
512
|
+
RDF::URI.new(value.text)
|
513
|
+
when :literal
|
514
|
+
RDF::Literal.new(value.text, {
|
515
|
+
:language => value.attributes['xml:lang'],
|
516
|
+
:datatype => value.attributes['datatype'],
|
517
|
+
})
|
518
|
+
else nil
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
##
|
523
|
+
# @param [Net::HTTPSuccess] response
|
524
|
+
# @param [Hash{Symbol => Object}] options
|
525
|
+
# @return [RDF::Enumerable]
|
526
|
+
def parse_rdf_serialization(response, options = {})
|
527
|
+
options = {:content_type => response.content_type} if options.empty?
|
528
|
+
if reader_for = RDF::Reader.for(options)
|
529
|
+
reader_for.new(response.body) do |reader|
|
530
|
+
reader # FIXME
|
531
|
+
end
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
275
535
|
# @private
|
276
|
-
#
|
277
|
-
#
|
278
|
-
def
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
536
|
+
#
|
537
|
+
# Serialize the context
|
538
|
+
def serialize_context(context)
|
539
|
+
context = [context] unless context.is_a?(Enumerable)
|
540
|
+
serialization = context.map do |c|
|
541
|
+
if c == 'null' or !c
|
542
|
+
c
|
543
|
+
else
|
544
|
+
RDF::NTriples.serialize(RDF::URI.new(c))
|
283
545
|
end
|
284
546
|
end
|
547
|
+
|
548
|
+
if serialization.size == 1
|
549
|
+
serialization.first
|
550
|
+
else
|
551
|
+
serialization
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
555
|
+
# @private
|
556
|
+
#
|
557
|
+
# Construct the statements options list
|
558
|
+
def statements_options
|
559
|
+
options = {}
|
560
|
+
options[:context] = @context if @context
|
561
|
+
options
|
285
562
|
end
|
286
563
|
end # class Repository
|
287
564
|
end # module RDF::Sesame
|