Skip to content

Commit

Permalink
Finish 3.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
gkellogg committed Dec 3, 2018
2 parents 2abd569 + b346727 commit ff9d21c
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 64 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -10,3 +10,4 @@ doc
coverage
.bundle
/.byebug_history
.ruby-version
94 changes: 58 additions & 36 deletions README.md
Expand Up @@ -25,70 +25,92 @@ This is a [Ruby][] implementation of a [SPARQL][] client for [RDF.rb][].
## Examples

### Querying a remote SPARQL endpoint
require 'sparql/client'

sparql = SPARQL::Client.new("http://dbpedia.org/sparql")
```ruby
require 'sparql/client'
sparql = SPARQL::Client.new("http://dbpedia.org/sparql")
```
### Querying a remote SPARQL endpoint with a specified default graph

### Querying a `RDF::Repository` instance
```ruby
require 'sparql/client'
sparql = SPARQL::Client.new("http://dbpedia.org/sparql", { :graph => "http://dbpedia.org" })
```

require 'rdf/trig'
repository = RDF::Repository.load("http://example/dataset.trig")

sparql = SPARQL::Client.new(repository)
### Querying a `RDF::Repository` instance

### Executing a boolean query and outputting the result
```ruby
require 'rdf/trig'
repository = RDF::Repository.load("http://example/dataset.trig")
sparql = SPARQL::Client.new(repository)
```

# ASK WHERE { ?s ?p ?o }
result = sparql.ask.whether([:s, :p, :o]).true?
### Executing a boolean query and outputting the result

puts result.inspect #=> true or false
```ruby
# ASK WHERE { ?s ?p ?o }
result = sparql.ask.whether([:s, :p, :o]).true?
puts result.inspect #=> true or false
```

### Executing a tuple query and iterating over the returned solutions

# SELECT * WHERE { ?s ?p ?o } OFFSET 100 LIMIT 10
query = sparql.select.where([:s, :p, :o]).offset(100).limit(10)
```ruby
# SELECT * WHERE { ?s ?p ?o } OFFSET 100 LIMIT 10
query = sparql.select.where([:s, :p, :o]).offset(100).limit(10)

query.each_solution do |solution|
puts solution.inspect
end
query.each_solution do |solution|
puts solution.inspect
end
```

### Executing a graph query and iterating over the returned statements

# CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o } LIMIT 10
query = sparql.construct([:s, :p, :o]).where([:s, :p, :o]).limit(10)

query.each_statement do |statement|
puts statement.inspect
end
```ruby
# CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o } LIMIT 10
query = sparql.construct([:s, :p, :o]).where([:s, :p, :o]).limit(10)

query.each_statement do |statement|
puts statement.inspect
end
```

### Executing an arbitrary textual SPARQL query string

result = sparql.query("ASK WHERE { ?s ?p ?o }")
```ruby
result = sparql.query("ASK WHERE { ?s ?p ?o }")

puts result.inspect #=> true or false
puts result.inspect #=> true or false
```

### Inserting data into a graph

# INSERT DATA { <http://example.org/jhacker> <http://xmlns.com/foaf/0.1/name> "J. Random Hacker" .}
data = RDF::Graph.new do |graph|
graph << [RDF::URI('http://example.org/jhacker'), RDF::Vocab::FOAF.name, "J. Random Hacker"]
end
sparql.insert_data(data)
```ruby
# INSERT DATA { <http://example.org/jhacker> <http://xmlns.com/foaf/0.1/name> "J. Random Hacker" .}
data = RDF::Graph.new do |graph|
graph << [RDF::URI('http://example.org/jhacker'), RDF::Vocab::FOAF.name, "J. Random Hacker"]
end
sparql.insert_data(data)
```

### Deleting data from a graph

# DELETE DATA { <http://example.org/jhacker> <http://xmlns.com/foaf/0.1/name> "J. Random Hacker" .}
data = RDF::Graph.new do |graph|
graph << [RDF::URI('http://example.org/jhacker'), RDF::Vocab::FOAF.name, "J. Random Hacker"]
end
sparql.delete_data(data)
```ruby
# DELETE DATA { <http://example.org/jhacker> <http://xmlns.com/foaf/0.1/name> "J. Random Hacker" .}
data = RDF::Graph.new do |graph|
graph << [RDF::URI('http://example.org/jhacker'), RDF::Vocab::FOAF.name, "J. Random Hacker"]
end
sparql.delete_data(data)
```

## Documentation

* {SPARQL::Client}
* {SPARQL::Client::Query}
* {SPARQL::Client::Repository}
* {SPARQL::Client::Update}
* [SPARQL::Client](https://www.rubydoc.info/github/ruby-rdf/sparql-client/SPARQL/Client)
* [SPARQL::Client::Query](https://www.rubydoc.info/github/ruby-rdf/sparql-client/SPARQL/Client/Query)
* [SPARQL::Client::Repository](https://www.rubydoc.info/github/ruby-rdf/sparql-client/SPARQL/Client/Repository)
* [SPARQL::Client::Update](https://www.rubydoc.info/github/ruby-rdf/sparql-client/SPARQL/Client/Update)

## Dependencies

Expand Down
2 changes: 1 addition & 1 deletion VERSION
@@ -1 +1 @@
3.0.0
3.0.1
46 changes: 40 additions & 6 deletions lib/sparql/client.rb
Expand Up @@ -40,7 +40,7 @@ class ServerError < StandardError; end
'*/*;q=0.1'
].join(', ').freeze
GRAPH_ALL = (
RDF::Format.content_types.keys +
RDF::Format.content_types.keys +
['*/*;q=0.1']
).join(', ').freeze

Expand Down Expand Up @@ -543,7 +543,7 @@ def parse_rdf_serialization(response, options = {})
if reader = RDF::Reader.for(options)
reader.new(response.body)
else
raise RDF::ReaderError, "no suitable rdf reader was found."
raise RDF::ReaderError, "no RDF reader was found for #{options}."
end
end

Expand Down Expand Up @@ -701,8 +701,8 @@ def request(query, headers = {}, &block)
if response.kind_of? Net::HTTPRedirection
response = @http.request(::URI.parse(response['location']), request)
else
return block_given? ? block.call(response) : response
end
return block_given? ? block.call(response) : response
end
end
raise ServerError, "Infinite redirect at #{url}. Redirected more than 10 times."
end
Expand All @@ -726,6 +726,7 @@ def request_method(query)
def make_get_request(query, headers = {})
url = self.url.dup
url.query_values = (url.query_values || {}).merge(:query => query.to_s)
set_url_default_graph url unless @options[:graph].nil?
request = Net::HTTP::Get.new(url.request_uri, self.headers.merge(headers))
request
end
Expand All @@ -740,23 +741,56 @@ def make_get_request(query, headers = {})
# @see http://www.w3.org/TR/sparql11-protocol/#query-via-post-urlencoded
def make_post_request(query, headers = {})
if @alt_endpoint.nil?
endpoint = url.request_uri
url = self.url.dup
set_url_default_graph url unless @options[:graph].nil?
endpoint = url.request_uri
else
endpoint = @alt_endpoint
end

request = Net::HTTP::Post.new(endpoint, self.headers.merge(headers))
case (self.options[:protocol] || DEFAULT_PROTOCOL).to_s
when '1.1'
request['Content-Type'] = 'application/sparql-' + (@op || :query).to_s
request.body = query.to_s
when '1.0'
request.set_form_data((@op || :query) => query.to_s)
form_data = {(@op || :query) => query.to_s}
form_data.merge!(
{:'default-graph-uri' => @options[:graph]}
) if !@options[:graph].nil? && (@op.eql? :query)
form_data.merge!(
{:'using-graph-uri' => @options[:graph]}
) if !@options[:graph].nil? && (@op.eql? :update)
request.set_form_data(form_data)
else
raise ArgumentError, "unknown SPARQL protocol version: #{self.options[:protocol].inspect}"
end
request
end

##
# Setup url query parameter to use a specified default graph
#
# @see https://www.w3.org/TR/sparql11-protocol/#query-operation
# @see https://www.w3.org/TR/sparql11-protocol/#update-operation
def set_url_default_graph url
if @options[:graph].is_a? Array
graphs = @options[:graph].map {|graph|
CGI::escape(graph)
}
else
graphs = CGI::escape(@options[:graph])
end
case @op
when :query
url.query_values = (url.query_values || {})
.merge(:'default-graph-uri' => graphs)
when :update
url.query_values = (url.query_values || {})
.merge(:'using-graph-uri' => graphs)
end
end

# 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.
class QueryElement
attr_reader :elements
Expand Down
42 changes: 32 additions & 10 deletions lib/sparql/client/query.rb
@@ -1,4 +1,4 @@
module SPARQL; class Client
class SPARQL::Client
##
# A SPARQL query builder.
#
Expand Down Expand Up @@ -366,16 +366,38 @@ def slice(start, length)
end

##
# @example PREFIX dc: <http://purl.org/dc/elements/1.1/> PREFIX foaf: <http://xmlns.com/foaf/0.1/> SELECT * WHERE \{ ?s ?p ?o . \}
# query.select.
# prefix(dc: RDF::URI("http://purl.org/dc/elements/1.1/")).
# prefix(foaf: RDF::URI("http://xmlns.com/foaf/0.1/")).
# where([:s, :p, :o])
# @overload prefix(prefix: uri)
# @example PREFIX dc: <http://purl.org/dc/elements/1.1/> PREFIX foaf: <http://xmlns.com/foaf/0.1/> SELECT * WHERE \{ ?s ?p ?o . \}
# query.select.
# prefix(dc: RDF::URI("http://purl.org/dc/elements/1.1/")).
# prefix(foaf: RDF::URI("http://xmlns.com/foaf/0.1/")).
# where([:s, :p, :o])
#
# @return [Query]
# @param [RDF::URI] uri
# @param [Symbol, String] prefix
# @return [Query]
#
# @overload prefix(string)
# @example PREFIX dc: <http://purl.org/dc/elements/1.1/> PREFIX foaf: <http://xmlns.com/foaf/0.1/> SELECT * WHERE \{ ?s ?p ?o . \}
# query.select.
# prefix("dc: <http://purl.org/dc/elements/1.1/>").
# prefix("foaf: <http://xmlns.com/foaf/0.1/>").
# where([:s, :p, :o])
#
# @param [string] string
# @return [Query]
# @see http://www.w3.org/TR/sparql11-query/#prefNames
def prefix(string)
(options[:prefixes] ||= []) << string
def prefix(val)
options[:prefixes] ||= []
if val.kind_of? String
options[:prefixes] << val
elsif val.kind_of? Hash
val.each do |k, v|
options[:prefixes] << "#{k}: <#{v}>"
end
else
raise ArgumentError, "prefix must be a kind of String or a Hash"
end
self
end

Expand Down Expand Up @@ -823,4 +845,4 @@ def to_s
end
end
end
end; end
end
4 changes: 2 additions & 2 deletions lib/sparql/client/repository.rb
@@ -1,4 +1,4 @@
module SPARQL; class Client
class SPARQL::Client
##
# A read-only repository view of a SPARQL endpoint.
#
Expand Down Expand Up @@ -345,4 +345,4 @@ def insert_statement(statement)
end

end
end; end
end
4 changes: 2 additions & 2 deletions lib/sparql/client/version.rb
@@ -1,4 +1,4 @@
module SPARQL; class Client
class SPARQL::Client
module VERSION
FILE = File.expand_path('../../../../VERSION', __FILE__)
MAJOR, MINOR, TINY, EXTRA = File.read(FILE).chomp.split('.')
Expand All @@ -16,4 +16,4 @@ def self.to_str() STRING end
# @return [Array(Integer, Integer, Integer)]
def self.to_a() [MAJOR, MINOR, TINY] end
end
end; end
end
5 changes: 0 additions & 5 deletions sparql-client.gemspec
Expand Up @@ -20,12 +20,7 @@ Gem::Specification.new do |gem|
gem.platform = Gem::Platform::RUBY
gem.files = %w(AUTHORS CREDITS README.md UNLICENSE VERSION) + Dir.glob('lib/**/*.rb')
gem.bindir = %q(bin)
gem.executables = %w()
gem.default_executable = gem.executables.first
gem.require_paths = %w(lib)
gem.extensions = %w()
gem.test_files = %w()
gem.has_rdoc = false

gem.required_ruby_version = '>= 2.2.2'
gem.requirements = []
Expand Down
45 changes: 45 additions & 0 deletions spec/client_spec.rb
Expand Up @@ -227,6 +227,51 @@ def response(header)
end
end

context "with multiple Graphs" do
let(:get_graph_client){ SPARQL::Client.new('http://data.linkedmdb.org/sparql', {:method => 'get', :graph => 'http://data.linkedmdb.org/graph1'}) }
let(:post_graph_client10){ SPARQL::Client.new('http://data.linkedmdb.org/sparql', {:method => 'post', :graph => 'http://data.linkedmdb.org/graph1', :protocol => '1.0'}) }
let(:post_graph_client11){ SPARQL::Client.new('http://data.linkedmdb.org/sparql', {:method => 'post', :graph => 'http://data.linkedmdb.org/graph1', :protocol => '1.1'}) }

it "should create 'query via GET' requests" do
WebMock.stub_request(:get, 'http://data.linkedmdb.org/sparql?default-graph-uri=http%3A%2F%2Fdata.linkedmdb.org%2Fgraph1&query=SELECT%20%3Fkb%20WHERE%20%7B%20%3Fkb%20%3Chttp%3A%2F%2Fdata.linkedmdb.org%2Fresource%2Fmovie%2Factor_name%3E%20%22Kevin%20Bacon%22%20.%20%7D').
to_return(:body => '{}', :status => 200, :headers => { 'Content-Type' => 'application/sparql-results+json'})
get_graph_client.query(select_query)
expect(WebMock).to have_requested(:get, "http://data.linkedmdb.org/sparql?default-graph-uri=http%3A%2F%2Fdata.linkedmdb.org%2Fgraph1&query=SELECT%20%3Fkb%20WHERE%20%7B%20%3Fkb%20%3Chttp%3A%2F%2Fdata.linkedmdb.org%2Fresource%2Fmovie%2Factor_name%3E%20%22Kevin%20Bacon%22%20.%20%7D")
end

it "should create 'query via URL-encoded Post' requests" do
WebMock.stub_request(:post, 'http://data.linkedmdb.org/sparql?default-graph-uri=http%3A%2F%2Fdata.linkedmdb.org%2Fgraph1').
to_return(:body => '{}', :status => 200, :headers => { 'Content-Type' => 'application/sparql-results+json'})
post_graph_client10.query(select_query)
expect(WebMock).to have_requested(:post, "http://data.linkedmdb.org/sparql?default-graph-uri=http%3A%2F%2Fdata.linkedmdb.org%2Fgraph1").
with(:body => "query=SELECT+%3Fkb+WHERE+%7B+%3Fkb+%3Chttp%3A%2F%2Fdata.linkedmdb.org%2Fresource%2Fmovie%2Factor_name%3E+%22Kevin+Bacon%22+.+%7D&default-graph-uri=http%3A%2F%2Fdata.linkedmdb.org%2Fgraph1")
end

it "should create 'query via Post directly' requests" do
WebMock.stub_request(:post, 'http://data.linkedmdb.org/sparql?default-graph-uri=http%3A%2F%2Fdata.linkedmdb.org%2Fgraph1').
to_return(:body => '{}', :status => 200, :headers => { 'Content-Type' => 'application/sparql-results+json'})
post_graph_client11.query(select_query)
expect(WebMock).to have_requested(:post, "http://data.linkedmdb.org/sparql?default-graph-uri=http%3A%2F%2Fdata.linkedmdb.org%2Fgraph1").
with(:body => select_query)
end

it "should create requests for 'update via URL-encoded POST'" do
WebMock.stub_request(:post, 'http://data.linkedmdb.org/sparql?using-graph-uri=http%3A%2F%2Fdata.linkedmdb.org%2Fgraph1').
to_return(:body => '{}', :status => 200)
post_graph_client10.update(update_query)
expect(WebMock).to have_requested(:post, "http://data.linkedmdb.org/sparql?using-graph-uri=http%3A%2F%2Fdata.linkedmdb.org%2Fgraph1").
with(:body => "update=DELETE+%7B%3Fs+%3Fp+%3Fo%7D+WHERE+%7B%7D&using-graph-uri=http%3A%2F%2Fdata.linkedmdb.org%2Fgraph1")
end

it "should create requests for 'update via POST directly'" do
WebMock.stub_request(:post, 'http://data.linkedmdb.org/sparql?using-graph-uri=http%3A%2F%2Fdata.linkedmdb.org%2Fgraph1').
to_return(:body => '{}', :status => 200)
post_graph_client11.update(update_query)
expect(WebMock).to have_requested(:post, "http://data.linkedmdb.org/sparql?using-graph-uri=http%3A%2F%2Fdata.linkedmdb.org%2Fgraph1").
with(:body => update_query)
end
end

context "Error response" do
{
"bad request" => {status: 400, error: SPARQL::Client::MalformedQuery },
Expand Down

0 comments on commit ff9d21c

Please sign in to comment.