sparql-client 0.3.2 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
data/AUTHORS CHANGED
@@ -1,2 +1,3 @@
1
- * Arto Bendiken <arto.bendiken@gmail.com>
1
+ * Arto Bendiken <arto@bendiken.net>
2
2
  * Ben Lavender <blavender@gmail.com>
3
+ * Gregg Kellogg <gregg@greggkellogg.net>
data/CREDITS CHANGED
@@ -5,5 +5,5 @@
5
5
  * Fumihiro Kato <fumi@fumi.me>
6
6
  * David Nielsen <david@graveyard.dk>
7
7
  * Thamaraiselvan Poomalai <p.thamarai@gmail.com>
8
- * Gregg Kellogg <gregg@greggkellogg.net>
9
8
  * Michael Sokol <mikaa123@gmail.com>
9
+ * Yves Raimond <yves.raimond@bbc.co.uk>
data/README CHANGED
@@ -7,11 +7,12 @@ This is a [Ruby][] implementation of a [SPARQL][] client for [RDF.rb][].
7
7
 
8
8
  ##Features
9
9
 
10
- * Executes queries against any SPARQL 1.0-compatible endpoints over HTTP.
10
+ * Executes queries against any SPARQL 1.0-compatible endpoint over HTTP.
11
11
  * Provides a query builder [DSL][] for `ASK`, `SELECT`, `DESCRIBE` and
12
12
  `CONSTRUCT` queries.
13
+ * Includes preliminary support for some SPARQL 1.1 Update operations.
13
14
  * Supports tuple result sets in both XML and JSON formats, with JSON being
14
- the preferred default for content negotiation purposes.
15
+ the preferred default for content-negotiation purposes.
15
16
  * Supports graph results in any RDF serialization format understood by RDF.rb.
16
17
  * Returns results using the [RDF.rb object model][RDF.rb model].
17
18
  * Supports accessing endpoints as read-only [`RDF::Repository`][RDF::Repository]
@@ -56,8 +57,6 @@ This is a [Ruby][] implementation of a [SPARQL][] client for [RDF.rb][].
56
57
 
57
58
  ##Documentation
58
59
 
59
- <http://sparql.rubyforge.org/client/>
60
-
61
60
  * {SPARQL::Client}
62
61
  * {SPARQL::Client::Query}
63
62
  * {SPARQL::Client::Repository}
@@ -65,8 +64,9 @@ This is a [Ruby][] implementation of a [SPARQL][] client for [RDF.rb][].
65
64
  ##Dependencies
66
65
 
67
66
  * [Ruby](http://ruby-lang.org/) (>= 1.8.7) or (>= 1.8.1 with [Backports][])
68
- * [RDF.rb](http://rubygems.org/gems/rdf) (>= 0.3.0)
69
- * [JSON](http://rubygems.org/gems/json_pure) (>= 1.4.2)
67
+ * [RDF.rb](http://rubygems.org/gems/rdf) (>= 0.3.5)
68
+ * [Net::HTTP::Persistent](http://rubygems.org/gems/net-http-persistent) (>= 1.4.1)
69
+ * [JSON](http://rubygems.org/gems/json_pure) (>= 1.4.6)
70
70
 
71
71
  ##Installation
72
72
 
@@ -79,12 +79,12 @@ To install the latest official release of the `SPARQL::Client` gem, do:
79
79
 
80
80
  To get a local working copy of the development repository, do:
81
81
 
82
- % git clone git://github.com/bendiken/sparql-client.git
82
+ % git clone git://github.com/ruby-rdf/sparql-client.git
83
83
 
84
84
  Alternatively, download the latest development version as a tarball as
85
85
  follows:
86
86
 
87
- % wget http://github.com/bendiken/sparql-client/tarball/master
87
+ % wget http://github.com/ruby-rdf/sparql-client/tarball/master
88
88
 
89
89
  ##Mailing List
90
90
 
@@ -94,6 +94,7 @@ follows:
94
94
 
95
95
  * [Arto Bendiken](http://github.com/bendiken) - <http://ar.to/>
96
96
  * [Ben Lavender](http://github.com/bhuga) - <http://bhuga.net/>
97
+ * [Gregg Kellogg](http://github.com/gkellogg) - <http://kellogg-assoc.com/>
97
98
 
98
99
  ##Contributors
99
100
 
@@ -104,7 +105,6 @@ follows:
104
105
  * [Fumihiro Kato](http://github.com/fumi) - <http://fumi.me/>
105
106
  * [David Nielsen](http://github.com/drankard) - <http://github.com/drankard>
106
107
  * [Thamaraiselvan Poomalai](http://github.com/selvan) - <http://softonaut.blogspot.com/>
107
- * [Gregg Kellogg](http://github.com/gkellogg) - <http://kellogg-assoc.com/>
108
108
  * [Michael Sokol](http://github.com/mikaa123) - <http://sokolmichael.com/>
109
109
  * [Yves Raimond](http://github.com/moustaki) - <http://moustaki.org/>
110
110
 
@@ -124,8 +124,8 @@ follows:
124
124
 
125
125
  ##Resources
126
126
 
127
- * <http://sparql.rubyforge.org/client/>
128
- * <http://github.com/bendiken/sparql-client>
127
+ * <http://ruby-rdf.github.com/sparql-client/>
128
+ * <http://github.com/ruby-rdf/sparql-client>
129
129
  * <http://rubygems.org/gems/sparql-client>
130
130
  * <http://rubyforge.org/projects/sparql/>
131
131
  * <http://raa.ruby-lang.org/project/sparql-client/>
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.2
1
+ 0.3.3
data/lib/sparql/client.rb CHANGED
@@ -1,13 +1,13 @@
1
- require 'net/http/persistent'
2
- require 'rdf' # @see http://rubygems.org/gems/rdf
3
- require 'rdf/ntriples'
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
4
 
5
5
  module SPARQL
6
6
  ##
7
7
  # A SPARQL client for RDF.rb.
8
8
  #
9
- # @see http://www.w3.org/TR/rdf-sparql-protocol/
10
- # @see http://www.w3.org/TR/rdf-sparql-json-res/
9
+ # @see http://www.w3.org/TR/sparql11-query/
10
+ # @see http://www.w3.org/TR/sparql11-results-json/
11
11
  class Client
12
12
  autoload :Query, 'sparql/client/query'
13
13
  autoload :Repository, 'sparql/client/repository'
@@ -17,25 +17,33 @@ module SPARQL
17
17
  class MalformedQuery < ClientError; end
18
18
  class ServerError < StandardError; end
19
19
 
20
- RESULT_BOOL = 'text/boolean'.freeze # Sesame-specific
21
20
  RESULT_JSON = 'application/sparql-results+json'.freeze
22
21
  RESULT_XML = 'application/sparql-results+xml'.freeze
22
+ RESULT_BOOL = 'text/boolean'.freeze # Sesame-specific
23
+ RESULT_BRTR = 'application/x-binary-rdf-results-table'.freeze # Sesame-specific
23
24
  ACCEPT_JSON = {'Accept' => RESULT_JSON}.freeze
24
25
  ACCEPT_XML = {'Accept' => RESULT_XML}.freeze
26
+ ACCEPT_BRTR = {'Accept' => RESULT_BRTR}.freeze
25
27
 
28
+ # @return [RDF::URI]
26
29
  attr_reader :url
30
+
31
+ # @return [Hash{String => String}]
32
+ attr_reader :headers
33
+
34
+ # @return [Hash{Symbol => Object}]
27
35
  attr_reader :options
28
36
 
29
37
  ##
30
38
  # @param [String, #to_s] url
31
39
  # @param [Hash{Symbol => Object}] options
40
+ # @option options [Symbol] :method (:post)
32
41
  # @option options [Hash] :headers
33
42
  def initialize(url, options = {}, &block)
34
- @url, @options = RDF::URI.new(url.to_s), options
35
- #@headers = {'Accept' => "#{RESULT_JSON}, #{RESULT_XML}, text/plain"}
43
+ @url, @options = RDF::URI.new(url.to_s), options.dup
36
44
  @headers = {
37
45
  'Accept' => [RESULT_JSON, RESULT_XML, RDF::Format.content_types.keys.map(&:to_s)].join(', ')
38
- }.merge @options[:headers] || {}
46
+ }.merge(@options.delete(:headers) || {})
39
47
  @http = http_klass(@url.scheme)
40
48
 
41
49
  if block_given?
@@ -81,6 +89,128 @@ module SPARQL
81
89
  call_query_method(:construct, *args)
82
90
  end
83
91
 
92
+ ##
93
+ # Executes an `INSERT DATA` operation.
94
+ #
95
+ # This requires that the endpoint support SPARQL 1.1 Update.
96
+ #
97
+ # Note that for inserting non-trivial amounts of data, you probably
98
+ # ought to consider using the RDF store's native bulk-loading facilities
99
+ # or APIs, as `INSERT DATA` operations entail comparably higher
100
+ # parsing overhead.
101
+ #
102
+ # @example Inserting data constructed ad-hoc
103
+ # client.insert_data(RDF::Graph.new { |graph|
104
+ # graph << [:jhacker, RDF::FOAF.name, "J. Random Hacker"]
105
+ # })
106
+ #
107
+ # @example Inserting data sourced from a file or URL
108
+ # data = RDF::Graph.load("http://rdf.rubyforge.org/doap.nt")
109
+ # client.insert_data(data)
110
+ #
111
+ # @example Inserting data into a named graph
112
+ # client.insert_data(data, :graph => "http://example.org/")
113
+ #
114
+ # @param [RDF::Graph] data
115
+ # @param [Hash{Symbol => Object}] options
116
+ # @option options [RDF::URI, String] :graph
117
+ # @return [void] `self`
118
+ # @see http://www.w3.org/TR/sparql11-update/#insertData
119
+ def insert_data(data, options = {})
120
+ raise ArgumentError, "no data given" if data.empty?
121
+ query_text = 'INSERT DATA {'
122
+ query_text += ' GRAPH ' + self.class.serialize_uri(options[:graph]) + ' {' if options[:graph]
123
+ query_text += "\n"
124
+ query_text += RDF::NTriples::Writer.buffer { |writer| writer << data }
125
+ query_text += '}' if options[:graph]
126
+ query_text += "}\n"
127
+ query(query_text)
128
+ self
129
+ end
130
+
131
+ ##
132
+ # Executes a `DELETE DATA` operation.
133
+ #
134
+ # This requires that the endpoint support SPARQL 1.1 Update.
135
+ #
136
+ # @example Deleting data sourced from a file or URL
137
+ # data = RDF::Graph.load("http://rdf.rubyforge.org/doap.nt")
138
+ # client.delete_data(data)
139
+ #
140
+ # @example Deleting data from a named graph
141
+ # client.delete_data(data, :graph => "http://example.org/")
142
+ #
143
+ # @param [RDF::Graph] data
144
+ # @param [Hash{Symbol => Object}] options
145
+ # @option options [RDF::URI, String] :graph
146
+ # @return [void] `self`
147
+ # @see http://www.w3.org/TR/sparql11-update/#deleteData
148
+ def delete_data(data, options = {})
149
+ raise ArgumentError, "no data given" if data.empty?
150
+ query_text = 'DELETE DATA {'
151
+ query_text += ' GRAPH ' + self.class.serialize_uri(options[:graph]) + ' {' if options[:graph]
152
+ query_text += "\n"
153
+ query_text += RDF::NTriples::Writer.buffer { |writer| writer << data }
154
+ query_text += '}' if options[:graph]
155
+ query_text += "}\n"
156
+ query(query_text)
157
+ self
158
+ end
159
+
160
+ ##
161
+ # Executes a `CLEAR GRAPH` operation.
162
+ #
163
+ # This is a convenience wrapper for the {#clear} method.
164
+ #
165
+ # @example `CLEAR GRAPH <http://example.org/>`
166
+ # client.clear_graph("http://example.org/")
167
+ #
168
+ # @param [RDF::URI, String] graph_uri
169
+ # @param [Hash{Symbol => Object}] options
170
+ # @option options [Boolean] :silent
171
+ # @return [void] `self`
172
+ # @see http://www.w3.org/TR/sparql11-update/#clear
173
+ def clear_graph(graph_uri, options = {})
174
+ self.clear(:graph, graph_uri, options)
175
+ end
176
+
177
+ ##
178
+ # Executes a `CLEAR` operation.
179
+ #
180
+ # This requires that the endpoint support SPARQL 1.1 Update.
181
+ #
182
+ # @example `CLEAR GRAPH <http://example.org/>`
183
+ # client.clear(:graph, RDF::URI("http://example.org/"))
184
+ #
185
+ # @example `CLEAR DEFAULT`
186
+ # client.clear(:default)
187
+ #
188
+ # @example `CLEAR NAMED`
189
+ # client.clear(:named)
190
+ #
191
+ # @example `CLEAR ALL`
192
+ # client.clear(:all)
193
+ #
194
+ # @param [Symbol, #to_sym] what
195
+ # @param [Hash{Symbol => Object}] options
196
+ # @option options [Boolean] :silent
197
+ # @return [void] `self`
198
+ # @see http://www.w3.org/TR/sparql11-update/#clear
199
+ def clear(what, *arguments)
200
+ options = arguments.last.is_a?(Hash) ? arguments.pop : {}
201
+ query_text = 'CLEAR '
202
+ query_text += 'SILENT ' if options[:silent]
203
+ case what.to_sym
204
+ when :graph then query_text += 'GRAPH ' + self.class.serialize_uri(arguments.pop)
205
+ when :default then query_text += 'DEFAULT'
206
+ when :named then query_text += 'NAMED'
207
+ when :all then query_text += 'ALL'
208
+ else raise ArgumentError, "invalid CLEAR operation: #{what.inspect}"
209
+ end
210
+ query(query_text)
211
+ self
212
+ end
213
+
84
214
  ##
85
215
  # @private
86
216
  def call_query_method(meth, *args)
@@ -93,7 +223,8 @@ module SPARQL
93
223
  end
94
224
 
95
225
  ##
96
- # A mapping of blank node results for this client
226
+ # Returns a mapping of blank node results for this client.
227
+ #
97
228
  # @private
98
229
  def nodes
99
230
  @nodes ||= {}
@@ -112,7 +243,8 @@ module SPARQL
112
243
  end
113
244
 
114
245
  ##
115
- # Executes a SPARQL query and returns the Net::HTTP::Response of the result.
246
+ # Executes a SPARQL query and returns the Net::HTTP::Response of the
247
+ # result.
116
248
  #
117
249
  # @param [String, #to_s] query
118
250
  # @param [Hash{Symbol => Object}] options
@@ -120,8 +252,9 @@ module SPARQL
120
252
  # @option options [Hash] :headers
121
253
  # @return [String]
122
254
  def response(query, options = {})
123
- @headers['Accept'] = options[:content_type] if options[:content_type]
124
- get(query, options[:headers] || {}) do |response|
255
+ headers = options[:headers] || {}
256
+ headers['Accept'] = options[:content_type] if options[:content_type]
257
+ request(query, headers) do |response|
125
258
  case response
126
259
  when Net::HTTPBadRequest # 400 Bad Request
127
260
  raise MalformedQuery.new(response.body)
@@ -133,7 +266,6 @@ module SPARQL
133
266
  response
134
267
  end
135
268
  end
136
-
137
269
  end
138
270
 
139
271
  ##
@@ -160,7 +292,6 @@ module SPARQL
160
292
  def self.parse_json_bindings(json, nodes = {})
161
293
  require 'json' unless defined?(::JSON)
162
294
  json = JSON.parse(json.to_s) unless json.is_a?(Hash)
163
-
164
295
  case
165
296
  when json['boolean']
166
297
  json['boolean']
@@ -249,6 +380,35 @@ module SPARQL
249
380
  end
250
381
  end
251
382
 
383
+ ##
384
+ # Serializes a URI or URI string into SPARQL syntax.
385
+ #
386
+ # @param [RDF::URI, String] uri
387
+ # @return [String]
388
+ # @private
389
+ def self.serialize_uri(uri)
390
+ case uri
391
+ when String then RDF::NTriples.serialize(RDF::URI(uri))
392
+ when RDF::URI then RDF::NTriples.serialize(uri)
393
+ else raise ArgumentError, "expected the graph URI to be a String or RDF::URI, but got #{uri.inspect}"
394
+ end
395
+ end
396
+
397
+ ##
398
+ # Serializes an `RDF::Value` into SPARQL syntax.
399
+ #
400
+ # @param [RDF::Value] value
401
+ # @return [String]
402
+ # @private
403
+ def self.serialize_value(value)
404
+ # SPARQL queries are UTF-8, but support ASCII-style Unicode escapes, so
405
+ # the N-Triples serializer is fine unless it's a variable:
406
+ case
407
+ when value.variable? then value.to_s
408
+ else RDF::NTriples.serialize(value)
409
+ end
410
+ end
411
+
252
412
  ##
253
413
  # Outputs a developer-friendly representation of this object to `stderr`.
254
414
  #
@@ -268,41 +428,75 @@ module SPARQL
268
428
  protected
269
429
 
270
430
  ##
271
- # Returns an HTTP class or HTTP proxy class based on environment http_proxy & https_proxy settings
431
+ # Returns an HTTP class or HTTP proxy class based on the `http_proxy`
432
+ # and `https_proxy` environment variables.
433
+ #
434
+ # @param [String] scheme
272
435
  # @return [Net::HTTP::Proxy]
273
436
  def http_klass(scheme)
274
- proxy_uri = nil
437
+ proxy_url = nil
275
438
  case scheme
276
- when "http"
277
- proxy_uri = URI.parse(ENV['http_proxy']) unless ENV['http_proxy'].nil?
278
- when "https"
279
- proxy_uri = URI.parse(ENV['https_proxy']) unless ENV['https_proxy'].nil?
439
+ when 'http'
440
+ value = ENV['http_proxy']
441
+ proxy_url = URI.parse(value) unless value.nil? || value.empty?
442
+ when 'https'
443
+ value = ENV['https_proxy']
444
+ proxy_url = URI.parse(value) unless value.nil? || value.empty?
280
445
  end
281
- klass = Net::HTTP::Persistent.new(self.class.to_s, proxy_uri)
282
- klass.keep_alive = 120 # increase to 2 minutes
446
+ klass = Net::HTTP::Persistent.new(self.class.to_s, proxy_url)
447
+ klass.keep_alive = 120 # increase to 2 minutes
283
448
  klass
284
449
  end
285
450
 
286
451
  ##
287
- # Performs an HTTP GET request against the SPARQL endpoint.
452
+ # Performs an HTTP request against the SPARQL endpoint.
288
453
  #
289
454
  # @param [String, #to_s] query
290
455
  # @param [Hash{String => String}] headers
291
456
  # @yield [response]
292
457
  # @yieldparam [Net::HTTPResponse] response
293
458
  # @return [Net::HTTPResponse]
294
- def get(query, headers = {}, &block)
295
- url = self.url.dup
296
- url.query_values = (url.query_values || {}).merge(:query => query.to_s)
459
+ # @see http://www.w3.org/TR/sparql11-protocol/#query-operation
460
+ def request(query, headers = {}, &block)
461
+ method = (self.options[:method] || :post).to_sym
462
+ request = send("make_#{method}_request", query, headers)
297
463
 
298
- request = Net::HTTP::Get.new(url.request_uri, @headers.merge(headers))
299
- request.basic_auth url.user, url.password if url.user && !url.user.empty?
300
- response = @http.request url, request
464
+ request.basic_auth(url.user, url.password) if url.user && !url.user.empty?
465
+
466
+ response = @http.request(url, request)
301
467
  if block_given?
302
- block.call(response)
468
+ block.call(response)
303
469
  else
304
- response
470
+ response
305
471
  end
306
472
  end
473
+
474
+ ##
475
+ # Constructs an HTTP GET request according to the SPARQL Protocol.
476
+ #
477
+ # @param [String, #to_s] query
478
+ # @param [Hash{String => String}] headers
479
+ # @return [Net::HTTPRequest]
480
+ # @see http://www.w3.org/TR/sparql11-protocol/#query-via-get
481
+ def make_get_request(query, headers = {})
482
+ url = self.url.dup
483
+ url.query_values = (url.query_values || {}).merge(:query => query.to_s)
484
+ request = Net::HTTP::Get.new(url.request_uri, self.headers.merge(headers))
485
+ request
486
+ end
487
+
488
+ ##
489
+ # Constructs an HTTP POST request according to the SPARQL Protocol.
490
+ #
491
+ # @param [String, #to_s] query
492
+ # @param [Hash{String => String}] headers
493
+ # @return [Net::HTTPRequest]
494
+ # @see http://www.w3.org/TR/sparql11-protocol/#query-via-post-urlencoded
495
+ def make_post_request(query, headers = {})
496
+ url = self.url
497
+ request = Net::HTTP::Post.new(url.request_uri, self.headers.merge(headers))
498
+ request.set_form_data(:query => query.to_s)
499
+ request
500
+ end
307
501
  end # Client
308
502
  end # SPARQL
@@ -8,7 +8,7 @@ module SPARQL; class Client
8
8
  class Query < RDF::Query
9
9
  ##
10
10
  # @return [Symbol]
11
- # @see http://www.w3.org/TR/rdf-sparql-query/#QueryForms
11
+ # @see http://www.w3.org/TR/sparql11-query/#QueryForms
12
12
  attr_reader :form
13
13
 
14
14
  ##
@@ -24,7 +24,7 @@ module SPARQL; class Client
24
24
  #
25
25
  # @param [Hash{Symbol => Object}] options
26
26
  # @return [Query]
27
- # @see http://www.w3.org/TR/rdf-sparql-query/#ask
27
+ # @see http://www.w3.org/TR/sparql11-query/#ask
28
28
  def self.ask(options = {})
29
29
  self.new(:ask, options)
30
30
  end
@@ -38,7 +38,7 @@ module SPARQL; class Client
38
38
  # @overload self.select(*variables, options)
39
39
  # @param [Array<Symbol>] variables
40
40
  # @return [Query]
41
- # @see http://www.w3.org/TR/rdf-sparql-query/#select
41
+ # @see http://www.w3.org/TR/sparql11-query/#select
42
42
  def self.select(*variables)
43
43
  options = variables.last.is_a?(Hash) ? variables.pop : {}
44
44
  self.new(:select, options).select(*variables)
@@ -53,7 +53,7 @@ module SPARQL; class Client
53
53
  # @overload self.describe(*variables, options)
54
54
  # @param [Array<Symbol, RDF::URI>] variables
55
55
  # @return [Query]
56
- # @see http://www.w3.org/TR/rdf-sparql-query/#describe
56
+ # @see http://www.w3.org/TR/sparql11-query/#describe
57
57
  def self.describe(*variables)
58
58
  options = variables.last.is_a?(Hash) ? variables.pop : {}
59
59
  self.new(:describe, options).describe(*variables)
@@ -69,7 +69,7 @@ module SPARQL; class Client
69
69
  # @param [Array<RDF::Query::Pattern, Array>] patterns
70
70
  # @param [Hash{Symbol => Object}] options
71
71
  # @return [Query]
72
- # @see http://www.w3.org/TR/rdf-sparql-query/#construct
72
+ # @see http://www.w3.org/TR/sparql11-query/#construct
73
73
  def self.construct(*patterns)
74
74
  options = patterns.last.is_a?(Hash) ? patterns.pop : {}
75
75
  self.new(:construct, options).construct(*patterns) # FIXME
@@ -89,7 +89,7 @@ module SPARQL; class Client
89
89
 
90
90
  ##
91
91
  # @return [Query]
92
- # @see http://www.w3.org/TR/rdf-sparql-query/#ask
92
+ # @see http://www.w3.org/TR/sparql11-query/#ask
93
93
  def ask
94
94
  @form = :ask
95
95
  self
@@ -98,7 +98,7 @@ module SPARQL; class Client
98
98
  ##
99
99
  # @param [Array<Symbol>] variables
100
100
  # @return [Query]
101
- # @see http://www.w3.org/TR/rdf-sparql-query/#select
101
+ # @see http://www.w3.org/TR/sparql11-query/#select
102
102
  def select(*variables)
103
103
  @values = variables.map { |var| [var, RDF::Query::Variable.new(var)] }
104
104
  self
@@ -107,7 +107,7 @@ module SPARQL; class Client
107
107
  ##
108
108
  # @param [Array<Symbol>] variables
109
109
  # @return [Query]
110
- # @see http://www.w3.org/TR/rdf-sparql-query/#describe
110
+ # @see http://www.w3.org/TR/sparql11-query/#describe
111
111
  def describe(*variables)
112
112
  @values = variables.map { |var|
113
113
  [var, var.is_a?(RDF::URI) ? var : RDF::Query::Variable.new(var)]
@@ -118,6 +118,7 @@ module SPARQL; class Client
118
118
  ##
119
119
  # @param [Array<RDF::Query::Pattern, Array>] patterns
120
120
  # @return [Query]
121
+ # @see http://www.w3.org/TR/sparql11-query/#construct
121
122
  def construct(*patterns)
122
123
  options[:template] = build_patterns(patterns)
123
124
  self
@@ -125,7 +126,7 @@ module SPARQL; class Client
125
126
 
126
127
  # @param [RDF::URI] uri
127
128
  # @return [Query]
128
- # @see http://www.w3.org/TR/rdf-sparql-query/#specDataset
129
+ # @see http://www.w3.org/TR/sparql11-query/#specifyingDataset
129
130
  def from(uri)
130
131
  options[:from] = uri
131
132
  self
@@ -134,7 +135,7 @@ module SPARQL; class Client
134
135
  ##
135
136
  # @param [Array<RDF::Query::Pattern, Array>] patterns
136
137
  # @return [Query]
137
- # @see http://www.w3.org/TR/rdf-sparql-query/#GraphPattern
138
+ # @see http://www.w3.org/TR/sparql11-query/#GraphPattern
138
139
  def where(*patterns)
139
140
  @patterns += build_patterns(patterns)
140
141
  self
@@ -145,7 +146,7 @@ module SPARQL; class Client
145
146
  ##
146
147
  # @param [Array<Symbol, String>] variables
147
148
  # @return [Query]
148
- # @see http://www.w3.org/TR/rdf-sparql-query/#modOrderBy
149
+ # @see http://www.w3.org/TR/sparql11-query/#modOrderBy
149
150
  def order(*variables)
150
151
  options[:order_by] = variables
151
152
  self
@@ -166,7 +167,7 @@ module SPARQL; class Client
166
167
 
167
168
  ##
168
169
  # @return [Query]
169
- # @see http://www.w3.org/TR/rdf-sparql-query/#modDistinct
170
+ # @see http://www.w3.org/TR/sparql11-query/#modDuplicates
170
171
  def distinct(state = true)
171
172
  options[:distinct] = state
172
173
  self
@@ -174,16 +175,30 @@ module SPARQL; class Client
174
175
 
175
176
  ##
176
177
  # @return [Query]
177
- # @see http://www.w3.org/TR/rdf-sparql-query/#modReduced
178
+ # @see http://www.w3.org/TR/sparql11-query/#modDuplicates
178
179
  def reduced(state = true)
179
180
  options[:reduced] = state
180
181
  self
181
182
  end
182
183
 
184
+ ##
185
+ # @param [RDF::Value] graph_uri_or_var
186
+ # @return [Query]
187
+ # @see http://www.w3.org/TR/sparql11-query/#queryDataset
188
+ def graph(graph_uri_or_var)
189
+ options[:graph] = case graph_uri_or_var
190
+ when Symbol then RDF::Query::Variable.new(graph_uri_or_var)
191
+ when String then RDF::URI(graph_uri_or_var)
192
+ when RDF::Value then graph_uri_or_var
193
+ else raise ArgumentError
194
+ end
195
+ self
196
+ end
197
+
183
198
  ##
184
199
  # @param [Integer, #to_i] start
185
200
  # @return [Query]
186
- # @see http://www.w3.org/TR/rdf-sparql-query/#modOffset
201
+ # @see http://www.w3.org/TR/sparql11-query/#modOffset
187
202
  def offset(start)
188
203
  slice(start, nil)
189
204
  end
@@ -191,7 +206,7 @@ module SPARQL; class Client
191
206
  ##
192
207
  # @param [Integer, #to_i] length
193
208
  # @return [Query]
194
- # @see http://www.w3.org/TR/rdf-sparql-query/#modResultLimit
209
+ # @see http://www.w3.org/TR/sparql11-query/#modResultLimit
195
210
  def limit(length)
196
211
  slice(nil, length)
197
212
  end
@@ -208,7 +223,7 @@ module SPARQL; class Client
208
223
 
209
224
  ##
210
225
  # @return [Query]
211
- # @see http://www.w3.org/TR/rdf-sparql-query/#prefNames
226
+ # @see http://www.w3.org/TR/sparql11-query/#prefNames
212
227
  def prefix(string)
213
228
  (options[:prefixes] ||= []) << string
214
229
  self
@@ -216,7 +231,7 @@ module SPARQL; class Client
216
231
 
217
232
  ##
218
233
  # @return [Query]
219
- # @see http://www.w3.org/TR/rdf-sparql-query/#optionals
234
+ # @see http://www.w3.org/TR/sparql11-query/#optionals
220
235
  def optional(*patterns)
221
236
  (options[:optionals] ||= []) << build_patterns(patterns)
222
237
  self
@@ -298,15 +313,17 @@ module SPARQL; class Client
298
313
  # @return [String]
299
314
  def to_s
300
315
  buffer = [form.to_s.upcase]
316
+
301
317
  case form
302
318
  when :select, :describe
303
319
  only_count = values.empty? and options[:count]
304
320
  buffer << 'DISTINCT' if options[:distinct] and not only_count
305
321
  buffer << 'REDUCED' if options[:reduced]
306
- buffer << ((values.empty? and not options[:count]) ? '*' : values.map { |v| serialize_value(v[1]) }.join(' '))
322
+ buffer << ((values.empty? and not options[:count]) ? '*' : values.map { |v| SPARQL::Client.serialize_value(v[1]) }.join(' '))
307
323
  if options[:count]
308
324
  options[:count].each do |var, count|
309
- buffer << '( COUNT(' + (options[:distinct] ? 'DISTINCT ' : '') + (var.is_a?(String) ? var : "?#{var}") + ') AS ' + (count.is_a?(String) ? count : "?#{count}") + ' )'
325
+ buffer << '( COUNT(' + (options[:distinct] ? 'DISTINCT ' : '') +
326
+ (var.is_a?(String) ? var : "?#{var}") + ') AS ' + (count.is_a?(String) ? count : "?#{count}") + ' )'
310
327
  end
311
328
  end
312
329
  when :construct
@@ -315,10 +332,15 @@ module SPARQL; class Client
315
332
  buffer << '}'
316
333
  end
317
334
 
318
- buffer << "FROM #{serialize_value(options[:from])}" if options[:from]
335
+ buffer << "FROM #{SPARQL::Client.serialize_value(options[:from])}" if options[:from]
319
336
 
320
337
  unless patterns.empty? && form == :describe
321
338
  buffer << 'WHERE {'
339
+
340
+ if options[:graph]
341
+ buffer << 'GRAPH ' + SPARQL::Client.serialize_value(options[:graph])
342
+ buffer << '{'
343
+ end
322
344
  buffer += serialize_patterns(patterns)
323
345
  if options[:optionals]
324
346
  options[:optionals].each do |patterns|
@@ -330,7 +352,11 @@ module SPARQL; class Client
330
352
  if options[:filters]
331
353
  buffer += options[:filters].map { |filter| "FILTER(#{filter})" }
332
354
  end
333
- buffer << '}'
355
+ if options[:graph]
356
+ buffer << '}' # GRAPH
357
+ end
358
+
359
+ buffer << '}' # WHERE
334
360
  end
335
361
 
336
362
  if options[:group_by]
@@ -345,7 +371,7 @@ module SPARQL; class Client
345
371
 
346
372
  buffer << "OFFSET #{options[:offset]}" if options[:offset]
347
373
  buffer << "LIMIT #{options[:limit]}" if options[:limit]
348
- options[:prefixes].reverse.each {|e| buffer.unshift("PREFIX #{e}") } if options[:prefixes]
374
+ options[:prefixes].reverse.each { |e| buffer.unshift("PREFIX #{e}") } if options[:prefixes]
349
375
 
350
376
  buffer.join(' ')
351
377
  end
@@ -353,8 +379,16 @@ module SPARQL; class Client
353
379
  ##
354
380
  # @private
355
381
  def serialize_patterns(patterns)
356
- patterns.map do |p|
357
- p.to_triple.map { |v| serialize_value(v) }.join(' ') + " ."
382
+ rdf_type = RDF.type
383
+ patterns.map do |pattern|
384
+ serialized_pattern = pattern.to_triple.each_with_index.map do |v, i|
385
+ if i == 1 && v.equal?(rdf_type)
386
+ 'a' # abbreviate RDF.type in the predicate position per SPARQL grammar
387
+ else
388
+ SPARQL::Client.serialize_value(v)
389
+ end
390
+ end
391
+ serialized_pattern.join(' ') + ' .'
358
392
  end
359
393
  end
360
394
 
@@ -374,20 +408,5 @@ module SPARQL; class Client
374
408
  def inspect
375
409
  sprintf("#<%s:%#0x(%s)>", self.class.name, __id__, to_s)
376
410
  end
377
-
378
- ##
379
- # Serializes an RDF::Value into a format appropriate for select, construct, and where clauses
380
- #
381
- # @param [RDF::Value] value
382
- # @return [String]
383
- # @private
384
- def serialize_value(value)
385
- # SPARQL queries are UTF-8, but support ASCII-style Unicode escapes, so
386
- # the N-Triples serializer is fine unless it's a variable:
387
- case
388
- when value.variable? then value.to_s
389
- else RDF::NTriples.serialize(value)
390
- end
391
- end
392
411
  end
393
412
  end; end
@@ -1,7 +1,7 @@
1
1
  module SPARQL; class Client
2
2
  module VERSION
3
- VERSION_FILE = File.expand_path("../../../../VERSION", __FILE__)
4
- MAJOR, MINOR, TINY, EXTRA = File.read(VERSION_FILE).chop.split(".")
3
+ VERSION_FILE = File.expand_path('../../../../VERSION', __FILE__)
4
+ MAJOR, MINOR, TINY, EXTRA = File.read(VERSION_FILE).chop.split('.')
5
5
  STRING = [MAJOR, MINOR, TINY, EXTRA].compact.join('.')
6
6
 
7
7
  ##
metadata CHANGED
@@ -1,25 +1,26 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sparql-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Arto Bendiken
9
9
  - Ben Lavender
10
+ - Gregg Kellogg
10
11
  autorequire:
11
12
  bindir: bin
12
13
  cert_chain: []
13
- date: 2013-01-09 00:00:00.000000000 Z
14
+ date: 2013-01-11 00:00:00.000000000 Z
14
15
  dependencies:
15
16
  - !ruby/object:Gem::Dependency
16
- name: json_pure
17
+ name: rdf
17
18
  requirement: !ruby/object:Gem::Requirement
18
19
  none: false
19
20
  requirements:
20
21
  - - ! '>='
21
22
  - !ruby/object:Gem::Version
22
- version: 1.4.6
23
+ version: 0.3.5
23
24
  type: :runtime
24
25
  prerelease: false
25
26
  version_requirements: !ruby/object:Gem::Requirement
@@ -27,15 +28,15 @@ dependencies:
27
28
  requirements:
28
29
  - - ! '>='
29
30
  - !ruby/object:Gem::Version
30
- version: 1.4.6
31
+ version: 0.3.5
31
32
  - !ruby/object:Gem::Dependency
32
- name: rdf
33
+ name: net-http-persistent
33
34
  requirement: !ruby/object:Gem::Requirement
34
35
  none: false
35
36
  requirements:
36
37
  - - ! '>='
37
38
  - !ruby/object:Gem::Version
38
- version: 0.3.5
39
+ version: 1.4.1
39
40
  type: :runtime
40
41
  prerelease: false
41
42
  version_requirements: !ruby/object:Gem::Requirement
@@ -43,15 +44,15 @@ dependencies:
43
44
  requirements:
44
45
  - - ! '>='
45
46
  - !ruby/object:Gem::Version
46
- version: 0.3.5
47
+ version: 1.4.1
47
48
  - !ruby/object:Gem::Dependency
48
- name: net-http-persistent
49
+ name: json_pure
49
50
  requirement: !ruby/object:Gem::Requirement
50
51
  none: false
51
52
  requirements:
52
53
  - - ! '>='
53
54
  - !ruby/object:Gem::Version
54
- version: 1.4.1
55
+ version: 1.4.6
55
56
  type: :runtime
56
57
  prerelease: false
57
58
  version_requirements: !ruby/object:Gem::Requirement
@@ -59,15 +60,15 @@ dependencies:
59
60
  requirements:
60
61
  - - ! '>='
61
62
  - !ruby/object:Gem::Version
62
- version: 1.4.1
63
+ version: 1.4.6
63
64
  - !ruby/object:Gem::Dependency
64
- name: yard
65
+ name: rdf-spec
65
66
  requirement: !ruby/object:Gem::Requirement
66
67
  none: false
67
68
  requirements:
68
69
  - - ! '>='
69
70
  - !ruby/object:Gem::Version
70
- version: 0.7.5
71
+ version: 0.3.5
71
72
  type: :development
72
73
  prerelease: false
73
74
  version_requirements: !ruby/object:Gem::Requirement
@@ -75,7 +76,7 @@ dependencies:
75
76
  requirements:
76
77
  - - ! '>='
77
78
  - !ruby/object:Gem::Version
78
- version: 0.7.5
79
+ version: 0.3.5
79
80
  - !ruby/object:Gem::Dependency
80
81
  name: rspec
81
82
  requirement: !ruby/object:Gem::Requirement
@@ -93,13 +94,13 @@ dependencies:
93
94
  - !ruby/object:Gem::Version
94
95
  version: 2.10.0
95
96
  - !ruby/object:Gem::Dependency
96
- name: rdf-spec
97
+ name: yard
97
98
  requirement: !ruby/object:Gem::Requirement
98
99
  none: false
99
100
  requirements:
100
101
  - - ! '>='
101
102
  - !ruby/object:Gem::Version
102
- version: 0.3.5
103
+ version: 0.8.3
103
104
  type: :development
104
105
  prerelease: false
105
106
  version_requirements: !ruby/object:Gem::Requirement
@@ -107,7 +108,7 @@ dependencies:
107
108
  requirements:
108
109
  - - ! '>='
109
110
  - !ruby/object:Gem::Version
110
- version: 0.3.5
111
+ version: 0.8.3
111
112
  description: SPARQL client for RDF.rb.
112
113
  email: public-rdf-ruby@w3.org
113
114
  executables: []