sparql-client 2.1.0 → 3.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +128 -96
- data/UNLICENSE +1 -1
- data/VERSION +1 -1
- data/lib/sparql/client.rb +157 -77
- data/lib/sparql/client/query.rb +399 -92
- data/lib/sparql/client/repository.rb +42 -18
- data/lib/sparql/client/update.rb +38 -39
- data/lib/sparql/client/version.rb +2 -2
- metadata +26 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b464200c11f2c711f8fae5cb6d75a2deea216a2d682246b7b74ba8316c515404
|
4
|
+
data.tar.gz: 63822682605b809841d008beee3599c81caa72df9d6d7c1004f9e7e1f136ed34
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da4608ed0bc2c17af4bc7eba4c9c82f0be8095174fb18e8ec455441622a7efd3782e375f85eb8200b51617472fa0a6a94d0c7352e21e00e07dc21b1ec2709e23
|
7
|
+
data.tar.gz: 1c78bbf4de5d7c6a30126938813991175598084ef19ebd38b3bd20d941ea8ab1b94cc347f3a7a1a2d7238745c2589ef173974e300edbb581f53e072c9b637724
|
data/README.md
CHANGED
@@ -1,14 +1,15 @@
|
|
1
|
-
#SPARQL Client for RDF.rb
|
1
|
+
# SPARQL Client for RDF.rb
|
2
2
|
|
3
3
|
This is a [Ruby][] implementation of a [SPARQL][] client for [RDF.rb][].
|
4
4
|
|
5
|
-
* <
|
5
|
+
* <https://ruby-rdf.github.com/sparql-client/>
|
6
6
|
|
7
|
-
[![Gem Version](https://badge.fury.io/rb/sparql-client.png)](
|
8
|
-
[![Build Status](https://
|
7
|
+
[![Gem Version](https://badge.fury.io/rb/sparql-client.png)](https://badge.fury.io/rb/sparql-client)
|
8
|
+
[![Build Status](https://github.com/ruby-rdf/sparql-client/workflows/CI/badge.svg?branch=develop)](https://github.com/ruby-rdf/sparql-client/actions?query=workflow%3ACI)
|
9
9
|
[![Coverage Status](https://coveralls.io/repos/ruby-rdf/sparql-client/badge.svg?branch=master&service=github)](https://coveralls.io/github/ruby-rdf/sparql-client?branch=master)
|
10
|
+
[![Gitter chat](https://badges.gitter.im/ruby-rdf/rdf.png)](https://gitter.im/ruby-rdf/rdf)
|
10
11
|
|
11
|
-
##Features
|
12
|
+
## Features
|
12
13
|
|
13
14
|
* Executes queries against any SPARQL 1.0/1.1-compatible endpoint over HTTP,
|
14
15
|
or against an `RDF::Queryable` instance, using the `SPARQL` gem.
|
@@ -18,94 +19,125 @@ This is a [Ruby][] implementation of a [SPARQL][] client for [RDF.rb][].
|
|
18
19
|
* Supports tuple result sets in both XML, JSON, CSV and TSV formats, with JSON being
|
19
20
|
the preferred default for content-negotiation purposes.
|
20
21
|
* Supports graph results in any RDF serialization format understood by RDF.rb.
|
21
|
-
* Returns results using the
|
22
|
+
* Returns results using the RDF.rb object model.
|
22
23
|
* Supports accessing endpoints as read/write [`RDF::Repository`][RDF::Repository]
|
23
24
|
instances {SPARQL::Client::Repository}.
|
24
25
|
|
25
|
-
##Examples
|
26
|
+
## Examples
|
26
27
|
|
27
28
|
### Querying a remote SPARQL endpoint
|
28
|
-
require 'sparql/client'
|
29
29
|
|
30
|
-
|
30
|
+
```ruby
|
31
|
+
require 'sparql/client'
|
32
|
+
sparql = SPARQL::Client.new("http://dbpedia.org/sparql")
|
33
|
+
```
|
31
34
|
|
32
|
-
### Querying a
|
35
|
+
### Querying a remote SPARQL endpoint with a custom User-Agent
|
36
|
+
By default, SPARQL::Client adds a `User-Agent` field to requests, but applications may choose to provide their own, using the `headers` option:
|
33
37
|
|
34
|
-
|
35
|
-
|
38
|
+
```ruby
|
39
|
+
require 'sparql/client'
|
40
|
+
sparql = SPARQL::Client.new("http://dbpedia.org/sparql", headers: {'User-Agent' => 'MyBotName'})
|
41
|
+
```
|
36
42
|
|
37
|
-
|
43
|
+
### Querying a remote SPARQL endpoint with a specified default graph
|
38
44
|
|
39
|
-
|
45
|
+
```ruby
|
46
|
+
require 'sparql/client'
|
47
|
+
sparql = SPARQL::Client.new("http://dbpedia.org/sparql", { graph: "http://dbpedia.org" })
|
48
|
+
```
|
49
|
+
|
50
|
+
|
51
|
+
### Querying a `RDF::Repository` instance
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
require 'rdf/trig'
|
55
|
+
repository = RDF::Repository.load("http://example/dataset.trig")
|
56
|
+
sparql = SPARQL::Client.new(repository)
|
57
|
+
```
|
40
58
|
|
41
|
-
|
42
|
-
result = sparql.ask.whether([:s, :p, :o]).true?
|
59
|
+
### Executing a boolean query and outputting the result
|
43
60
|
|
44
|
-
|
61
|
+
```ruby
|
62
|
+
# ASK WHERE { ?s ?p ?o }
|
63
|
+
result = sparql.ask.whether([:s, :p, :o]).true?
|
64
|
+
puts result.inspect #=> true or false
|
65
|
+
```
|
45
66
|
|
46
67
|
### Executing a tuple query and iterating over the returned solutions
|
47
68
|
|
48
|
-
|
49
|
-
|
69
|
+
```ruby
|
70
|
+
# SELECT * WHERE { ?s ?p ?o } OFFSET 100 LIMIT 10
|
71
|
+
query = sparql.select.where([:s, :p, :o]).offset(100).limit(10)
|
50
72
|
|
51
|
-
|
52
|
-
|
53
|
-
|
73
|
+
query.each_solution do |solution|
|
74
|
+
puts solution.inspect
|
75
|
+
end
|
76
|
+
```
|
54
77
|
|
55
78
|
### Executing a graph query and iterating over the returned statements
|
56
79
|
|
57
|
-
# CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o } LIMIT 10
|
58
|
-
query = sparql.construct([:s, :p, :o]).where([:s, :p, :o]).limit(10)
|
59
80
|
|
60
|
-
|
61
|
-
|
62
|
-
|
81
|
+
```ruby
|
82
|
+
# CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o } LIMIT 10
|
83
|
+
query = sparql.construct([:s, :p, :o]).where([:s, :p, :o]).limit(10)
|
84
|
+
|
85
|
+
query.each_statement do |statement|
|
86
|
+
puts statement.inspect
|
87
|
+
end
|
88
|
+
```
|
63
89
|
|
64
90
|
### Executing an arbitrary textual SPARQL query string
|
65
91
|
|
66
|
-
|
92
|
+
```ruby
|
93
|
+
result = sparql.query("ASK WHERE { ?s ?p ?o }")
|
67
94
|
|
68
|
-
|
95
|
+
puts result.inspect #=> true or false
|
96
|
+
```
|
69
97
|
|
70
98
|
### Inserting data into a graph
|
71
99
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
100
|
+
```ruby
|
101
|
+
# INSERT DATA { <http://example.org/jhacker> <http://xmlns.com/foaf/0.1/name> "J. Random Hacker" .}
|
102
|
+
data = RDF::Graph.new do |graph|
|
103
|
+
graph << [RDF::URI('http://example.org/jhacker'), RDF::Vocab::FOAF.name, "J. Random Hacker"]
|
104
|
+
end
|
105
|
+
sparql.insert_data(data)
|
106
|
+
```
|
77
107
|
|
78
108
|
### Deleting data from a graph
|
79
109
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
110
|
+
```ruby
|
111
|
+
# DELETE DATA { <http://example.org/jhacker> <http://xmlns.com/foaf/0.1/name> "J. Random Hacker" .}
|
112
|
+
data = RDF::Graph.new do |graph|
|
113
|
+
graph << [RDF::URI('http://example.org/jhacker'), RDF::Vocab::FOAF.name, "J. Random Hacker"]
|
114
|
+
end
|
115
|
+
sparql.delete_data(data)
|
116
|
+
```
|
85
117
|
|
86
|
-
##Documentation
|
118
|
+
## Documentation
|
87
119
|
|
88
|
-
*
|
89
|
-
*
|
90
|
-
*
|
91
|
-
*
|
120
|
+
* [SPARQL::Client](https://www.rubydoc.info/github/ruby-rdf/sparql-client/SPARQL/Client)
|
121
|
+
* [SPARQL::Client::Query](https://www.rubydoc.info/github/ruby-rdf/sparql-client/SPARQL/Client/Query)
|
122
|
+
* [SPARQL::Client::Repository](https://www.rubydoc.info/github/ruby-rdf/sparql-client/SPARQL/Client/Repository)
|
123
|
+
* [SPARQL::Client::Update](https://www.rubydoc.info/github/ruby-rdf/sparql-client/SPARQL/Client/Update)
|
92
124
|
|
93
|
-
##Dependencies
|
125
|
+
## Dependencies
|
94
126
|
|
95
|
-
* [Ruby](
|
96
|
-
* [RDF.rb](
|
97
|
-
* [Net::HTTP::Persistent](
|
98
|
-
* Soft dependency on [SPARQL](
|
99
|
-
* Soft dependency on [Nokogiri](
|
127
|
+
* [Ruby](https://ruby-lang.org/) (>= 2.4)
|
128
|
+
* [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.1)
|
129
|
+
* [Net::HTTP::Persistent](https://rubygems.org/gems/net-http-persistent) (~> 3.1)
|
130
|
+
* Soft dependency on [SPARQL](https://rubygems.org/gems/sparql) (~> 3.1)
|
131
|
+
* Soft dependency on [Nokogiri](https://rubygems.org/gems/nokogiri) (>= 1.10)
|
100
132
|
|
101
|
-
##Installation
|
133
|
+
## Installation
|
102
134
|
|
103
|
-
The recommended installation method is via [RubyGems](
|
135
|
+
The recommended installation method is via [RubyGems](https://rubygems.org/).
|
104
136
|
To install the latest official release of the `SPARQL::Client` gem, do:
|
105
137
|
|
106
138
|
% [sudo] gem install sparql-client
|
107
139
|
|
108
|
-
##Download
|
140
|
+
## Download
|
109
141
|
|
110
142
|
To get a local working copy of the development repository, do:
|
111
143
|
|
@@ -114,33 +146,33 @@ To get a local working copy of the development repository, do:
|
|
114
146
|
Alternatively, download the latest development version as a tarball as
|
115
147
|
follows:
|
116
148
|
|
117
|
-
% wget
|
149
|
+
% wget https://github.com/ruby-rdf/sparql-client/tarball/master
|
118
150
|
|
119
|
-
##Mailing List
|
151
|
+
## Mailing List
|
120
152
|
|
121
|
-
* <
|
153
|
+
* <https://lists.w3.org/Archives/Public/public-rdf-ruby/>
|
122
154
|
|
123
|
-
##Authors
|
155
|
+
## Authors
|
124
156
|
|
125
|
-
* [Arto Bendiken](
|
126
|
-
* [Ben Lavender](
|
127
|
-
* [Gregg Kellogg](
|
157
|
+
* [Arto Bendiken](https://github.com/artob) - <https://ar.to/>
|
158
|
+
* [Ben Lavender](https://github.com/bhuga) - <https://bhuga.net/>
|
159
|
+
* [Gregg Kellogg](https://github.com/gkellogg) - <https://greggkellogg.net/>
|
128
160
|
|
129
|
-
##Contributors
|
161
|
+
## Contributors
|
130
162
|
|
131
|
-
* [Christoph Badura](
|
132
|
-
* [James Hetherington](
|
133
|
-
* [Gabriel Horner](
|
134
|
-
* [Nicholas Humfrey](
|
135
|
-
* [Fumihiro Kato](
|
136
|
-
* [David Nielsen](
|
137
|
-
* [Thamaraiselvan Poomalai](
|
138
|
-
* [Michael Sokol](
|
139
|
-
* [Yves Raimond](
|
140
|
-
* [Thomas Feron](
|
141
|
-
* [Nick Gottlieb](
|
163
|
+
* [Christoph Badura](https://github.com/bad) - <https://github.com/bad>
|
164
|
+
* [James Hetherington](https://github.com/jamespjh) - <https://twitter.com/jamespjh>
|
165
|
+
* [Gabriel Horner](https://github.com/cldwalker) - <https://tagaholic.me/>
|
166
|
+
* [Nicholas Humfrey](https://github.com/njh) - <https://www.aelius.com/njh/>
|
167
|
+
* [Fumihiro Kato](https://github.com/fumi) - <https://fumi.me/>
|
168
|
+
* [David Nielsen](https://github.com/drankard) - <https://github.com/drankard>
|
169
|
+
* [Thamaraiselvan Poomalai](https://github.com/selvan) - <https://softonaut.blogspot.com/>
|
170
|
+
* [Michael Sokol](https://github.com/mikaa123) - <https://sokolmichael.com/>
|
171
|
+
* [Yves Raimond](https://github.com/moustaki) - <https://moustaki.org/>
|
172
|
+
* [Thomas Feron](https://github.com/thoferon) - <https://github.com/thoferon>
|
173
|
+
* [Nick Gottlieb](https://github.com/ngottlieb) - <https://www.nicholasgottlieb.com>
|
142
174
|
|
143
|
-
##Contributing
|
175
|
+
## Contributing
|
144
176
|
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.
|
145
177
|
|
146
178
|
* Do your best to adhere to the existing coding conventions and idioms.
|
@@ -153,32 +185,32 @@ This repository uses [Git Flow](https://github.com/nvie/gitflow) to mange develo
|
|
153
185
|
list in the the `README`. Alphabetical order applies.
|
154
186
|
* Do note that in order for us to merge any non-trivial changes (as a rule
|
155
187
|
of thumb, additions larger than about 15 lines of code), we need an
|
156
|
-
explicit [public domain dedication][PDD] on record from you
|
188
|
+
explicit [public domain dedication][PDD] on record from you,
|
189
|
+
which you will be asked to agree to on the first commit to a repo within the organization.
|
190
|
+
Note that the agreement applies to all repos in the [Ruby RDF](https://github.com/ruby-rdf/) organization.
|
157
191
|
|
158
|
-
##Resources
|
192
|
+
## Resources
|
159
193
|
|
160
|
-
* <
|
161
|
-
* <
|
162
|
-
* <
|
163
|
-
* <
|
164
|
-
* <
|
165
|
-
* <http://www.ohloh.net/p/rdf>
|
194
|
+
* <https://ruby-rdf.github.com/sparql-client/>
|
195
|
+
* <https://github.com/ruby-rdf/sparql-client>
|
196
|
+
* <https://rubygems.org/gems/sparql-client>
|
197
|
+
* <https://raa.ruby-lang.org/project/sparql-client/>
|
198
|
+
* <https://www.ohloh.net/p/rdf>
|
166
199
|
|
167
|
-
##License
|
200
|
+
## License
|
168
201
|
|
169
202
|
This is free and unencumbered public domain software. For more information,
|
170
|
-
see <
|
171
|
-
|
172
|
-
[Ruby]:
|
173
|
-
[RDF]:
|
174
|
-
[SPARQL]:
|
175
|
-
[SPARQL JSON]:
|
176
|
-
[RDF.rb]:
|
177
|
-
[RDF
|
178
|
-
[
|
179
|
-
[DSL]: http://en.wikipedia.org/wiki/Domain-specific_language
|
203
|
+
see <https://unlicense.org/> or the accompanying {file:UNLICENSE} file.
|
204
|
+
|
205
|
+
[Ruby]: https://ruby-lang.org/
|
206
|
+
[RDF]: https://www.w3.org/RDF/
|
207
|
+
[SPARQL]: https://en.wikipedia.org/wiki/SPARQL
|
208
|
+
[SPARQL JSON]: https://www.w3.org/TR/rdf-sparql-json-res/
|
209
|
+
[RDF.rb]: https://rubygems.org/gems/rdf
|
210
|
+
[RDF::Repository]: https://rubydoc.info/github/ruby-rdf/rdf/RDF/Repository
|
211
|
+
[DSL]: https://en.wikipedia.org/wiki/Domain-specific_language
|
180
212
|
"domain-specific language"
|
181
|
-
[YARD]:
|
182
|
-
[YARD-GS]:
|
183
|
-
[PDD]:
|
184
|
-
[Backports]:
|
213
|
+
[YARD]: https://yardoc.org/
|
214
|
+
[YARD-GS]: https://rubydoc.info/docs/yard/file/docs/GettingStarted.md
|
215
|
+
[PDD]: https://unlicense.org/#unlicensing-contributions
|
216
|
+
[Backports]: https://rubygems.org/gems/backports
|
data/UNLICENSE
CHANGED
@@ -21,4 +21,4 @@ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
21
21
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
22
|
OTHER DEALINGS IN THE SOFTWARE.
|
23
23
|
|
24
|
-
For more information, please refer to <
|
24
|
+
For more information, please refer to <https://unlicense.org/>
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
3.1.1
|
data/lib/sparql/client.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
require 'net/http/persistent' # @see
|
2
|
-
require 'rdf' # @see
|
3
|
-
require 'rdf/ntriples' # @see
|
1
|
+
require 'net/http/persistent' # @see https://rubygems.org/gems/net-http-persistent
|
2
|
+
require 'rdf' # @see https://rubygems.org/gems/rdf
|
3
|
+
require 'rdf/ntriples' # @see https://rubygems.org/gems/rdf
|
4
4
|
begin
|
5
5
|
require 'nokogiri'
|
6
6
|
rescue LoadError
|
@@ -11,10 +11,10 @@ module SPARQL
|
|
11
11
|
##
|
12
12
|
# A SPARQL 1.0/1.1 client for RDF.rb.
|
13
13
|
#
|
14
|
-
# @see
|
15
|
-
# @see
|
16
|
-
# @see
|
17
|
-
# @see
|
14
|
+
# @see https://www.w3.org/TR/sparql11-query/
|
15
|
+
# @see https://www.w3.org/TR/sparql11-protocol/
|
16
|
+
# @see https://www.w3.org/TR/sparql11-results-json/
|
17
|
+
# @see https://www.w3.org/TR/sparql11-results-csv-tsv/
|
18
18
|
class Client
|
19
19
|
autoload :Query, 'sparql/client/query'
|
20
20
|
autoload :Repository, 'sparql/client/repository'
|
@@ -40,7 +40,7 @@ module SPARQL
|
|
40
40
|
'*/*;q=0.1'
|
41
41
|
].join(', ').freeze
|
42
42
|
GRAPH_ALL = (
|
43
|
-
RDF::Format.content_types.keys +
|
43
|
+
RDF::Format.content_types.keys +
|
44
44
|
['*/*;q=0.1']
|
45
45
|
).join(', ').freeze
|
46
46
|
|
@@ -86,8 +86,13 @@ module SPARQL
|
|
86
86
|
# @option options [Symbol] :method (DEFAULT_METHOD)
|
87
87
|
# @option options [Number] :protocol (DEFAULT_PROTOCOL)
|
88
88
|
# @option options [Hash] :headers
|
89
|
+
# HTTP Request headers
|
90
|
+
#
|
91
|
+
# Defaults `Accept` header based on available reader content types if triples are expected and to SPARQL result types otherwise, to allow for content negotiation based on available readers.
|
92
|
+
#
|
93
|
+
# Defaults `User-Agent` header, unless one is specified.
|
89
94
|
# @option options [Hash] :read_timeout
|
90
|
-
def initialize(url, options
|
95
|
+
def initialize(url, **options, &block)
|
91
96
|
case url
|
92
97
|
when RDF::Queryable
|
93
98
|
@url, @options = url, options.dup
|
@@ -95,6 +100,9 @@ module SPARQL
|
|
95
100
|
@url, @options = RDF::URI.new(url.to_s), options.dup
|
96
101
|
@headers = @options.delete(:headers) || {}
|
97
102
|
@http = http_klass(@url.scheme)
|
103
|
+
|
104
|
+
# Close the http connection when object is deallocated
|
105
|
+
ObjectSpace.define_finalizer(self, proc {@http.shutdown if @http.respond_to?(:shutdown)})
|
98
106
|
end
|
99
107
|
|
100
108
|
if block_given?
|
@@ -105,13 +113,23 @@ module SPARQL
|
|
105
113
|
end
|
106
114
|
end
|
107
115
|
|
116
|
+
##
|
117
|
+
# Closes a client instance by finishing the connection.
|
118
|
+
# The client is unavailable for any further data operations; an IOError is raised if such an attempt is made. I/O streams are automatically closed when they are claimed by the garbage collector.
|
119
|
+
# @return [void] `self`
|
120
|
+
def close
|
121
|
+
@http.shutdown if @http
|
122
|
+
@http = nil
|
123
|
+
self
|
124
|
+
end
|
125
|
+
|
108
126
|
##
|
109
127
|
# Executes a boolean `ASK` query.
|
110
128
|
#
|
111
129
|
# @param (see Query.ask)
|
112
130
|
# @return [Query]
|
113
|
-
def ask(*args)
|
114
|
-
call_query_method(:ask, *args)
|
131
|
+
def ask(*args, **options)
|
132
|
+
call_query_method(:ask, *args, **options)
|
115
133
|
end
|
116
134
|
|
117
135
|
##
|
@@ -119,8 +137,8 @@ module SPARQL
|
|
119
137
|
#
|
120
138
|
# @param (see Query.select)
|
121
139
|
# @return [Query]
|
122
|
-
def select(*args)
|
123
|
-
call_query_method(:select, *args)
|
140
|
+
def select(*args, **options)
|
141
|
+
call_query_method(:select, *args, **options)
|
124
142
|
end
|
125
143
|
|
126
144
|
##
|
@@ -128,8 +146,8 @@ module SPARQL
|
|
128
146
|
#
|
129
147
|
# @param (see Query.describe)
|
130
148
|
# @return [Query]
|
131
|
-
def describe(*args)
|
132
|
-
call_query_method(:describe, *args)
|
149
|
+
def describe(*args, **options)
|
150
|
+
call_query_method(:describe, *args, **options)
|
133
151
|
end
|
134
152
|
|
135
153
|
##
|
@@ -137,8 +155,8 @@ module SPARQL
|
|
137
155
|
#
|
138
156
|
# @param (see Query.construct)
|
139
157
|
# @return [Query]
|
140
|
-
def construct(*args)
|
141
|
-
call_query_method(:construct, *args)
|
158
|
+
def construct(*args, **options)
|
159
|
+
call_query_method(:construct, *args, **options)
|
142
160
|
end
|
143
161
|
|
144
162
|
##
|
@@ -157,19 +175,19 @@ module SPARQL
|
|
157
175
|
# })
|
158
176
|
#
|
159
177
|
# @example Inserting data sourced from a file or URL
|
160
|
-
# data = RDF::Graph.load("
|
178
|
+
# data = RDF::Graph.load("https://raw.githubusercontent.com/ruby-rdf/rdf/develop/etc/doap.nt")
|
161
179
|
# client.insert_data(data)
|
162
180
|
#
|
163
181
|
# @example Inserting data into a named graph
|
164
|
-
# client.insert_data(data, :
|
182
|
+
# client.insert_data(data, graph: "http://example.org/")
|
165
183
|
#
|
166
184
|
# @param [RDF::Enumerable] data
|
167
185
|
# @param [Hash{Symbol => Object}] options
|
168
186
|
# @option options [RDF::URI, String] :graph
|
169
187
|
# @return [void] `self`
|
170
|
-
# @see
|
171
|
-
def insert_data(data, options
|
172
|
-
self.update(Update::InsertData.new(data, options))
|
188
|
+
# @see https://www.w3.org/TR/sparql11-update/#insertData
|
189
|
+
def insert_data(data, **options)
|
190
|
+
self.update(Update::InsertData.new(data, **options))
|
173
191
|
end
|
174
192
|
|
175
193
|
##
|
@@ -178,19 +196,19 @@ module SPARQL
|
|
178
196
|
# This requires that the endpoint support SPARQL 1.1 Update.
|
179
197
|
#
|
180
198
|
# @example Deleting data sourced from a file or URL
|
181
|
-
# data = RDF::Graph.load("
|
199
|
+
# data = RDF::Graph.load("https://raw.githubusercontent.com/ruby-rdf/rdf/develop/etc/doap.nt")
|
182
200
|
# client.delete_data(data)
|
183
201
|
#
|
184
202
|
# @example Deleting data from a named graph
|
185
|
-
# client.delete_data(data, :
|
203
|
+
# client.delete_data(data, graph: "http://example.org/")
|
186
204
|
#
|
187
205
|
# @param [RDF::Enumerable] data
|
188
206
|
# @param [Hash{Symbol => Object}] options
|
189
207
|
# @option options [RDF::URI, String] :graph
|
190
208
|
# @return [void] `self`
|
191
|
-
# @see
|
192
|
-
def delete_data(data, options
|
193
|
-
self.update(Update::DeleteData.new(data, options))
|
209
|
+
# @see https://www.w3.org/TR/sparql11-update/#deleteData
|
210
|
+
def delete_data(data, **options)
|
211
|
+
self.update(Update::DeleteData.new(data, **options))
|
194
212
|
end
|
195
213
|
|
196
214
|
##
|
@@ -204,9 +222,9 @@ module SPARQL
|
|
204
222
|
# @param [Hash{Symbol => Object}] options
|
205
223
|
# @option options [RDF::URI, String] :graph
|
206
224
|
# @return [void] `self`
|
207
|
-
# @see
|
208
|
-
def delete_insert(delete_graph, insert_graph = nil, where_graph = nil, options
|
209
|
-
self.update(Update::DeleteInsert.new(delete_graph, insert_graph, where_graph, options))
|
225
|
+
# @see https://www.w3.org/TR/sparql11-update/#deleteInsert
|
226
|
+
def delete_insert(delete_graph, insert_graph = nil, where_graph = nil, **options)
|
227
|
+
self.update(Update::DeleteInsert.new(delete_graph, insert_graph, where_graph, **options))
|
210
228
|
end
|
211
229
|
|
212
230
|
##
|
@@ -221,9 +239,9 @@ module SPARQL
|
|
221
239
|
# @param [Hash{Symbol => Object}] options
|
222
240
|
# @option options [Boolean] :silent
|
223
241
|
# @return [void] `self`
|
224
|
-
# @see
|
225
|
-
def clear_graph(graph_uri, options
|
226
|
-
self.clear(:graph, graph_uri, options)
|
242
|
+
# @see https://www.w3.org/TR/sparql11-update/#clear
|
243
|
+
def clear_graph(graph_uri, **options)
|
244
|
+
self.clear(:graph, graph_uri, **options)
|
227
245
|
end
|
228
246
|
|
229
247
|
##
|
@@ -249,23 +267,23 @@ module SPARQL
|
|
249
267
|
# @option options [Boolean] :silent
|
250
268
|
# @return [void] `self`
|
251
269
|
#
|
252
|
-
# @overload clear(what, *arguments, options
|
270
|
+
# @overload clear(what, *arguments, **options)
|
253
271
|
# @param [Symbol, #to_sym] what
|
254
272
|
# @param [Array] arguments splat of other arguments to {Update::Clear}.
|
255
273
|
# @param [Hash{Symbol => Object}] options
|
256
274
|
# @option options [Boolean] :silent
|
257
275
|
# @return [void] `self`
|
258
276
|
#
|
259
|
-
# @see
|
277
|
+
# @see https://www.w3.org/TR/sparql11-update/#clear
|
260
278
|
def clear(what, *arguments)
|
261
279
|
self.update(Update::Clear.new(what, *arguments))
|
262
280
|
end
|
263
281
|
|
264
282
|
##
|
265
283
|
# @private
|
266
|
-
def call_query_method(meth, *args)
|
284
|
+
def call_query_method(meth, *args, **options)
|
267
285
|
client = self
|
268
|
-
result = Query.send(meth, *args)
|
286
|
+
result = Query.send(meth, *args, **options)
|
269
287
|
(class << result; self; end).send(:define_method, :execute) do
|
270
288
|
client.query(self)
|
271
289
|
end
|
@@ -288,21 +306,22 @@ module SPARQL
|
|
288
306
|
# @option options [String] :content_type
|
289
307
|
# @option options [Hash] :headers
|
290
308
|
# @return [Array<RDF::Query::Solution>]
|
291
|
-
# @
|
292
|
-
|
309
|
+
# @raise [IOError] if connection is closed
|
310
|
+
# @see https://www.w3.org/TR/sparql11-protocol/#query-operation
|
311
|
+
def query(query, **options)
|
293
312
|
@op = :query
|
294
313
|
@alt_endpoint = options[:endpoint]
|
295
314
|
case @url
|
296
315
|
when RDF::Queryable
|
297
316
|
require 'sparql' unless defined?(::SPARQL::Grammar)
|
298
317
|
begin
|
299
|
-
SPARQL.execute(query, @url, options)
|
318
|
+
SPARQL.execute(query, @url, optimize: true, **options)
|
300
319
|
rescue SPARQL::MalformedQuery
|
301
320
|
$stderr.puts "error running #{query}: #{$!}"
|
302
321
|
raise
|
303
322
|
end
|
304
323
|
else
|
305
|
-
parse_response(response(query, options), options)
|
324
|
+
parse_response(response(query, **options), **options)
|
306
325
|
end
|
307
326
|
end
|
308
327
|
|
@@ -315,16 +334,17 @@ module SPARQL
|
|
315
334
|
# @option options [String] :content_type
|
316
335
|
# @option options [Hash] :headers
|
317
336
|
# @return [void] `self`
|
318
|
-
# @
|
319
|
-
|
337
|
+
# @raise [IOError] if connection is closed
|
338
|
+
# @see https://www.w3.org/TR/sparql11-protocol/#update-operation
|
339
|
+
def update(query, **options)
|
320
340
|
@op = :update
|
321
341
|
@alt_endpoint = options[:endpoint]
|
322
342
|
case @url
|
323
343
|
when RDF::Queryable
|
324
344
|
require 'sparql' unless defined?(::SPARQL::Grammar)
|
325
|
-
SPARQL.execute(query, @url,
|
345
|
+
SPARQL.execute(query, @url, update: true, optimize: true, **options)
|
326
346
|
else
|
327
|
-
response(query, options)
|
347
|
+
response(query, **options)
|
328
348
|
end
|
329
349
|
self
|
330
350
|
end
|
@@ -338,8 +358,9 @@ module SPARQL
|
|
338
358
|
# @option options [String] :content_type
|
339
359
|
# @option options [Hash] :headers
|
340
360
|
# @return [String]
|
341
|
-
|
342
|
-
|
361
|
+
# @raise [IOError] if connection is closed
|
362
|
+
def response(query, **options)
|
363
|
+
headers = options[:headers] || @headers
|
343
364
|
headers['Accept'] = options[:content_type] if options[:content_type]
|
344
365
|
request(query, headers) do |response|
|
345
366
|
case response
|
@@ -359,7 +380,7 @@ module SPARQL
|
|
359
380
|
# @param [Net::HTTPSuccess] response
|
360
381
|
# @param [Hash{Symbol => Object}] options
|
361
382
|
# @return [Object]
|
362
|
-
def parse_response(response, options
|
383
|
+
def parse_response(response, **options)
|
363
384
|
case options[:content_type] || response.content_type
|
364
385
|
when NilClass
|
365
386
|
response.body
|
@@ -374,14 +395,14 @@ module SPARQL
|
|
374
395
|
when RESULT_TSV
|
375
396
|
self.class.parse_tsv_bindings(response.body, nodes)
|
376
397
|
else
|
377
|
-
parse_rdf_serialization(response, options)
|
398
|
+
parse_rdf_serialization(response, **options)
|
378
399
|
end
|
379
400
|
end
|
380
401
|
|
381
402
|
##
|
382
403
|
# @param [String, Hash] json
|
383
404
|
# @return [<RDF::Query::Solutions>]
|
384
|
-
# @see
|
405
|
+
# @see https://www.w3.org/TR/rdf-sparql-json-res/#results
|
385
406
|
def self.parse_json_bindings(json, nodes = {})
|
386
407
|
require 'json' unless defined?(::JSON)
|
387
408
|
json = JSON.parse(json.to_s) unless json.is_a?(Hash)
|
@@ -402,8 +423,8 @@ module SPARQL
|
|
402
423
|
##
|
403
424
|
# @param [Hash{String => String}] value
|
404
425
|
# @return [RDF::Value]
|
405
|
-
# @see
|
406
|
-
# @see
|
426
|
+
# @see https://www.w3.org/TR/sparql11-results-json/#select-encode-terms
|
427
|
+
# @see https://www.w3.org/TR/rdf-sparql-json-res/#variable-binding-results
|
407
428
|
def self.parse_json_value(value, nodes = {})
|
408
429
|
case value['type'].to_sym
|
409
430
|
when :bnode
|
@@ -411,9 +432,9 @@ module SPARQL
|
|
411
432
|
when :uri
|
412
433
|
RDF::URI.new(value['value'])
|
413
434
|
when :literal
|
414
|
-
RDF::Literal.new(value['value'], :
|
435
|
+
RDF::Literal.new(value['value'], datatype: value['datatype'], language: value['xml:lang'])
|
415
436
|
when :'typed-literal'
|
416
|
-
RDF::Literal.new(value['value'], :
|
437
|
+
RDF::Literal.new(value['value'], datatype: value['datatype'])
|
417
438
|
else nil
|
418
439
|
end
|
419
440
|
end
|
@@ -421,7 +442,7 @@ module SPARQL
|
|
421
442
|
##
|
422
443
|
# @param [String, Array<Array<String>>] csv
|
423
444
|
# @return [<RDF::Query::Solutions>]
|
424
|
-
# @see
|
445
|
+
# @see https://www.w3.org/TR/sparql11-results-csv-tsv/
|
425
446
|
def self.parse_csv_bindings(csv, nodes = {})
|
426
447
|
require 'csv' unless defined?(::CSV)
|
427
448
|
csv = CSV.parse(csv.to_s) unless csv.is_a?(Array)
|
@@ -445,7 +466,7 @@ module SPARQL
|
|
445
466
|
##
|
446
467
|
# @param [String, Array<Array<String>>] tsv
|
447
468
|
# @return [<RDF::Query::Solutions>]
|
448
|
-
# @see
|
469
|
+
# @see https://www.w3.org/TR/sparql11-results-csv-tsv/
|
449
470
|
def self.parse_tsv_bindings(tsv, nodes = {})
|
450
471
|
tsv = tsv.lines.map {|l| l.chomp.split("\t")} unless tsv.is_a?(Array)
|
451
472
|
vars = tsv.shift.map {|h| h.sub(/^\?/, '')}
|
@@ -474,7 +495,7 @@ module SPARQL
|
|
474
495
|
##
|
475
496
|
# @param [String, IO, Nokogiri::XML::Node, REXML::Element] xml
|
476
497
|
# @return [<RDF::Query::Solutions>]
|
477
|
-
# @see
|
498
|
+
# @see https://www.w3.org/TR/rdf-sparql-json-res/#results
|
478
499
|
def self.parse_xml_bindings(xml, nodes = {})
|
479
500
|
xml.force_encoding(::Encoding::UTF_8) if xml.respond_to?(:force_encoding)
|
480
501
|
|
@@ -519,7 +540,7 @@ module SPARQL
|
|
519
540
|
##
|
520
541
|
# @param [Nokogiri::XML::Element, REXML::Element] value
|
521
542
|
# @return [RDF::Value]
|
522
|
-
# @see
|
543
|
+
# @see https://www.w3.org/TR/rdf-sparql-json-res/#variable-binding-results
|
523
544
|
def self.parse_xml_value(value, nodes = {})
|
524
545
|
case value.name.to_sym
|
525
546
|
when :bnode
|
@@ -529,7 +550,7 @@ module SPARQL
|
|
529
550
|
when :literal
|
530
551
|
lang = value.respond_to?(:attr) ? value.attr('xml:lang') : value.attributes['xml:lang']
|
531
552
|
datatype = value.respond_to?(:attr) ? value.attr('datatype') : value.attributes['datatype']
|
532
|
-
RDF::Literal.new(value.text, :
|
553
|
+
RDF::Literal.new(value.text, language: lang, datatype: datatype)
|
533
554
|
else nil
|
534
555
|
end
|
535
556
|
end
|
@@ -538,12 +559,12 @@ module SPARQL
|
|
538
559
|
# @param [Net::HTTPSuccess] response
|
539
560
|
# @param [Hash{Symbol => Object}] options
|
540
561
|
# @return [RDF::Enumerable]
|
541
|
-
def parse_rdf_serialization(response, options
|
542
|
-
options = {:
|
562
|
+
def parse_rdf_serialization(response, **options)
|
563
|
+
options = {content_type: response.content_type} unless options[:content_type]
|
543
564
|
if reader = RDF::Reader.for(options)
|
544
565
|
reader.new(response.body)
|
545
566
|
else
|
546
|
-
raise RDF::ReaderError, "no
|
567
|
+
raise RDF::ReaderError, "no RDF reader was found for #{options}."
|
547
568
|
end
|
548
569
|
end
|
549
570
|
|
@@ -609,11 +630,15 @@ module SPARQL
|
|
609
630
|
# @private
|
610
631
|
def self.serialize_patterns(patterns, use_vars = false)
|
611
632
|
patterns.map do |pattern|
|
612
|
-
serialized_pattern =
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
633
|
+
serialized_pattern = case pattern
|
634
|
+
when SPARQL::Client::QueryElement then [pattern.to_s]
|
635
|
+
else
|
636
|
+
RDF::Statement.from(pattern).to_triple.each_with_index.map do |v, i|
|
637
|
+
if i == 1
|
638
|
+
SPARQL::Client.serialize_predicate(v)
|
639
|
+
else
|
640
|
+
SPARQL::Client.serialize_value(v, use_vars)
|
641
|
+
end
|
617
642
|
end
|
618
643
|
end
|
619
644
|
serialized_pattern.join(' ') + ' .'
|
@@ -654,7 +679,7 @@ module SPARQL
|
|
654
679
|
value = ENV['https_proxy']
|
655
680
|
proxy_url = URI.parse(value) unless value.nil? || value.empty?
|
656
681
|
end
|
657
|
-
klass = Net::HTTP::Persistent.new(self.class.to_s, proxy_url)
|
682
|
+
klass = Net::HTTP::Persistent.new(name: self.class.to_s, proxy: proxy_url)
|
658
683
|
klass.keep_alive = @options[:keep_alive] || 120
|
659
684
|
klass.read_timeout = @options[:read_timeout] || 60
|
660
685
|
klass
|
@@ -665,10 +690,16 @@ module SPARQL
|
|
665
690
|
#
|
666
691
|
# @param [String, #to_s] query
|
667
692
|
# @param [Hash{String => String}] headers
|
693
|
+
# HTTP Request headers
|
694
|
+
#
|
695
|
+
# Defaults `Accept` header based on available reader content types if triples are expected and to SPARQL result types otherwise, to allow for content negotiation based on available readers.
|
696
|
+
#
|
697
|
+
# Defaults `User-Agent` header, unless one is specified.
|
668
698
|
# @yield [response]
|
669
699
|
# @yieldparam [Net::HTTPResponse] response
|
670
700
|
# @return [Net::HTTPResponse]
|
671
|
-
# @
|
701
|
+
# @raise [IOError] if connection is closed
|
702
|
+
# @see https://www.w3.org/TR/sparql11-protocol/#query-operation
|
672
703
|
def request(query, headers = {}, &block)
|
673
704
|
# Make sure an appropriate Accept header is present
|
674
705
|
headers['Accept'] ||= if (query.respond_to?(:expects_statements?) ?
|
@@ -678,6 +709,7 @@ module SPARQL
|
|
678
709
|
else
|
679
710
|
RESULT_ALL
|
680
711
|
end
|
712
|
+
headers['User-Agent'] ||= "Ruby SPARQL::Client/#{SPARQL::Client::VERSION}"
|
681
713
|
|
682
714
|
request = send("make_#{request_method(query)}_request", query, headers)
|
683
715
|
|
@@ -685,6 +717,7 @@ module SPARQL
|
|
685
717
|
|
686
718
|
pre_http_hook(request) if respond_to?(:pre_http_hook)
|
687
719
|
|
720
|
+
raise IOError, "Client has been closed" unless @http
|
688
721
|
response = @http.request(::URI.parse(url.to_s), request)
|
689
722
|
|
690
723
|
post_http_hook(response) if respond_to?(:post_http_hook)
|
@@ -693,8 +726,8 @@ module SPARQL
|
|
693
726
|
if response.kind_of? Net::HTTPRedirection
|
694
727
|
response = @http.request(::URI.parse(response['location']), request)
|
695
728
|
else
|
696
|
-
return block_given? ? block.call(response) : response
|
697
|
-
end
|
729
|
+
return block_given? ? block.call(response) : response
|
730
|
+
end
|
698
731
|
end
|
699
732
|
raise ServerError, "Infinite redirect at #{url}. Redirected more than 10 times."
|
700
733
|
end
|
@@ -714,10 +747,11 @@ module SPARQL
|
|
714
747
|
# @param [String, #to_s] query
|
715
748
|
# @param [Hash{String => String}] headers
|
716
749
|
# @return [Net::HTTPRequest]
|
717
|
-
# @see
|
750
|
+
# @see https://www.w3.org/TR/sparql11-protocol/#query-via-get
|
718
751
|
def make_get_request(query, headers = {})
|
719
752
|
url = self.url.dup
|
720
|
-
url.query_values = (url.query_values || {}).merge(:
|
753
|
+
url.query_values = (url.query_values || {}).merge(query: query.to_s)
|
754
|
+
set_url_default_graph url unless @options[:graph].nil?
|
721
755
|
request = Net::HTTP::Get.new(url.request_uri, self.headers.merge(headers))
|
722
756
|
request
|
723
757
|
end
|
@@ -728,25 +762,71 @@ module SPARQL
|
|
728
762
|
# @param [String, #to_s] query
|
729
763
|
# @param [Hash{String => String}] headers
|
730
764
|
# @return [Net::HTTPRequest]
|
731
|
-
# @see
|
732
|
-
# @see
|
765
|
+
# @see https://www.w3.org/TR/sparql11-protocol/#query-via-post-direct
|
766
|
+
# @see https://www.w3.org/TR/sparql11-protocol/#query-via-post-urlencoded
|
733
767
|
def make_post_request(query, headers = {})
|
734
768
|
if @alt_endpoint.nil?
|
735
|
-
|
769
|
+
url = self.url.dup
|
770
|
+
set_url_default_graph url unless @options[:graph].nil?
|
771
|
+
endpoint = url.request_uri
|
736
772
|
else
|
737
773
|
endpoint = @alt_endpoint
|
738
774
|
end
|
775
|
+
|
739
776
|
request = Net::HTTP::Post.new(endpoint, self.headers.merge(headers))
|
740
777
|
case (self.options[:protocol] || DEFAULT_PROTOCOL).to_s
|
741
778
|
when '1.1'
|
742
779
|
request['Content-Type'] = 'application/sparql-' + (@op || :query).to_s
|
743
780
|
request.body = query.to_s
|
744
781
|
when '1.0'
|
745
|
-
|
782
|
+
form_data = {(@op || :query) => query.to_s}
|
783
|
+
form_data.merge!(
|
784
|
+
{:'default-graph-uri' => @options[:graph]}
|
785
|
+
) if !@options[:graph].nil? && (@op.eql? :query)
|
786
|
+
form_data.merge!(
|
787
|
+
{:'using-graph-uri' => @options[:graph]}
|
788
|
+
) if !@options[:graph].nil? && (@op.eql? :update)
|
789
|
+
request.set_form_data(form_data)
|
746
790
|
else
|
747
791
|
raise ArgumentError, "unknown SPARQL protocol version: #{self.options[:protocol].inspect}"
|
748
792
|
end
|
749
793
|
request
|
750
794
|
end
|
795
|
+
|
796
|
+
##
|
797
|
+
# Setup url query parameter to use a specified default graph
|
798
|
+
#
|
799
|
+
# @see https://www.w3.org/TR/sparql11-protocol/#query-operation
|
800
|
+
# @see https://www.w3.org/TR/sparql11-protocol/#update-operation
|
801
|
+
def set_url_default_graph url
|
802
|
+
if @options[:graph].is_a? Array
|
803
|
+
graphs = @options[:graph].map {|graph|
|
804
|
+
CGI::escape(graph)
|
805
|
+
}
|
806
|
+
else
|
807
|
+
graphs = CGI::escape(@options[:graph])
|
808
|
+
end
|
809
|
+
case @op
|
810
|
+
when :query
|
811
|
+
url.query_values = (url.query_values || {})
|
812
|
+
.merge(:'default-graph-uri' => graphs)
|
813
|
+
when :update
|
814
|
+
url.query_values = (url.query_values || {})
|
815
|
+
.merge(:'using-graph-uri' => graphs)
|
816
|
+
end
|
817
|
+
end
|
818
|
+
|
819
|
+
# A query element can be used as a component of a query. It may be initialized with a string, which is wrapped in an appropriate container depending on the type of QueryElement. Implements {#to_s} to property serialize when generating a SPARQL query.
|
820
|
+
class QueryElement
|
821
|
+
attr_reader :elements
|
822
|
+
|
823
|
+
def initialize(*args)
|
824
|
+
@elements = args
|
825
|
+
end
|
826
|
+
|
827
|
+
def to_s
|
828
|
+
raise NotImplemented
|
829
|
+
end
|
830
|
+
end
|
751
831
|
end # Client
|
752
832
|
end # SPARQL
|