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

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.
@@ -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