sparql-client 3.2.0 → 3.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 91430f8e6c63118569f32b193b274fe8f19ca59728af08591177bdd5795ff05e
4
- data.tar.gz: 3e0da28e7522a09004e2ac4bcd1b42e17cc943f02fd242d3a9e399597baa33e1
3
+ metadata.gz: 2c9eca6a3f6e57b16150e55c6a595c616c4b8b58778772e619168e5dd19c05a5
4
+ data.tar.gz: 5c383ab056d6f28951a366a5cb546f3aa900e7c81170b2f42a04ce72167deea8
5
5
  SHA512:
6
- metadata.gz: 21e38994cd92126304d4a481a3ee471018c9dd99206d537340b2c91eb49bd853bd76fa3bf558e795ed18110b6d639f97cb5288d2b520f5196739d7bb9cc0f095
7
- data.tar.gz: 75a14ccdce74d985fb83f541853e4e8d95edfeb9c8d59a5f5fbc988cf378c985623df574585480e30945791327ce9f99ab5da2178b5c886c8cc91152d7174442
6
+ metadata.gz: 12f686f4d066f3539de0fd3924895bbd854d2998f4b666d60e58df2efac33343c214588ff19b5ed759d8f2c481f8c3ff5a02f9f432fe55977a56e4080029d8c1
7
+ data.tar.gz: 4168b202c647758fffb13ad0a6445b46d797a34482764cdc5efb3a9e36a258c0157e03f958eaf5a96530453210afbbcc986aa0b3374a849dde73a9f090d15b6d
data/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  This is a [Ruby][] implementation of a [SPARQL][] client for [RDF.rb][].
4
4
 
5
- * <https://ruby-rdf.github.com/sparql-client/>
5
+ * <https://ruby-rdf.github.io/sparql-client/>
6
6
 
7
- [![Gem Version](https://badge.fury.io/rb/sparql-client.png)](https://badge.fury.io/rb/sparql-client)
7
+ [![Gem Version](https://badge.fury.io/rb/sparql-client.svg)](https://badge.fury.io/rb/sparql-client)
8
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
10
  [![Gitter chat](https://badges.gitter.im/ruby-rdf/rdf.png)](https://gitter.im/ruby-rdf/rdf)
@@ -44,7 +44,7 @@ sparql = SPARQL::Client.new("http://dbpedia.org/sparql", headers: {'User-Agent'
44
44
 
45
45
  ```ruby
46
46
  require 'sparql/client'
47
- sparql = SPARQL::Client.new("http://dbpedia.org/sparql", { graph: "http://dbpedia.org" })
47
+ sparql = SPARQL::Client.new("http://dbpedia.org/sparql", graph: "http://dbpedia.org")
48
48
  ```
49
49
 
50
50
 
@@ -117,10 +117,10 @@ sparql.delete_data(data)
117
117
 
118
118
  ## Documentation
119
119
 
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)
120
+ * [SPARQL::Client](https://ruby-rdf.github.io/sparql-client/SPARQL/Client)
121
+ * [SPARQL::Client::Query](https://ruby-rdf.github.io/sparql-client/SPARQL/Client/Query)
122
+ * [SPARQL::Client::Repository](https://ruby-rdf.github.io/sparql-client/SPARQL/Client/Repository)
123
+ * [SPARQL::Client::Update](https://ruby-rdf.github.io/sparql-client/SPARQL/Client/Update)
124
124
 
125
125
  ## Dependencies
126
126
 
@@ -191,7 +191,7 @@ This repository uses [Git Flow](https://github.com/nvie/gitflow) to mange develo
191
191
 
192
192
  ## Resources
193
193
 
194
- * <https://ruby-rdf.github.com/sparql-client/>
194
+ * <https://ruby-rdf.github.io/sparql-client/>
195
195
  * <https://github.com/ruby-rdf/sparql-client>
196
196
  * <https://rubygems.org/gems/sparql-client>
197
197
  * <https://raa.ruby-lang.org/project/sparql-client/>
@@ -207,7 +207,7 @@ see <https://unlicense.org/> or the accompanying {file:UNLICENSE} file.
207
207
  [SPARQL]: https://en.wikipedia.org/wiki/SPARQL
208
208
  [SPARQL JSON]: https://www.w3.org/TR/rdf-sparql-json-res/
209
209
  [RDF.rb]: https://rubygems.org/gems/rdf
210
- [RDF::Repository]: https://rubydoc.info/github/ruby-rdf/rdf/RDF/Repository
210
+ [RDF::Repository]: https://ruby-rdf.github.io/rdf/RDF/Repository
211
211
  [DSL]: https://en.wikipedia.org/wiki/Domain-specific_language
212
212
  "domain-specific language"
213
213
  [YARD]: https://yardoc.org/
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.2.0
1
+ 3.2.2
@@ -111,7 +111,7 @@ class SPARQL::Client
111
111
 
112
112
  ##
113
113
  # @example ASK WHERE { ?s ?p ?o . }
114
- # query.ask.where([:s, :p, :o])
114
+ # Query.ask.where([:s, :p, :o])
115
115
  #
116
116
  # @return [Query]
117
117
  # @see https://www.w3.org/TR/sparql11-query/#ask
@@ -122,13 +122,13 @@ class SPARQL::Client
122
122
 
123
123
  ##
124
124
  # @example `SELECT * WHERE { ?s ?p ?o . }`
125
- # query.select.where([:s, :p, :o])
125
+ # Query.select.where([:s, :p, :o])
126
126
  #
127
127
  # @example `SELECT ?s WHERE {?s ?p ?o .}`
128
- # query.select(:s).where([:s, :p, :o])
128
+ # Query.select(:s).where([:s, :p, :o])
129
129
  #
130
130
  # @example `SELECT COUNT(?uri as ?c) WHERE {?uri a owl:Class}`
131
- # query.select(count: {uri: :c}).where([:uri, RDF.type, RDF::OWL.Class])
131
+ # Query.select(count: {uri: :c}).where([:uri, RDF.type, RDF::OWL.Class])
132
132
  #
133
133
  # @param [Array<Symbol>, Hash{Symbol => RDF::Query::Variable}] variables
134
134
  # @return [Query]
@@ -144,7 +144,7 @@ class SPARQL::Client
144
144
 
145
145
  ##
146
146
  # @example DESCRIBE * WHERE { ?s ?p ?o . }
147
- # query.describe.where([:s, :p, :o])
147
+ # Query.describe.where([:s, :p, :o])
148
148
  #
149
149
  # @param [Array<Symbol>] variables
150
150
  # @return [Query]
@@ -158,7 +158,7 @@ class SPARQL::Client
158
158
 
159
159
  ##
160
160
  # @example CONSTRUCT { ?s ?p ?o . } WHERE { ?s ?p ?o . }
161
- # query.construct([:s, :p, :o]).where([:s, :p, :o])
161
+ # Query.construct([:s, :p, :o]).where([:s, :p, :o])
162
162
  #
163
163
  # @param [Array<RDF::Query::Pattern, Array>] patterns
164
164
  # @return [Query]
@@ -170,7 +170,7 @@ class SPARQL::Client
170
170
 
171
171
  ##
172
172
  # @example SELECT * FROM <a> WHERE \{ ?s ?p ?o . \}
173
- # query.select.from(RDF::URI.new(a)).where([:s, :p, :o])
173
+ # Query.select.from(RDF::URI.new(a)).where([:s, :p, :o])
174
174
  #
175
175
  # @param [RDF::URI] uri
176
176
  # @return [Query]
@@ -182,22 +182,22 @@ class SPARQL::Client
182
182
 
183
183
  ##
184
184
  # @example SELECT * WHERE { ?s ?p ?o . }
185
- # query.select.where([:s, :p, :o])
186
- # query.select.whether([:s, :p, :o])
185
+ # Query.select.where([:s, :p, :o])
186
+ # Query.select.whether([:s, :p, :o])
187
187
  #
188
188
  # @example SELECT * WHERE { { SELECT * WHERE { ?s ?p ?o . } } . ?s ?p ?o . }
189
- # subquery = query.select.where([:s, :p, :o])
190
- # query.select.where([:s, :p, :o], subquery)
189
+ # subquery = Query.select.where([:s, :p, :o])
190
+ # Query.select.where([:s, :p, :o], subquery)
191
191
  #
192
192
  # @example SELECT * WHERE { { SELECT * WHERE { ?s ?p ?o . } } . ?s ?p ?o . }
193
- # query.select.where([:s, :p, :o]) do |q|
193
+ # Query.select.where([:s, :p, :o]) do |q|
194
194
  # q.select.where([:s, :p, :o])
195
195
  # end
196
196
  #
197
197
  # Block form can be used for chaining calls in addition to creating sub-select queries.
198
198
  #
199
199
  # @example SELECT * WHERE { ?s ?p ?o . } ORDER BY ?o
200
- # query.select.where([:s, :p, :o]) do
200
+ # Query.select.where([:s, :p, :o]) do
201
201
  # order(:o)
202
202
  # end
203
203
  #
@@ -236,14 +236,14 @@ class SPARQL::Client
236
236
 
237
237
  ##
238
238
  # @example SELECT * WHERE { ?s ?p ?o . } ORDER BY ?o
239
- # query.select.where([:s, :p, :o]).order(:o)
240
- # query.select.where([:s, :p, :o]).order_by(:o)
239
+ # Query.select.where([:s, :p, :o]).order(:o)
240
+ # Query.select.where([:s, :p, :o]).order_by(:o)
241
241
  #
242
242
  # @example SELECT * WHERE { ?s ?p ?o . } ORDER BY ?o ?p
243
- # query.select.where([:s, :p, :o]).order_by(:o, :p)
243
+ # Query.select.where([:s, :p, :o]).order_by(:o, :p)
244
244
  #
245
245
  # @example SELECT * WHERE { ?s ?p ?o . } ORDER BY ASC(?o) DESC(?p)
246
- # query.select.where([:s, :p, :o]).order_by(o: :asc, p: :desc)
246
+ # Query.select.where([:s, :p, :o]).order_by(o: :asc, p: :desc)
247
247
  #
248
248
  # @param [Array<Symbol, String>] variables
249
249
  # @return [Query]
@@ -257,8 +257,8 @@ class SPARQL::Client
257
257
 
258
258
  ##
259
259
  # @example SELECT * WHERE { ?s ?p ?o . } ORDER BY ASC(?o)
260
- # query.select.where([:s, :p, :o]).order.asc(:o)
261
- # query.select.where([:s, :p, :o]).asc(:o)
260
+ # Query.select.where([:s, :p, :o]).order.asc(:o)
261
+ # Query.select.where([:s, :p, :o]).asc(:o)
262
262
  #
263
263
  # @param [Array<Symbol, String>] var
264
264
  # @return [Query]
@@ -270,8 +270,8 @@ class SPARQL::Client
270
270
 
271
271
  ##
272
272
  # @example SELECT * WHERE { ?s ?p ?o . } ORDER BY DESC(?o)
273
- # query.select.where([:s, :p, :o]).order.desc(:o)
274
- # query.select.where([:s, :p, :o]).desc(:o)
273
+ # Query.select.where([:s, :p, :o]).order.desc(:o)
274
+ # Query.select.where([:s, :p, :o]).desc(:o)
275
275
  #
276
276
  # @param [Array<Symbol, String>] var
277
277
  # @return [Query]
@@ -283,7 +283,7 @@ class SPARQL::Client
283
283
 
284
284
  ##
285
285
  # @example SELECT ?s WHERE { ?s ?p ?o . } GROUP BY ?s
286
- # query.select(:s).where([:s, :p, :o]).group_by(:s)
286
+ # Query.select(:s).where([:s, :p, :o]).group_by(:s)
287
287
  #
288
288
  # @param [Array<Symbol, String>] variables
289
289
  # @return [Query]
@@ -297,7 +297,7 @@ class SPARQL::Client
297
297
 
298
298
  ##
299
299
  # @example SELECT DISTINCT ?s WHERE { ?s ?p ?o . }
300
- # query.select(:s).distinct.where([:s, :p, :o])
300
+ # Query.select(:s).distinct.where([:s, :p, :o])
301
301
  #
302
302
  # @return [Query]
303
303
  # @see https://www.w3.org/TR/sparql11-query/#modDuplicates
@@ -308,7 +308,7 @@ class SPARQL::Client
308
308
 
309
309
  ##
310
310
  # @example SELECT REDUCED ?s WHERE { ?s ?p ?o . }
311
- # query.select(:s).reduced.where([:s, :p, :o])
311
+ # Query.select(:s).reduced.where([:s, :p, :o])
312
312
  #
313
313
  # @return [Query]
314
314
  # @see https://www.w3.org/TR/sparql11-query/#modDuplicates
@@ -319,7 +319,8 @@ class SPARQL::Client
319
319
 
320
320
  ##
321
321
  # @example SELECT * WHERE { GRAPH ?g { ?s ?p ?o . } }
322
- # query.select.graph(:g).where([:s, :p, :o])
322
+ # Query.select.graph(:g).where([:s, :p, :o])
323
+ #
323
324
  # @param [RDF::Value] graph_uri_or_var
324
325
  # @return [Query]
325
326
  # @see https://www.w3.org/TR/sparql11-query/#queryDataset
@@ -335,7 +336,7 @@ class SPARQL::Client
335
336
 
336
337
  ##
337
338
  # @example SELECT * WHERE { ?s ?p ?o . } OFFSET 100
338
- # query.select.where([:s, :p, :o]).offset(100)
339
+ # Query.select.where([:s, :p, :o]).offset(100)
339
340
  #
340
341
  # @param [Integer, #to_i] start
341
342
  # @return [Query]
@@ -346,7 +347,7 @@ class SPARQL::Client
346
347
 
347
348
  ##
348
349
  # @example SELECT * WHERE { ?s ?p ?o . } LIMIT 10
349
- # query.select.where([:s, :p, :o]).limit(10)
350
+ # Query.select.where([:s, :p, :o]).limit(10)
350
351
  #
351
352
  # @param [Integer, #to_i] length
352
353
  # @return [Query]
@@ -357,7 +358,7 @@ class SPARQL::Client
357
358
 
358
359
  ##
359
360
  # @example SELECT * WHERE { ?s ?p ?o . } OFFSET 100 LIMIT 10
360
- # query.select.where([:s, :p, :o]).slice(100, 10)
361
+ # Query.select.where([:s, :p, :o]).slice(100, 10)
361
362
  #
362
363
  # @param [Integer, #to_i] start
363
364
  # @param [Integer, #to_i] length
@@ -371,7 +372,7 @@ class SPARQL::Client
371
372
  ##
372
373
  # @overload prefix(prefix: uri)
373
374
  # @example PREFIX dc: <http://purl.org/dc/elements/1.1/> PREFIX foaf: <http://xmlns.com/foaf/0.1/> SELECT * WHERE \{ ?s ?p ?o . \}
374
- # query.select.
375
+ # Query.select.
375
376
  # prefix(dc: RDF::URI("http://purl.org/dc/elements/1.1/")).
376
377
  # prefix(foaf: RDF::URI("http://xmlns.com/foaf/0.1/")).
377
378
  # where([:s, :p, :o])
@@ -382,7 +383,7 @@ class SPARQL::Client
382
383
  #
383
384
  # @overload prefix(string)
384
385
  # @example PREFIX dc: <http://purl.org/dc/elements/1.1/> PREFIX foaf: <http://xmlns.com/foaf/0.1/> SELECT * WHERE \{ ?s ?p ?o . \}
385
- # query.select.
386
+ # Query.select.
386
387
  # prefix("dc: <http://purl.org/dc/elements/1.1/>").
387
388
  # prefix("foaf: <http://xmlns.com/foaf/0.1/>").
388
389
  # where([:s, :p, :o])
@@ -406,13 +407,13 @@ class SPARQL::Client
406
407
 
407
408
  ##
408
409
  # @example SELECT * WHERE \{ ?s ?p ?o . OPTIONAL \{ ?s a ?o . ?s \<http://purl.org/dc/terms/abstract\> ?o . \} \}
409
- # query.select.where([:s, :p, :o]).
410
+ # Query.select.where([:s, :p, :o]).
410
411
  # optional([:s, RDF.type, :o], [:s, RDF::Vocab::DC.abstract, :o])
411
412
  #
412
413
  # The block form can be used for adding filters:
413
414
  #
414
415
  # @example ASK WHERE { ?s ?p ?o . OPTIONAL { ?s ?p ?o . FILTER(regex(?s, 'Abiline, Texas'))} }
415
- # query.ask.where([:s, :p, :o]).optional([:s, :p, :o]) do
416
+ # Query.ask.where([:s, :p, :o]).optional([:s, :p, :o]) do
416
417
  # filter("regex(?s, 'Abiline, Texas')")
417
418
  # end
418
419
  #
@@ -441,22 +442,89 @@ class SPARQL::Client
441
442
  self
442
443
  end
443
444
 
445
+ ##
446
+ # Federated Queries via the SERVICE keyword.
447
+ #
448
+ # Supports limited use of the SERVICE keyword with an endpoint term, a sequence of patterns, a query, or a block.
449
+ #
450
+ # @example SELECT * WHERE \{ ?s ?p1 ?o1 . SERVICE ?l \{ ?s ?p2 ?o2 \} \}
451
+ # Query.select.where([:s, :p1, :o1]).
452
+ # service(:l, [:s, :p2, :o2])
453
+ #
454
+ # @example SELECT * WHERE \{ ?book <http://purl.org/dc/terms/title> ?title . SERVICE ?l \{ ?book <http://purl.org/dc/elements/1.1/title> ?title . FILTER(langmatches(?title, 'en')) \} \}
455
+ # query1 = SPARQL::Client::Query.select.
456
+ # where([:book, RDF::Vocab::DC11.title, :title]).
457
+ # filter("langmatches(?title, 'en')")
458
+ # Query.select.where([:book, RDF::Vocab::DC.title, :title]).service(?l, query1)
459
+ #
460
+ # The block form can be used for more complicated queries, using the `select` form (note, use either block or argument forms, not both):
461
+ #
462
+ # @example SELECT * WHERE \{ ?book dc:title ?title \} SERVICE ?l \{ ?book dc11:title ?title \}
463
+ # query1 = SPARQL::Client::Query.select.where([:book, RDF::Vocab::DC11.title, :title])
464
+ # Query.select.where([:book, RDF::Vocab::DC.title, :title]).service :l do |q|
465
+ # q.select.
466
+ # where([:book, RDF::Vocab::DC11.title, :title])
467
+ # end
468
+ #
469
+ # @example SELECT * WHERE \{ ?s ?p1 ?o1 . SERVICE SILENT ?l \{ ?s ?p2 ?o2 \} \}
470
+ # Query.select.where([:s, :p1, :o1]).
471
+ # service(:l, [:s, :p2, :o2], silent: true)
472
+ #
473
+ # @param [Array<RDF::Query::Pattern, Array>] patterns
474
+ # splat of zero or more patterns followed by zero or more queries.
475
+ # @param [Boolean] silent
476
+ # @yield [query]
477
+ # Yield form with or without argument; without an argument, evaluates within the query.
478
+ # @yieldparam [SPARQL::Client::Query] query used for adding select clauses.
479
+ # @return [Query]
480
+ # @see https://www.w3.org/TR/sparql11-federated-query/
481
+ def service(endpoint, *patterns, silent: false, &block)
482
+ service = {
483
+ endpoint: (endpoint.is_a?(Symbol) ? RDF::Query::Variable.new(endpoint) : endpoint),
484
+ silent: silent,
485
+ query: nil
486
+ }
487
+ (options[:services] ||= []) << service
488
+
489
+ if block_given?
490
+ raise ArgumentError, "#service requires either arguments or a block, not both." unless patterns.empty?
491
+ # Evaluate calls in a new query instance
492
+ query = self.class.select.where
493
+ case block.arity
494
+ when 1 then block.call(query)
495
+ else query.instance_eval(&block)
496
+ end
497
+ service[:query] = query
498
+ elsif patterns.all? {|p| p.is_a?(SPARQL::Client::Query)}
499
+ # With argument form, all must be patterns or queries
500
+ raise ArgumentError, "#service arguments are triple patterns or a query, not both." if patterns.length != 1
501
+ service[:query] = patterns.first
502
+ elsif patterns.all? {|p| p.is_a?(Array)}
503
+ # With argument form, all must be patterns, or queries
504
+ service[:query] = self.class.select.where(*patterns)
505
+ else
506
+ raise ArgumentError, "#service arguments are triple patterns a query, not both."
507
+ end
508
+
509
+ self
510
+ end
511
+
444
512
  ##
445
513
  # @example SELECT * WHERE \{ ?book dc:title ?title \} UNION \{ ?book dc11:title ?title \}
446
- # query.select.where([:book, RDF::Vocab::DC.title, :title]).
514
+ # Query.select.where([:book, RDF::Vocab::DC.title, :title]).
447
515
  # union([:book, RDF::Vocab::DC11.title, :title])
448
516
  #
449
517
  # @example SELECT * WHERE \{ ?book dc:title ?title \} UNION \{ ?book dc11:title ?title . FILTER(langmatches(lang(?title), 'EN'))\}
450
518
  # query1 = SPARQL::Client::Query.select.
451
519
  # where([:book, RDF::Vocab::DC11.title, :title]).
452
520
  # filter("langmatches(?title, 'en')")
453
- # query.select.where([:book, RDF::Vocab::DC.title, :title]).union(query1)
521
+ # Query.select.where([:book, RDF::Vocab::DC.title, :title]).union(query1)
454
522
  #
455
523
  # The block form can be used for more complicated queries, using the `select` form (note, use either block or argument forms, not both):
456
524
  #
457
525
  # @example SELECT * WHERE \{ ?book dc:title ?title \} UNION \{ ?book dc11:title ?title . FILTER(langmatches(lang(?title), 'EN'))\}
458
526
  # query1 = SPARQL::Client::Query.select.where([:book, RDF::Vocab::DC11.title, :title]).filter("langmatches(?title, 'en')")
459
- # query.select.where([:book, RDF::Vocab::DC.title, :title]).union do |q|
527
+ # Query.select.where([:book, RDF::Vocab::DC.title, :title]).union do |q|
460
528
  # q.select.
461
529
  # where([:book, RDF::Vocab::DC11.title, :title]).
462
530
  # filter("langmatches(?title, 'en')")
@@ -468,7 +536,7 @@ class SPARQL::Client
468
536
  # Yield form with or without argument; without an argument, evaluates within the query.
469
537
  # @yieldparam [SPARQL::Client::Query] query used for adding select clauses.
470
538
  # @return [Query]
471
- # @see https://www.w3.org/TR/sparql11-query/#optionals
539
+ # @see https://www.w3.org/TR/sparql11-query/#alternatives
472
540
  def union(*patterns, &block)
473
541
  options[:unions] ||= []
474
542
 
@@ -488,7 +556,7 @@ class SPARQL::Client
488
556
  # With argument form, all must be patterns, or queries
489
557
  options[:unions] << self.class.select.where(*patterns)
490
558
  else
491
- raise ArgumentError, "#union arguments are triple patters or queries, not both."
559
+ raise ArgumentError, "#union arguments are triple patterns or queries, not both."
492
560
  end
493
561
 
494
562
  self
@@ -496,20 +564,20 @@ class SPARQL::Client
496
564
 
497
565
  ##
498
566
  # @example SELECT * WHERE \{ ?book dc:title ?title . MINUS \{ ?book dc11:title ?title \} \}
499
- # query.select.where([:book, RDF::Vocab::DC.title, :title]).
567
+ # Query.select.where([:book, RDF::Vocab::DC.title, :title]).
500
568
  # minus([:book, RDF::Vocab::DC11.title, :title])
501
569
  #
502
570
  # @example SELECT * WHERE \{ ?book dc:title ?title MINUS \{ ?book dc11:title ?title . FILTER(langmatches(lang(?title), 'EN')) \} \}
503
571
  # query1 = SPARQL::Client::Query.select.
504
572
  # where([:book, RDF::Vocab::DC11.title, :title]).
505
573
  # filter("langmatches(?title, 'en')")
506
- # query.select.where([:book, RDF::Vocab::DC.title, :title]).minus(query1)
574
+ # Query.select.where([:book, RDF::Vocab::DC.title, :title]).minus(query1)
507
575
  #
508
576
  # The block form can be used for more complicated queries, using the `select` form (note, use either block or argument forms, not both):
509
577
  #
510
578
  # @example SELECT * WHERE \{ ?book dc:title ?title MINUS \{ ?book dc11:title ?title . FILTER(langmatches(lang(?title), 'EN'))\} \}
511
579
  # query1 = SPARQL::Client::Query.select.where([:book, RDF::Vocab::DC11.title, :title]).filter("langmatches(?title, 'en')")
512
- # query.select.where([:book, RDF::Vocab::DC.title, :title]).minus do |q|
580
+ # Query.select.where([:book, RDF::Vocab::DC.title, :title]).minus do |q|
513
581
  # q.select.
514
582
  # where([:book, RDF::Vocab::DC11.title, :title]).
515
583
  # filter("langmatches(?title, 'en')")
@@ -521,7 +589,7 @@ class SPARQL::Client
521
589
  # Yield form with or without argument; without an argument, evaluates within the query.
522
590
  # @yieldparam [SPARQL::Client::Query] query used for adding select clauses.
523
591
  # @return [Query]
524
- # @see https://www.w3.org/TR/sparql11-query/#optionals
592
+ # @see https://www.w3.org/TR/sparql11-query/#negation
525
593
  def minus(*patterns, &block)
526
594
  options[:minuses] ||= []
527
595
 
@@ -541,7 +609,7 @@ class SPARQL::Client
541
609
  # With argument form, all must be patterns, or queries
542
610
  options[:minuses] << self.class.select.where(*patterns)
543
611
  else
544
- raise ArgumentError, "#minus arguments are triple patters or queries, not both."
612
+ raise ArgumentError, "#minus arguments are triple patterns or queries, not both."
545
613
  end
546
614
 
547
615
  self
@@ -557,12 +625,12 @@ class SPARQL::Client
557
625
  #
558
626
  # @overload values(vars, *data)
559
627
  # @example single variable with multiple values
560
- # query.select
628
+ # Query.select
561
629
  # .where([:s, RDF::URI('http://purl.org/dc/terms/title'), :title])
562
630
  # .values(:title, "This title", "Another title")
563
631
  #
564
632
  # @example multiple variables with multiple values
565
- # query.select
633
+ # Query.select
566
634
  # .where([:s, RDF::URI('http://purl.org/dc/terms/title'), :title],
567
635
  # [:s, RDF.type, :type])
568
636
  # .values([:type, :title],
@@ -570,7 +638,7 @@ class SPARQL::Client
570
638
  # [RDF::URI('http://pcdm.org/models#Collection', 'Another title'])
571
639
  #
572
640
  # @example multiple variables with UNDEF
573
- # query.select
641
+ # Query.select
574
642
  # .where([:s, RDF::URI('http://purl.org/dc/terms/title'), :title],
575
643
  # [:s, RDF.type, :type])
576
644
  # .values([:type, :title],
@@ -626,7 +694,7 @@ class SPARQL::Client
626
694
 
627
695
  ##
628
696
  # @example ASK WHERE { ?s ?p ?o . FILTER(regex(?s, 'Abiline, Texas')) }
629
- # query.ask.where([:s, :p, :o]).filter("regex(?s, 'Abiline, Texas')")
697
+ # Query.ask.where([:s, :p, :o]).filter("regex(?s, 'Abiline, Texas')")
630
698
  # @return [Query]
631
699
  def filter(string)
632
700
  ((options[:filters] ||= []) << Filter.new(string)) if string and not string.empty?
@@ -796,6 +864,16 @@ class SPARQL::Client
796
864
  if options[:filters]
797
865
  buffer += options[:filters].map(&:to_s)
798
866
  end
867
+
868
+ if options[:services]
869
+ options[:services].each do |service|
870
+ buffer << 'SERVICE'
871
+ buffer << 'SILENT' if service[:silent]
872
+ buffer << SPARQL::Client.serialize_value(service[:endpoint])
873
+ buffer << service[:query].to_s_ggp
874
+ end
875
+ end
876
+
799
877
  if options[:values]
800
878
  vars = options[:values].first.map {|var| SPARQL::Client.serialize_value(var)}
801
879
  buffer << "VALUES (#{vars.join(' ')}) {"
data/lib/sparql/client.rb CHANGED
@@ -116,7 +116,14 @@ module SPARQL
116
116
  # Close the http connection when object is deallocated
117
117
  def self.finalize(klass)
118
118
  proc do
119
- klass.shutdown if klass.respond_to?(:shutdown)
119
+ if klass.respond_to?(:shutdown)
120
+ begin
121
+ # Attempt asynchronous shutdown
122
+ Thread.new {klass.shutdown}
123
+ rescue ThreadError
124
+ klass.shutdown
125
+ end
126
+ end
120
127
  end
121
128
  end
122
129
 
@@ -423,7 +430,13 @@ module SPARQL
423
430
  end
424
431
  RDF::Query::Solution.new(row)
425
432
  end
426
- RDF::Query::Solutions.new(solutions)
433
+ solns = RDF::Query::Solutions.new(solutions)
434
+
435
+ # Set variable names explicitly
436
+ if json.fetch('head', {}).has_key?('vars')
437
+ solns.variable_names = json['head']['vars'].map(&:to_sym)
438
+ end
439
+ solns
427
440
  end
428
441
  end
429
442
 
@@ -484,20 +497,23 @@ module SPARQL
484
497
  vars = tsv.shift.map {|h| h.sub(/^\?/, '')}
485
498
  solutions = RDF::Query::Solutions.new
486
499
  tsv.each do |row|
500
+ # Flesh out columns which may be missing
501
+ vars.each_with_index do |_, i|
502
+ row[i] ||= ""
503
+ end
487
504
  solution = RDF::Query::Solution.new
488
505
  row.each_with_index do |v, i|
489
- if !v.empty?
490
- term = RDF::NTriples.unserialize(v) || case v
491
- when /^\d+\.\d*[eE][+-]?[0-9]+$/ then RDF::Literal::Double.new(v)
492
- when /^\d*\.\d+[eE][+-]?[0-9]+$/ then RDF::Literal::Double.new(v)
493
- when /^\d*\.\d+$/ then RDF::Literal::Decimal.new(v)
494
- when /^\d+$/ then RDF::Literal::Integer.new(v)
495
- else
496
- RDF::Literal(v)
497
- end
498
- nodes[term.id] = term if term.is_a? RDF::Node
499
- solution[vars[i].to_sym] = term
506
+ term = case v
507
+ when "" then RDF::Literal("")
508
+ when /^\d+\.\d*[eE][+-]?[0-9]+$/ then RDF::Literal::Double.new(v)
509
+ when /^\d*\.\d+[eE][+-]?[0-9]+$/ then RDF::Literal::Double.new(v)
510
+ when /^\d*\.\d+$/ then RDF::Literal::Decimal.new(v)
511
+ when /^\d+$/ then RDF::Literal::Integer.new(v)
512
+ else
513
+ RDF::NTriples.unserialize(v) || RDF::Literal(v)
500
514
  end
515
+ nodes[term.id] = term if term.is_a? RDF::Node
516
+ solution[vars[i].to_sym] = term
501
517
  end
502
518
  solutions << solution
503
519
  end
@@ -506,45 +522,57 @@ module SPARQL
506
522
 
507
523
  ##
508
524
  # @param [String, IO, Nokogiri::XML::Node, REXML::Element] xml
525
+ # @param [Symbol] library (:nokogiri)
526
+ # One of :nokogiri or :rexml.
509
527
  # @return [<RDF::Query::Solutions>]
510
528
  # @see https://www.w3.org/TR/rdf-sparql-json-res/#results
511
- def self.parse_xml_bindings(xml, nodes = {})
529
+ def self.parse_xml_bindings(xml, nodes = {}, library: :nokogiri)
512
530
  xml.force_encoding(::Encoding::UTF_8) if xml.respond_to?(:force_encoding)
513
531
 
514
- if defined?(::Nokogiri)
532
+ if defined?(::Nokogiri) && library == :nokogiri
515
533
  xml = Nokogiri::XML(xml).root unless xml.is_a?(Nokogiri::XML::Document)
516
534
  case
517
- when boolean = xml.xpath("//sparql:boolean", XMLNS)[0]
518
- boolean.text == 'true'
519
- when results = xml.xpath("//sparql:results", XMLNS)[0]
520
- solutions = results.elements.map do |result|
521
- row = {}
522
- result.elements.each do |binding|
523
- name = binding.attr('name').to_sym
524
- value = binding.elements.first
525
- row[name] = parse_xml_value(value, nodes)
526
- end
527
- RDF::Query::Solution.new(row)
535
+ when boolean = xml.xpath("//sparql:boolean", XMLNS)[0]
536
+ boolean.text == 'true'
537
+ when results = xml.xpath("//sparql:results", XMLNS)[0]
538
+ solutions = results.elements.map do |result|
539
+ row = {}
540
+ result.elements.each do |binding|
541
+ name = binding.attr('name').to_sym
542
+ value = binding.elements.first
543
+ row[name] = parse_xml_value(value, nodes) if value
528
544
  end
529
- RDF::Query::Solutions.new(solutions)
545
+ RDF::Query::Solution.new(row)
546
+ end
547
+ solns = RDF::Query::Solutions.new(solutions)
548
+
549
+ # Set variable names explicitly
550
+ var_names = xml.xpath("//sparql:head/sparql:variable/@name", XMLNS)
551
+ solns.variable_names = var_names.map(&:to_s)
552
+ solns
530
553
  end
531
554
  else
532
555
  # REXML
533
556
  xml = REXML::Document.new(xml).root unless xml.is_a?(REXML::Element)
534
557
  case
535
- when boolean = xml.elements['boolean']
536
- boolean.text == 'true'
537
- when results = xml.elements['results']
538
- solutions = results.elements.map do |result|
539
- row = {}
540
- result.elements.each do |binding|
541
- name = binding.attributes['name'].to_sym
542
- value = binding.select { |node| node.kind_of?(::REXML::Element) }.first
543
- row[name] = parse_xml_value(value, nodes)
544
- end
545
- RDF::Query::Solution.new(row)
558
+ when boolean = xml.elements['boolean']
559
+ boolean.text == 'true'
560
+ when results = xml.elements['results']
561
+ solutions = results.elements.map do |result|
562
+ row = {}
563
+ result.elements.each do |binding|
564
+ name = binding.attributes['name'].to_sym
565
+ value = binding.select { |node| node.kind_of?(::REXML::Element) }.first
566
+ row[name] = parse_xml_value(value, nodes) if value
546
567
  end
547
- RDF::Query::Solutions.new(solutions)
568
+ RDF::Query::Solution.new(row)
569
+ end
570
+ solns = RDF::Query::Solutions.new(solutions)
571
+
572
+ # Set variable names explicitly
573
+ var_names = xml.elements['head'].elements.map {|e| e.attributes['name']}
574
+ solns.variable_names = var_names.map(&:to_sym)
575
+ solns
548
576
  end
549
577
  end
550
578
  end
@@ -578,7 +606,7 @@ module SPARQL
578
606
  # @return [RDF::Enumerable]
579
607
  def parse_rdf_serialization(response, **options)
580
608
  options = {content_type: response.content_type} unless options[:content_type]
581
- if reader = RDF::Reader.for(options)
609
+ if reader = RDF::Reader.for(**options)
582
610
  reader.new(response.body)
583
611
  else
584
612
  raise RDF::ReaderError, "no RDF reader was found for #{options}."
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sparql-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arto Bendiken
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-12-13 00:00:00.000000000 Z
13
+ date: 2023-07-23 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rdf
@@ -19,6 +19,9 @@ dependencies:
19
19
  - - "~>"
20
20
  - !ruby/object:Gem::Version
21
21
  version: '3.2'
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 3.2.11
22
25
  type: :runtime
23
26
  prerelease: false
24
27
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,6 +29,9 @@ dependencies:
26
29
  - - "~>"
27
30
  - !ruby/object:Gem::Version
28
31
  version: '3.2'
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 3.2.11
29
35
  - !ruby/object:Gem::Dependency
30
36
  name: net-http-persistent
31
37
  requirement: !ruby/object:Gem::Requirement
@@ -35,7 +41,7 @@ dependencies:
35
41
  version: '4.0'
36
42
  - - ">="
37
43
  - !ruby/object:Gem::Version
38
- version: 4.0.1
44
+ version: 4.0.2
39
45
  type: :runtime
40
46
  prerelease: false
41
47
  version_requirements: !ruby/object:Gem::Requirement
@@ -45,7 +51,7 @@ dependencies:
45
51
  version: '4.0'
46
52
  - - ">="
47
53
  - !ruby/object:Gem::Version
48
- version: 4.0.1
54
+ version: 4.0.2
49
55
  - !ruby/object:Gem::Dependency
50
56
  name: rdf-spec
51
57
  requirement: !ruby/object:Gem::Requirement
@@ -80,14 +86,14 @@ dependencies:
80
86
  requirements:
81
87
  - - "~>"
82
88
  - !ruby/object:Gem::Version
83
- version: '3.10'
89
+ version: '3.12'
84
90
  type: :development
85
91
  prerelease: false
86
92
  version_requirements: !ruby/object:Gem::Requirement
87
93
  requirements:
88
94
  - - "~>"
89
95
  - !ruby/object:Gem::Version
90
- version: '3.10'
96
+ version: '3.12'
91
97
  - !ruby/object:Gem::Dependency
92
98
  name: rspec-its
93
99
  requirement: !ruby/object:Gem::Requirement
@@ -150,10 +156,15 @@ files:
150
156
  - lib/sparql/client/repository.rb
151
157
  - lib/sparql/client/update.rb
152
158
  - lib/sparql/client/version.rb
153
- homepage: https://github.com/ruby-rdf/sparql-client/
159
+ homepage: https://github.com/ruby-rdf/sparql-client
154
160
  licenses:
155
161
  - Unlicense
156
- metadata: {}
162
+ metadata:
163
+ documentation_uri: https://ruby-rdf.github.io/sparql-client
164
+ bug_tracker_uri: https://github.com/ruby-rdf/sparql-client/issues
165
+ homepage_uri: https://github.com/ruby-rdf/sparql-client
166
+ mailing_list_uri: https://lists.w3.org/Archives/Public/public-rdf-ruby/
167
+ source_code_uri: https://github.com/ruby-rdf/sparql-client
157
168
  post_install_message:
158
169
  rdoc_options: []
159
170
  require_paths:
@@ -169,7 +180,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
169
180
  - !ruby/object:Gem::Version
170
181
  version: '0'
171
182
  requirements: []
172
- rubygems_version: 3.3.3
183
+ rubygems_version: 3.3.26
173
184
  signing_key:
174
185
  specification_version: 4
175
186
  summary: SPARQL client for RDF.rb.