dpla-sparql-client 1.1.3.2.pre.dpla.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c50c325669a54017b51fd39cc9b45c917975fe4b
4
+ data.tar.gz: 1faeb1c880d70a5d17b7ed31728f74d28b4a0d68
5
+ SHA512:
6
+ metadata.gz: 5f7efd4c331a19b1255e24c386364f33e3046b35d6159783f829e54e4dd0ea140749195d8d8dd565e9e923f9310b0aa3314c00e39c1ed8aaf4be05c69cda2810
7
+ data.tar.gz: f077ec74a124366ddbc0633ad5891af4563d4f3885dabc3da26fdd136ae54d30bc7662ab1fb2c94db793d87fcad0680dc2835234bb2d8995d8bd043ec00eed4f
data/AUTHORS ADDED
@@ -0,0 +1,3 @@
1
+ * Arto Bendiken <arto@bendiken.net>
2
+ * Ben Lavender <blavender@gmail.com>
3
+ * Gregg Kellogg <gregg@greggkellogg.net>
data/CREDITS ADDED
@@ -0,0 +1,12 @@
1
+ * Christoph Badura <bad@bsd.de>
2
+ * Thomas Feron <tho.feron@gmail.com>
3
+ * James Hetherington <jamespjh@googlemail.com>
4
+ * Gabriel Horner <gabriel.horner@gmail.com>
5
+ * Nicholas Humfrey <njh@aelius.com>
6
+ * Fumihiro Kato <fumi@fumi.me>
7
+ * David Nielsen <david@graveyard.dk>
8
+ * Thamaraiselvan Poomalai <p.thamarai@gmail.com>
9
+ * Michael Sokol <mikaa123@gmail.com>
10
+ * Yves Raimond <yves.raimond@bbc.co.uk>
11
+ * Danny Tran <dannybtran@gmail.com>
12
+ * Nick Gottlieb <ngottlieb@gmail.com>
data/README ADDED
@@ -0,0 +1,166 @@
1
+ #SPARQL Client for RDF.rb
2
+
3
+ This is a [Ruby][] implementation of a [SPARQL][] client for [RDF.rb][].
4
+
5
+ * <http://ruby-rdf.github.com/sparql-client/>
6
+
7
+ [![Gem Version](https://badge.fury.io/rb/sparql-client.png)](http://badge.fury.io/rb/sparql-client)
8
+ [![Build Status](https://travis-ci.org/ruby-rdf/sparql-client.png?branch=master)](http://travis-ci.org/ruby-rdf/sparql-client)
9
+
10
+ ##Features
11
+
12
+ * Executes queries against any SPARQL 1.0/1.1-compatible endpoint over HTTP,
13
+ or against an `RDF::Queryable` instance, using the `SPARQL` gem.
14
+ * Provides a query builder [DSL][] for `ASK`, `SELECT`, `DESCRIBE` and
15
+ `CONSTRUCT` queries.
16
+ * Includes preliminary support for some SPARQL 1.1 Update operations.
17
+ * Supports tuple result sets in both XML, JSON, CSV and TSV formats, with JSON being
18
+ the preferred default for content-negotiation purposes.
19
+ * Supports graph results in any RDF serialization format understood by RDF.rb.
20
+ * Returns results using the [RDF.rb object model][RDF.rb model].
21
+ * Supports accessing endpoints as read/write [`RDF::Repository`][RDF::Repository]
22
+ instances {SPARQL::Client::Repository}.
23
+
24
+ ##Examples
25
+
26
+ ### Querying a remote SPARQL endpoint
27
+ require 'sparql/client'
28
+
29
+ sparql = SPARQL::Client.new("http://dbpedia.org/sparql")
30
+
31
+ ### Querying a `RDF::Repository` instance
32
+
33
+ require 'rdf/trig'
34
+ repository = RDF::Repository.load("http://example/dataset.trig")
35
+
36
+ sparql = SPARQL::Client.new(repository)
37
+
38
+ ### Executing a boolean query and outputting the result
39
+
40
+ # ASK WHERE { ?s ?p ?o }
41
+ result = sparql.ask.whether([:s, :p, :o]).true?
42
+
43
+ puts result.inspect #=> true or false
44
+
45
+ ### Executing a tuple query and iterating over the returned solutions
46
+
47
+ # SELECT * WHERE { ?s ?p ?o } OFFSET 100 LIMIT 10
48
+ query = sparql.select.where([:s, :p, :o]).offset(100).limit(10)
49
+
50
+ query.each_solution do |solution|
51
+ puts solution.inspect
52
+ end
53
+
54
+ ### Executing a graph query and iterating over the returned statements
55
+
56
+ # CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o } LIMIT 10
57
+ query = sparql.construct([:s, :p, :o]).where([:s, :p, :o]).limit(10)
58
+
59
+ query.each_statement do |statement|
60
+ puts statement.inspect
61
+ end
62
+
63
+ ### Executing an arbitrary textual SPARQL query string
64
+
65
+ result = sparql.query("ASK WHERE { ?s ?p ?o }")
66
+
67
+ puts result.inspect #=> true or false
68
+
69
+ ##Documentation
70
+
71
+ * {SPARQL::Client}
72
+ * {SPARQL::Client::Query}
73
+ * {SPARQL::Client::Repository}
74
+
75
+ ##Dependencies
76
+
77
+ * [Ruby](http://ruby-lang.org/) (>= 1.9.3)
78
+ * [RDF.rb](http://rubygems.org/gems/rdf) (>= 1.1)
79
+ * [Net::HTTP::Persistent](http://rubygems.org/gems/net-http-persistent) (>= 1.4)
80
+ * Soft dependency on [SPARQL](http://rubygems.org/gems/sparql) (>= 1.1)
81
+ * Soft dependency on [Nokogiri](http://rubygems.org/gems/nokogiri) (>= 1.5)
82
+
83
+ ##Installation
84
+
85
+ The recommended installation method is via [RubyGems](http://rubygems.org/).
86
+ To install the latest official release of the `SPARQL::Client` gem, do:
87
+
88
+ % [sudo] gem install sparql-client
89
+
90
+ ##Download
91
+
92
+ To get a local working copy of the development repository, do:
93
+
94
+ % git clone git://github.com/ruby-rdf/sparql-client.git
95
+
96
+ Alternatively, download the latest development version as a tarball as
97
+ follows:
98
+
99
+ % wget http://github.com/ruby-rdf/sparql-client/tarball/master
100
+
101
+ ##Mailing List
102
+
103
+ * <http://lists.w3.org/Archives/Public/public-rdf-ruby/>
104
+
105
+ ##Authors
106
+
107
+ * [Arto Bendiken](http://github.com/bendiken) - <http://ar.to/>
108
+ * [Ben Lavender](http://github.com/bhuga) - <http://bhuga.net/>
109
+ * [Gregg Kellogg](http://github.com/gkellogg) - <http://greggkellogg.net/>
110
+
111
+ ##Contributors
112
+
113
+ * [Christoph Badura](http://github.com/bad) - <http://github.com/bad>
114
+ * [James Hetherington](http://github.com/jamespjh) - <http://twitter.com/jamespjh>
115
+ * [Gabriel Horner](http://github.com/cldwalker) - <http://tagaholic.me/>
116
+ * [Nicholas Humfrey](http://github.com/njh) - <http://www.aelius.com/njh/>
117
+ * [Fumihiro Kato](http://github.com/fumi) - <http://fumi.me/>
118
+ * [David Nielsen](http://github.com/drankard) - <http://github.com/drankard>
119
+ * [Thamaraiselvan Poomalai](http://github.com/selvan) - <http://softonaut.blogspot.com/>
120
+ * [Michael Sokol](http://github.com/mikaa123) - <http://sokolmichael.com/>
121
+ * [Yves Raimond](http://github.com/moustaki) - <http://moustaki.org/>
122
+ * [Thomas Feron](http://github.com/thoferon) - <http://github.com/thoferon>
123
+ * [Nick Gottlieb](http://github.com/ngottlieb) - <http://www.nicholasgottlieb.com>
124
+
125
+ ##Contributing
126
+ This repository uses [Git Flow](https://github.com/nvie/gitflow) to mange development and release activity. All submissions _must_ be on a feature branch based on the _develop_ branch to ease staging and integration.
127
+
128
+ * Do your best to adhere to the existing coding conventions and idioms.
129
+ * Don't use hard tabs, and don't leave trailing whitespace on any line.
130
+ * Do document every method you add using [YARD][] annotations. Read the
131
+ [tutorial][YARD-GS] or just look at the existing code for examples.
132
+ * Don't touch the `.gemspec`, `VERSION` or `AUTHORS` files. If you need to
133
+ change them, do so on your private branch only.
134
+ * Do feel free to add yourself to the `CREDITS` file and the corresponding
135
+ list in the the `README`. Alphabetical order applies.
136
+ * Do note that in order for us to merge any non-trivial changes (as a rule
137
+ of thumb, additions larger than about 15 lines of code), we need an
138
+ explicit [public domain dedication][PDD] on record from you.
139
+
140
+ ##Resources
141
+
142
+ * <http://ruby-rdf.github.com/sparql-client/>
143
+ * <http://github.com/ruby-rdf/sparql-client>
144
+ * <http://rubygems.org/gems/sparql-client>
145
+ * <http://rubyforge.org/projects/sparql/>
146
+ * <http://raa.ruby-lang.org/project/sparql-client/>
147
+ * <http://www.ohloh.net/p/rdf>
148
+
149
+ ##License
150
+
151
+ This is free and unencumbered public domain software. For more information,
152
+ see <http://unlicense.org/> or the accompanying {file:UNLICENSE} file.
153
+
154
+ [Ruby]: http://ruby-lang.org/
155
+ [RDF]: http://www.w3.org/RDF/
156
+ [SPARQL]: http://en.wikipedia.org/wiki/SPARQL
157
+ [SPARQL JSON]: http://www.w3.org/TR/rdf-sparql-json-res/
158
+ [RDF.rb]: http://rubygems.org/gems/rdf
159
+ [RDF.rb model]: http://blog.datagraph.org/2010/03/rdf-for-ruby
160
+ [RDF::Repository]: http://rubydoc.info/github/ruby-rdf/rdf/RDF/Repository
161
+ [DSL]: http://en.wikipedia.org/wiki/Domain-specific_language
162
+ "domain-specific language"
163
+ [YARD]: http://yardoc.org/
164
+ [YARD-GS]: http://rubydoc.info/docs/yard/file/docs/GettingStarted.md
165
+ [PDD]: http://unlicense.org/#unlicensing-contributions
166
+ [Backports]: http://rubygems.org/gems/backports
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org/>
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.1.3.2.pre.dpla.1
@@ -0,0 +1,730 @@
1
+ require 'net/http/persistent' # @see http://rubygems.org/gems/net-http-persistent
2
+ require 'rdf' # @see http://rubygems.org/gems/rdf
3
+ require 'rdf/ntriples' # @see http://rubygems.org/gems/rdf
4
+ begin
5
+ require 'nokogiri'
6
+ rescue LoadError
7
+ require 'rexml/document'
8
+ end
9
+
10
+ module SPARQL
11
+ ##
12
+ # A SPARQL 1.0/1.1 client for RDF.rb.
13
+ #
14
+ # @see http://www.w3.org/TR/sparql11-query/
15
+ # @see http://www.w3.org/TR/sparql11-protocol/
16
+ # @see http://www.w3.org/TR/sparql11-results-json/
17
+ # @see http://www.w3.org/TR/sparql11-results-csv-tsv/
18
+ class Client
19
+ autoload :Query, 'sparql/client/query'
20
+ autoload :Repository, 'sparql/client/repository'
21
+ autoload :Update, 'sparql/client/update'
22
+ autoload :VERSION, 'sparql/client/version'
23
+
24
+ class ClientError < StandardError; end
25
+ class MalformedQuery < ClientError; end
26
+ class ServerError < StandardError; end
27
+
28
+ RESULT_JSON = 'application/sparql-results+json'.freeze
29
+ RESULT_XML = 'application/sparql-results+xml'.freeze
30
+ RESULT_CSV = 'text/csv'.freeze
31
+ RESULT_TSV = 'text/tab-separated-values'.freeze
32
+ RESULT_BOOL = 'text/boolean'.freeze # Sesame-specific
33
+ RESULT_BRTR = 'application/x-binary-rdf-results-table'.freeze # Sesame-specific
34
+ RESULT_ALL = [
35
+ RESULT_JSON,
36
+ RESULT_XML,
37
+ RESULT_BOOL,
38
+ "#{RESULT_TSV};p=0.8",
39
+ "#{RESULT_CSV};p=0.2",
40
+ '*/*;p=0.1'
41
+ ].join(', ').freeze
42
+ GRAPH_ALL = (
43
+ RDF::Format.content_types.keys +
44
+ ['*/*;p=0.1']
45
+ ).join(', ').freeze
46
+
47
+ ACCEPT_JSON = {'Accept' => RESULT_JSON}.freeze
48
+ ACCEPT_XML = {'Accept' => RESULT_XML}.freeze
49
+ ACCEPT_CSV = {'Accept' => RESULT_CSV}.freeze
50
+ ACCEPT_TSV = {'Accept' => RESULT_TSV}.freeze
51
+ ACCEPT_BRTR = {'Accept' => RESULT_BRTR}.freeze
52
+ ACCEPT_RESULTS = {'Accept' => RESULT_ALL}.freeze
53
+ ACCEPT_GRAPH = {'Accept' => GRAPH_ALL}.freeze
54
+
55
+ DEFAULT_PROTOCOL = 1.0
56
+ DEFAULT_METHOD = :post
57
+
58
+ XMLNS = {'sparql' => 'http://www.w3.org/2005/sparql-results#'}.freeze
59
+
60
+ ##
61
+ # The SPARQL endpoint URL, or an RDF::Queryable instance, to use the native SPARQL engine.
62
+ #
63
+ # @return [RDF::URI, RDF::Queryable]
64
+ attr_reader :url
65
+
66
+ ##
67
+ # The HTTP headers that will be sent in requests to the endpoint.
68
+ #
69
+ # @return [Hash{String => String}]
70
+ attr_reader :headers
71
+
72
+ ##
73
+ # Any miscellaneous configuration.
74
+ #
75
+ # @return [Hash{Symbol => Object}]
76
+ attr_reader :options
77
+
78
+ ##
79
+ # Initialize a new sparql client, either using the URL of
80
+ # a SPARQL endpoint or an `RDF::Queryable` instance to use
81
+ # the native SPARQL gem.
82
+ #
83
+ # @param [String, RDF::Queryable, #to_s] url
84
+ # URL of endpoint, or queryable object.
85
+ # @param [Hash{Symbol => Object}] options
86
+ # @option options [Symbol] :method (DEFAULT_METHOD)
87
+ # @option options [Number] :protocol (DEFAULT_PROTOCOL)
88
+ # @option options [Hash] :headers
89
+ # @option options [Hash] :read_timeout
90
+ def initialize(url, options = {}, &block)
91
+ case url
92
+ when RDF::Queryable
93
+ @url, @options = url, options.dup
94
+ else
95
+ @url, @options = RDF::URI.new(url.to_s), options.dup
96
+ @headers = @options.delete(:headers) || {}
97
+ @http = http_klass(@url.scheme)
98
+ end
99
+
100
+ if block_given?
101
+ case block.arity
102
+ when 1 then block.call(self)
103
+ else instance_eval(&block)
104
+ end
105
+ end
106
+ end
107
+
108
+ ##
109
+ # Executes a boolean `ASK` query.
110
+ #
111
+ # @return [Query]
112
+ def ask(*args)
113
+ call_query_method(:ask, *args)
114
+ end
115
+
116
+ ##
117
+ # Executes a tuple `SELECT` query.
118
+ #
119
+ # @param [Array<Symbol>] args
120
+ # @return [Query]
121
+ def select(*args)
122
+ call_query_method(:select, *args)
123
+ end
124
+
125
+ ##
126
+ # Executes a `DESCRIBE` query.
127
+ #
128
+ # @param [Array<Symbol, RDF::URI>] args
129
+ # @return [Query]
130
+ def describe(*args)
131
+ call_query_method(:describe, *args)
132
+ end
133
+
134
+ ##
135
+ # Executes a graph `CONSTRUCT` query.
136
+ #
137
+ # @param [Array<Symbol>] args
138
+ # @return [Query]
139
+ def construct(*args)
140
+ call_query_method(:construct, *args)
141
+ end
142
+
143
+ ##
144
+ # Executes an `INSERT DATA` operation.
145
+ #
146
+ # This requires that the endpoint support SPARQL 1.1 Update.
147
+ #
148
+ # Note that for inserting non-trivial amounts of data, you probably
149
+ # ought to consider using the RDF store's native bulk-loading facilities
150
+ # or APIs, as `INSERT DATA` operations entail comparably higher
151
+ # parsing overhead.
152
+ #
153
+ # @example Inserting data constructed ad-hoc
154
+ # client.insert_data(RDF::Graph.new { |graph|
155
+ # graph << [:jhacker, RDF::FOAF.name, "J. Random Hacker"]
156
+ # })
157
+ #
158
+ # @example Inserting data sourced from a file or URL
159
+ # data = RDF::Graph.load("http://rdf.rubyforge.org/doap.nt")
160
+ # client.insert_data(data)
161
+ #
162
+ # @example Inserting data into a named graph
163
+ # client.insert_data(data, :graph => "http://example.org/")
164
+ #
165
+ # @param [RDF::Enumerable] data
166
+ # @param [Hash{Symbol => Object}] options
167
+ # @option options [RDF::URI, String] :graph
168
+ # @return [void] `self`
169
+ # @see http://www.w3.org/TR/sparql11-update/#insertData
170
+ def insert_data(data, options = {})
171
+ self.update(Update::InsertData.new(data, options))
172
+ end
173
+
174
+ ##
175
+ # Executes a `DELETE DATA` operation.
176
+ #
177
+ # This requires that the endpoint support SPARQL 1.1 Update.
178
+ #
179
+ # @example Deleting data sourced from a file or URL
180
+ # data = RDF::Graph.load("http://rdf.rubyforge.org/doap.nt")
181
+ # client.delete_data(data)
182
+ #
183
+ # @example Deleting data from a named graph
184
+ # client.delete_data(data, :graph => "http://example.org/")
185
+ #
186
+ # @param [RDF::Enumerable] data
187
+ # @param [Hash{Symbol => Object}] options
188
+ # @option options [RDF::URI, String] :graph
189
+ # @return [void] `self`
190
+ # @see http://www.w3.org/TR/sparql11-update/#deleteData
191
+ def delete_data(data, options = {})
192
+ self.update(Update::DeleteData.new(data, options))
193
+ end
194
+
195
+ ##
196
+ # Executes a `DELETE/INSERT` operation.
197
+ #
198
+ # This requires that the endpoint support SPARQL 1.1 Update.
199
+ #
200
+ # @param [RDF::Enumerable] delete_graph
201
+ # @param [RDF::Enumerable] insert_graph
202
+ # @param [RDF::Enumerable] where_graph
203
+ # @param [Hash{Symbol => Object}] options
204
+ # @option options [RDF::URI, String] :graph
205
+ # @return [void] `self`
206
+ # @see http://www.w3.org/TR/sparql11-update/#deleteInsert
207
+ def delete_insert(delete_graph, insert_graph = nil, where_graph = nil, options = {})
208
+ self.update(Update::DeleteInsert.new(delete_graph, insert_graph, where_graph, options))
209
+ end
210
+
211
+ ##
212
+ # Executes a `CLEAR GRAPH` operation.
213
+ #
214
+ # This is a convenience wrapper for the {#clear} method.
215
+ #
216
+ # @example `CLEAR GRAPH <http://example.org/>`
217
+ # client.clear_graph("http://example.org/")
218
+ #
219
+ # @param [RDF::URI, String] graph_uri
220
+ # @param [Hash{Symbol => Object}] options
221
+ # @option options [Boolean] :silent
222
+ # @return [void] `self`
223
+ # @see http://www.w3.org/TR/sparql11-update/#clear
224
+ def clear_graph(graph_uri, options = {})
225
+ self.clear(:graph, graph_uri, options)
226
+ end
227
+
228
+ ##
229
+ # Executes a `CLEAR` operation.
230
+ #
231
+ # This requires that the endpoint support SPARQL 1.1 Update.
232
+ #
233
+ # @example `CLEAR GRAPH <http://example.org/>`
234
+ # client.clear(:graph, RDF::URI("http://example.org/"))
235
+ #
236
+ # @example `CLEAR DEFAULT`
237
+ # client.clear(:default)
238
+ #
239
+ # @example `CLEAR NAMED`
240
+ # client.clear(:named)
241
+ #
242
+ # @example `CLEAR ALL`
243
+ # client.clear(:all)
244
+ #
245
+ # @overload clear(what, *arguments)
246
+ # @param [Symbol, #to_sym] what
247
+ # @param [Array] arguments splat of other arguments to {Update::Clear}.
248
+ # @option options [Boolean] :silent
249
+ # @return [void] `self`
250
+ #
251
+ # @overload clear(what, *arguments, options = {})
252
+ # @param [Symbol, #to_sym] what
253
+ # @param [Array] arguments splat of other arguments to {Update::Clear}.
254
+ # @param [Hash{Symbol => Object}] options
255
+ # @option options [Boolean] :silent
256
+ # @return [void] `self`
257
+ #
258
+ # @see http://www.w3.org/TR/sparql11-update/#clear
259
+ def clear(what, *arguments)
260
+ self.update(Update::Clear.new(what, *arguments))
261
+ end
262
+
263
+ ##
264
+ # @private
265
+ def call_query_method(meth, *args)
266
+ client = self
267
+ result = Query.send(meth, *args)
268
+ (class << result; self; end).send(:define_method, :execute) do
269
+ client.query(self)
270
+ end
271
+ result
272
+ end
273
+
274
+ ##
275
+ # Returns a mapping of blank node results for this client.
276
+ #
277
+ # @private
278
+ def nodes
279
+ @nodes ||= {}
280
+ end
281
+
282
+ ##
283
+ # Executes a SPARQL query and returns the parsed results.
284
+ #
285
+ # @param [String, #to_s] query
286
+ # @param [Hash{Symbol => Object}] options
287
+ # @option options [String] :content_type
288
+ # @option options [Hash] :headers
289
+ # @return [Array<RDF::Query::Solution>]
290
+ # @see http://www.w3.org/TR/sparql11-protocol/#query-operation
291
+ def query(query, options = {})
292
+ @op = :query
293
+ case @url
294
+ when RDF::Queryable
295
+ require 'sparql' unless defined?(::SPARQL::Grammar)
296
+ SPARQL.execute(query, @url, options)
297
+ else
298
+ parse_response(response(query, options), options)
299
+ end
300
+ end
301
+
302
+ ##
303
+ # Executes a SPARQL update operation.
304
+ #
305
+ # @param [String, #to_s] query
306
+ # @param [Hash{Symbol => Object}] options
307
+ # @option options [String] :endpoint
308
+ # @option options [String] :content_type
309
+ # @option options [Hash] :headers
310
+ # @return [void] `self`
311
+ # @see http://www.w3.org/TR/sparql11-protocol/#update-operation
312
+ def update(query, options = {})
313
+ @op = :update
314
+ @alt_endpoint = options[:endpoint] unless options[:endpoint].nil?
315
+ case @url
316
+ when RDF::Queryable
317
+ require 'sparql' unless defined?(::SPARQL::Grammar)
318
+ SPARQL.execute(query, @url, options)
319
+ else
320
+ parse_response(response(query, options), options)
321
+ end
322
+ self
323
+ end
324
+
325
+ ##
326
+ # Executes a SPARQL query and returns the Net::HTTP::Response of the
327
+ # result.
328
+ #
329
+ # @param [String, #to_s] query
330
+ # @param [Hash{Symbol => Object}] options
331
+ # @option options [String] :content_type
332
+ # @option options [Hash] :headers
333
+ # @return [String]
334
+ def response(query, options = {})
335
+ headers = options[:headers] || {}
336
+ headers['Accept'] = options[:content_type] if options[:content_type]
337
+ request(query, headers) do |response|
338
+ case response
339
+ when Net::HTTPBadRequest # 400 Bad Request
340
+ raise MalformedQuery.new(response.body + " Processing query #{query}")
341
+ when Net::HTTPClientError # 4xx
342
+ raise ClientError.new(response.body + " Processing query #{query}")
343
+ when Net::HTTPServerError # 5xx
344
+ raise ServerError.new(response.body + " Processing query #{query}")
345
+ when Net::HTTPSuccess # 2xx
346
+ response
347
+ end
348
+ end
349
+ end
350
+
351
+ ##
352
+ # @param [Net::HTTPSuccess] response
353
+ # @param [Hash{Symbol => Object}] options
354
+ # @return [Object]
355
+ def parse_response(response, options = {})
356
+ case options[:content_type] || response.content_type
357
+ when RESULT_BOOL # Sesame-specific
358
+ response.body == 'true'
359
+ when RESULT_JSON
360
+ self.class.parse_json_bindings(response.body, nodes)
361
+ when RESULT_XML
362
+ self.class.parse_xml_bindings(response.body, nodes)
363
+ when RESULT_CSV
364
+ self.class.parse_csv_bindings(response.body, nodes)
365
+ when RESULT_TSV
366
+ self.class.parse_tsv_bindings(response.body, nodes)
367
+ else
368
+ parse_rdf_serialization(response, options)
369
+ end
370
+ end
371
+
372
+ ##
373
+ # @param [String, Hash] json
374
+ # @return [<RDF::Query::Solutions>]
375
+ # @see http://www.w3.org/TR/rdf-sparql-json-res/#results
376
+ def self.parse_json_bindings(json, nodes = {})
377
+ require 'json' unless defined?(::JSON)
378
+ json = JSON.parse(json.to_s) unless json.is_a?(Hash)
379
+ case
380
+ when json.has_key?('boolean')
381
+ json['boolean']
382
+ when json.has_key?('results')
383
+ solutions = json['results']['bindings'].map do |row|
384
+ row = row.inject({}) do |cols, (name, value)|
385
+ cols.merge(name.to_sym => parse_json_value(value, nodes))
386
+ end
387
+ RDF::Query::Solution.new(row)
388
+ end
389
+ RDF::Query::Solutions.new(solutions)
390
+ end
391
+ end
392
+
393
+ ##
394
+ # @param [Hash{String => String}] value
395
+ # @return [RDF::Value]
396
+ # @see http://www.w3.org/TR/sparql11-results-json/#select-encode-terms
397
+ # @see http://www.w3.org/TR/rdf-sparql-json-res/#variable-binding-results
398
+ def self.parse_json_value(value, nodes = {})
399
+ case value['type'].to_sym
400
+ when :bnode
401
+ nodes[id = value['value']] ||= RDF::Node.new(id)
402
+ when :uri
403
+ RDF::URI.new(value['value'])
404
+ when :literal
405
+ RDF::Literal.new(value['value'], :datatype => value['datatype'], :language => value['xml:lang'])
406
+ when :'typed-literal'
407
+ RDF::Literal.new(value['value'], :datatype => value['datatype'])
408
+ else nil
409
+ end
410
+ end
411
+
412
+ ##
413
+ # @param [String, Array<Array<String>>] csv
414
+ # @return [<RDF::Query::Solutions>]
415
+ # @see http://www.w3.org/TR/sparql11-results-csv-tsv/
416
+ def self.parse_csv_bindings(csv, nodes = {})
417
+ require 'csv' unless defined?(::CSV)
418
+ csv = CSV.parse(csv.to_s) unless csv.is_a?(Array)
419
+ vars = csv.shift
420
+ solutions = RDF::Query::Solutions.new
421
+ csv.each do |row|
422
+ solution = RDF::Query::Solution.new
423
+ row.each_with_index do |v, i|
424
+ term = case v
425
+ when /^_:(.*)$/ then nodes[$1] ||= RDF::Node($1)
426
+ when /^\w+:.*$/ then RDF::URI(v)
427
+ else RDF::Literal(v)
428
+ end
429
+ solution[vars[i].to_sym] = term
430
+ end
431
+ solutions << solution
432
+ end
433
+ solutions
434
+ end
435
+
436
+ ##
437
+ # @param [String, Array<Array<String>>] tsv
438
+ # @return [<RDF::Query::Solutions>]
439
+ # @see http://www.w3.org/TR/sparql11-results-csv-tsv/
440
+ def self.parse_tsv_bindings(tsv, nodes = {})
441
+ tsv = tsv.lines.map {|l| l.chomp.split("\t")} unless tsv.is_a?(Array)
442
+ vars = tsv.shift.map {|h| h.sub(/^\?/, '')}
443
+ solutions = RDF::Query::Solutions.new
444
+ tsv.each do |row|
445
+ solution = RDF::Query::Solution.new
446
+ row.each_with_index do |v, i|
447
+ if !v.empty?
448
+ term = RDF::NTriples.unserialize(v) || case v
449
+ when /^\d+\.\d*[eE][+-]?[0-9]+$/ then RDF::Literal::Double.new(v)
450
+ when /^\d*\.\d+[eE][+-]?[0-9]+$/ then RDF::Literal::Double.new(v)
451
+ when /^\d*\.\d+$/ then RDF::Literal::Decimal.new(v)
452
+ when /^\d+$/ then RDF::Literal::Integer.new(v)
453
+ else
454
+ RDF::Literal(v)
455
+ end
456
+ nodes[term.id] = term if term.is_a? RDF::Node
457
+ solution[vars[i].to_sym] = term
458
+ end
459
+ end
460
+ solutions << solution
461
+ end
462
+ solutions
463
+ end
464
+
465
+ ##
466
+ # @param [String, IO, Nokogiri::XML::Node, REXML::Element] xml
467
+ # @return [<RDF::Query::Solutions>]
468
+ # @see http://www.w3.org/TR/rdf-sparql-json-res/#results
469
+ def self.parse_xml_bindings(xml, nodes = {})
470
+ xml.force_encoding(::Encoding::UTF_8) if xml.respond_to?(:force_encoding)
471
+
472
+ if defined?(::Nokogiri)
473
+ xml = Nokogiri::XML(xml).root unless xml.is_a?(Nokogiri::XML::Document)
474
+ case
475
+ when boolean = xml.xpath("//sparql:boolean", XMLNS)[0]
476
+ boolean.text == 'true'
477
+ when results = xml.xpath("//sparql:results", XMLNS)[0]
478
+ solutions = results.elements.map do |result|
479
+ row = {}
480
+ result.elements.each do |binding|
481
+ name = binding.attr('name').to_sym
482
+ value = binding.elements.first
483
+ row[name] = parse_xml_value(value, nodes)
484
+ end
485
+ RDF::Query::Solution.new(row)
486
+ end
487
+ RDF::Query::Solutions.new(solutions)
488
+ end
489
+ else
490
+ # REXML
491
+ xml = REXML::Document.new(xml).root unless xml.is_a?(REXML::Element)
492
+ case
493
+ when boolean = xml.elements['boolean']
494
+ boolean.text == 'true'
495
+ when results = xml.elements['results']
496
+ solutions = results.elements.map do |result|
497
+ row = {}
498
+ result.elements.each do |binding|
499
+ name = binding.attributes['name'].to_sym
500
+ value = binding.select { |node| node.kind_of?(::REXML::Element) }.first
501
+ row[name] = parse_xml_value(value, nodes)
502
+ end
503
+ RDF::Query::Solution.new(row)
504
+ end
505
+ RDF::Query::Solutions.new(solutions)
506
+ end
507
+ end
508
+ end
509
+
510
+ ##
511
+ # @param [Nokogiri::XML::Element, REXML::Element] value
512
+ # @return [RDF::Value]
513
+ # @see http://www.w3.org/TR/rdf-sparql-json-res/#variable-binding-results
514
+ def self.parse_xml_value(value, nodes = {})
515
+ case value.name.to_sym
516
+ when :bnode
517
+ nodes[id = value.text] ||= RDF::Node.new(id)
518
+ when :uri
519
+ RDF::URI.new(value.text)
520
+ when :literal
521
+ lang = value.respond_to?(:attr) ? value.attr('xml:lang') : value.attributes['xml:lang']
522
+ datatype = value.respond_to?(:attr) ? value.attr('datatype') : value.attributes['datatype']
523
+ RDF::Literal.new(value.text, :language => lang, :datatype => datatype)
524
+ else nil
525
+ end
526
+ end
527
+
528
+ ##
529
+ # @param [Net::HTTPSuccess] response
530
+ # @param [Hash{Symbol => Object}] options
531
+ # @return [RDF::Enumerable]
532
+ def parse_rdf_serialization(response, options = {})
533
+ options = {:content_type => response.content_type} if options.empty?
534
+ if reader = RDF::Reader.for(options)
535
+ reader.new(response.body)
536
+ end
537
+ end
538
+
539
+ ##
540
+ # Serializes a URI or URI string into SPARQL syntax.
541
+ #
542
+ # @param [RDF::URI, String] uri
543
+ # @return [String]
544
+ # @private
545
+ def self.serialize_uri(uri)
546
+ case uri
547
+ when String then RDF::NTriples.serialize(RDF::URI(uri))
548
+ when RDF::URI then RDF::NTriples.serialize(uri)
549
+ else raise ArgumentError, "expected the graph URI to be a String or RDF::URI, but got #{uri.inspect}"
550
+ end
551
+ end
552
+
553
+ ##
554
+ # Serializes an `RDF::Value` into SPARQL syntax.
555
+ #
556
+ # @param [RDF::Value] value
557
+ # @param [Boolean] use_vars (false) Use variables in place of BNodes
558
+ # @return [String]
559
+ # @private
560
+ def self.serialize_value(value, use_vars = false)
561
+ # SPARQL queries are UTF-8, but support ASCII-style Unicode escapes, so
562
+ # the N-Triples serializer is fine unless it's a variable:
563
+ case
564
+ when value.nil? then RDF::Query::Variable.new.to_s
565
+ when value.variable? then value.to_s
566
+ when value.node? then (use_vars ? RDF::Query::Variable.new(value.id) : value)
567
+ else RDF::NTriples.serialize(value)
568
+ end
569
+ end
570
+
571
+ ##
572
+ # Serializes a SPARQL predicate
573
+ #
574
+ # @param [RDF::Value, Array, String] value
575
+ # @param [Fixnum] rdepth
576
+ # @return [String]
577
+ # @private
578
+ def self.serialize_predicate(value,rdepth=0)
579
+ case value
580
+ when nil
581
+ RDF::Query::Variable.new.to_s
582
+ when String then value
583
+ when Array
584
+ s = value.map{|v|serialize_predicate(v,rdepth+1)}.join
585
+ rdepth > 0 ? "(#{s})" : s
586
+ when RDF::Value
587
+ # abbreviate RDF.type in the predicate position per SPARQL grammar
588
+ value.equal?(RDF.type) ? 'a' : serialize_value(value)
589
+ end
590
+ end
591
+
592
+ ##
593
+ # Serializes a SPARQL graph
594
+ #
595
+ # @param [RDF::Enumerable] patterns
596
+ # @param [Boolean] use_vars (false) Use variables in place of BNodes
597
+ # @return [String]
598
+ # @private
599
+ def self.serialize_patterns(patterns, use_vars = false)
600
+ patterns.map do |pattern|
601
+ serialized_pattern = RDF::Statement.from(pattern).to_triple.each_with_index.map do |v, i|
602
+ if i == 1
603
+ SPARQL::Client.serialize_predicate(v)
604
+ else
605
+ SPARQL::Client.serialize_value(v, use_vars)
606
+ end
607
+ end
608
+ serialized_pattern.join(' ') + ' .'
609
+ end
610
+ end
611
+
612
+ ##
613
+ # Outputs a developer-friendly representation of this object to `stderr`.
614
+ #
615
+ # @return [void]
616
+ def inspect!
617
+ warn(inspect)
618
+ end
619
+
620
+ ##
621
+ # Returns a developer-friendly representation of this object.
622
+ #
623
+ # @return [String]
624
+ def inspect
625
+ sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, url.to_s)
626
+ end
627
+
628
+ protected
629
+
630
+ ##
631
+ # Returns an HTTP class or HTTP proxy class based on the `http_proxy`
632
+ # and `https_proxy` environment variables.
633
+ #
634
+ # @param [String] scheme
635
+ # @return [Net::HTTP::Proxy]
636
+ def http_klass(scheme)
637
+ proxy_url = nil
638
+ case scheme
639
+ when 'http'
640
+ value = ENV['http_proxy']
641
+ proxy_url = URI.parse(value) unless value.nil? || value.empty?
642
+ when 'https'
643
+ value = ENV['https_proxy']
644
+ proxy_url = URI.parse(value) unless value.nil? || value.empty?
645
+ end
646
+ klass = Net::HTTP::Persistent.new(self.class.to_s, proxy_url)
647
+ klass.keep_alive = 120 # increase to 2 minutes
648
+ klass.read_timeout = @options[:read_timeout] || 60
649
+ klass
650
+ end
651
+
652
+ ##
653
+ # Performs an HTTP request against the SPARQL endpoint.
654
+ #
655
+ # @param [String, #to_s] query
656
+ # @param [Hash{String => String}] headers
657
+ # @yield [response]
658
+ # @yieldparam [Net::HTTPResponse] response
659
+ # @return [Net::HTTPResponse]
660
+ # @see http://www.w3.org/TR/sparql11-protocol/#query-operation
661
+ def request(query, headers = {}, &block)
662
+ method = (self.options[:method] || DEFAULT_METHOD).to_sym
663
+
664
+ # Make sure an appropriate Accept header is present
665
+ headers['Accept'] ||= if (query.respond_to?(:expects_statements?) ?
666
+ query.expects_statements? :
667
+ (query =~ /CONSTRUCT|DESCRIBE|DELETE|CLEAR/))
668
+ GRAPH_ALL
669
+ else
670
+ RESULT_ALL
671
+ end
672
+
673
+ request = send("make_#{method}_request", query, headers)
674
+
675
+ request.basic_auth(url.user, url.password) if url.user && !url.user.empty?
676
+
677
+ response = @http.request(url, request)
678
+
679
+ 10.times do
680
+ if response.kind_of? Net::HTTPRedirection
681
+ response = @http.request(RDF::URI(response['location']), request)
682
+ else
683
+ return block_given? ? block.call(response) : response
684
+ end
685
+ end
686
+ raise ServerError, "Infinite redirect at #{url}. Redirected more than 10 times."
687
+ end
688
+
689
+ ##
690
+ # Constructs an HTTP GET request according to the SPARQL Protocol.
691
+ #
692
+ # @param [String, #to_s] query
693
+ # @param [Hash{String => String}] headers
694
+ # @return [Net::HTTPRequest]
695
+ # @see http://www.w3.org/TR/sparql11-protocol/#query-via-get
696
+ def make_get_request(query, headers = {})
697
+ url = self.url.dup
698
+ url.query_values = (url.query_values || {}).merge(:query => query.to_s)
699
+ request = Net::HTTP::Get.new(url.request_uri, self.headers.merge(headers))
700
+ request
701
+ end
702
+
703
+ ##
704
+ # Constructs an HTTP POST request according to the SPARQL Protocol.
705
+ #
706
+ # @param [String, #to_s] query
707
+ # @param [Hash{String => String}] headers
708
+ # @return [Net::HTTPRequest]
709
+ # @see http://www.w3.org/TR/sparql11-protocol/#query-via-post-direct
710
+ # @see http://www.w3.org/TR/sparql11-protocol/#query-via-post-urlencoded
711
+ def make_post_request(query, headers = {})
712
+ if @alt_endpoint.nil?
713
+ endpoint = url.request_uri
714
+ else
715
+ endpoint = @alt_endpoint
716
+ end
717
+ request = Net::HTTP::Post.new(endpoint, self.headers.merge(headers))
718
+ case (self.options[:protocol] || DEFAULT_PROTOCOL).to_s
719
+ when '1.1'
720
+ request['Content-Type'] = 'application/sparql-' + (@op || :query).to_s
721
+ request.body = query.to_s
722
+ when '1.0'
723
+ request.set_form_data((@op || :query) => query.to_s)
724
+ else
725
+ raise ArgumentError, "unknown SPARQL protocol version: #{self.options[:protocol].inspect}"
726
+ end
727
+ request
728
+ end
729
+ end # Client
730
+ end # SPARQL