Skip to content

Commit

Permalink
Finish 3.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
gkellogg committed Dec 29, 2021
2 parents 111cb8b + e8b9b20 commit bf2fb1e
Show file tree
Hide file tree
Showing 144 changed files with 3,351 additions and 576 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/ci.yml
Expand Up @@ -15,14 +15,15 @@ jobs:
runs-on: ubuntu-latest
env:
CI: true
ALLOW_FAILURES: ${{ endsWith(matrix.ruby, 'head') }}
strategy:
fail-fast: false
matrix:
ruby:
- 2.5
- 2.6
- 2.7
- 3.0
- 3.1
- ruby-head
- jruby
steps:
Expand All @@ -35,7 +36,7 @@ jobs:
- name: Install dependencies
run: bundle install --jobs 4 --retry 3
- name: Run tests
run: bundle exec rspec spec
run: ruby --version; bundle exec rspec spec || $ALLOW_FAILURES
- name: Coveralls GitHub Action
uses: coverallsapp/github-action@v1.1.2
if: "matrix.ruby == '3.0'"
Expand Down
51 changes: 29 additions & 22 deletions README.md
Expand Up @@ -23,8 +23,7 @@ This is a [Ruby][] implementation of [SPARQL][] for [RDF.rb][].
* Compatible with any [Rack][] or [Sinatra][] application and any Rack-based framework.
* Helper method for describing [SPARQL Service Description][SSD]
* Implementation Report: {file:etc/earl.html EARL}
* Compatible with Ruby >= 2.2.2.
* Compatible with older Ruby versions with the help of the [Backports][] gem.
* Compatible with Ruby >= 2.6.
* Supports Unicode query strings both on all versions of Ruby.
* Provisional support for [SPARQL-star][].

Expand Down Expand Up @@ -248,27 +247,27 @@ a full set of RDF formats.
### Querying a repository with a SPARQL query

queryable = RDF::Repository.load("etc/doap.ttl")
sse = SPARQL.parse("SELECT * WHERE { ?s ?p ?o }")
queryable.query(sse) do |result|
query = SPARQL.parse("SELECT * WHERE { ?s ?p ?o }")
queryable.query(query) do |result|
result.inspect
end

### Executing a SPARQL query against a repository

queryable = RDF::Repository.load("etc/doap.ttl")
sse = SPARQL.parse("SELECT * WHERE { ?s ?p ?o }")
sse.execute(queryable) do |result|
query = SPARQL.parse("SELECT * WHERE { ?s ?p ?o }")
query.execute(queryable) do |result|
result.inspect
end

### Updating a repository

queryable = RDF::Repository.load("etc/doap.ttl")
sse = SPARQL.parse(%(
update = SPARQL.parse(%(
PREFIX doap: <http://usefulinc.com/ns/doap#>
INSERT DATA { <https://rubygems> doap:implements <http://www.w3.org/TR/sparql11-update/>}
), update: true)
sse.execute(queryable)
update.execute(queryable)

### Rendering solutions as JSON, XML, CSV, TSV or HTML
queryable = RDF::Repository.load("etc/doap.ttl")
Expand All @@ -277,8 +276,13 @@ a full set of RDF formats.

### Parsing a SPARQL query string to SSE

sse = SPARQL.parse("SELECT * WHERE { ?s ?p ?o }")
sse.to_sxp #=> (bgp (triple ?s ?p ?o))
query = SPARQL.parse("SELECT * WHERE { ?s ?p ?o }")
query.to_sxp #=> (bgp (triple ?s ?p ?o))

### Parsing a SSE to SPARQL query or update string to SPARQL

query = SPARQL::Algebra.parse(%{(bgp (triple ?s ?p ?o))})
sparql = query.to_sparql #=> "SELECT * WHERE { ?s ?p ?o }"

### Command line processing

Expand All @@ -289,6 +293,10 @@ a full set of RDF formats.
sparql parse etc/input.rq
sparql parse -e "SELECT * WHERE { ?s ?p ?o }"

# Generate SPARQL Query from SSE
sparql parse --sse etc/input.sse --format sparql
sparql parse --sse --format sparql -e "(dataset (<http://usefulinc.com/ns/doap>) (bgp (triple ?s ?p ?o))))"

# Run query using SSE input
sparql execute --dataset etc/doap.ttl --sse etc/input.sse
sparql execute --sse -e "(dataset (<etc/doap.ttl>) (bgp (triple ?s ?p ?o))))"
Expand Down Expand Up @@ -368,19 +376,19 @@ Full documentation available on [Rubydoc.info][SPARQL doc]

## Dependencies

* [Ruby](https://ruby-lang.org/) (>= 2.2.2)
* [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.0)
* [SPARQL::Client](https://rubygems.org/gems/sparql-client) (~> 3.0)
* [SXP](https://rubygems.org/gems/sxp) (~> 1.0)
* [Builder](https://rubygems.org/gems/builder) (>= 3.0.0)
* [JSON](https://rubygems.org/gems/json) (>= 1.8.2)
* Soft dependency on [Linked Data][] (>= 3.0)
* Soft dependency on [Nokogiri](https://rubygems.org/gems/nokogiri) (>= 1.7)
* [Ruby](https://ruby-lang.org/) (>= 2.6)
* [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.2)
* [SPARQL::Client](https://rubygems.org/gems/sparql-client) (~> 3.1)
* [SXP](https://rubygems.org/gems/sxp) (~> 1.2)
* [Builder](https://rubygems.org/gems/builder) (~> 3.2)
* [JSON](https://rubygems.org/gems/json) (~> 2.6)
* Soft dependency on [Linked Data][] (>= 3.1)
* Soft dependency on [Nokogiri](https://rubygems.org/gems/nokogiri) (~> 1.12)
Falls back to REXML for XML parsing Builder for XML serializing. Nokogiri is much more efficient
* Soft dependency on [Equivalent XML](https://rubygems.org/gems/equivalent-xml) (>= 0.3.0)
* Soft dependency on [Equivalent XML](https://rubygems.org/gems/equivalent-xml) (>= 0.6)
Equivalent XML performs more efficient comparisons of XML Literals when Nokogiri is included
* Soft dependency on [Rack][] (>= 2.0)
* Soft dependency on [Sinatra][] (>= 2.0)
* Soft dependency on [Rack][] (~> 2.2)
* Soft dependency on [Sinatra][] (~> 2.1)

## Installation

Expand Down Expand Up @@ -450,7 +458,6 @@ A copy of the [SPARQL 1.0 tests][] and [SPARQL 1.1 tests][] are also included in
[RDF.rb]: https://rubydoc.info/github/ruby-rdf/rdf
[RDF-star]: https://w3c.github.io/rdf-star/rdf-star-cg-spec.html
[SPARQL-star]: https://w3c.github.io/rdf-star/rdf-star-cg-spec.html#sparql-query-language
[Backports]: https://rubygems.org/gems/backports
[Linked Data]: https://rubygems.org/gems/linkeddata
[SPARQL doc]: https://rubydoc.info/github/ruby-rdf/sparql/frames
[SPARQL XML]: https://www.w3.org/TR/rdf-sparql-XMLres/
Expand Down
2 changes: 1 addition & 1 deletion VERSION
@@ -1 +1 @@
3.1.8
3.2.0
17 changes: 13 additions & 4 deletions bin/sparql
Expand Up @@ -45,9 +45,18 @@ def run(input, **options)
SPARQL::Grammar.parse(input, **options)
end

puts ("\nSSE:\n" + query.to_sse) if options[:debug] || options[:to_sse]
puts ("\nSSE:\n" + query.to_sse) if options[:debug]

unless options[:to_sse]
if options[:parse_only]
case options[:format]
when :sparql
puts ("\nSPARQL:\n" + query.to_sparql)
when nil, :sse
puts ("\nSSE:\n" + query.to_sse)
else
$stderr.puts "Unknown output format for parsing: #{options[:format]}. Use 'sse' or 'sparql'"
end
else
res = query.execute(options[:dataset], logger: options[:logger])
display_results(res, **options)
end
Expand Down Expand Up @@ -98,7 +107,7 @@ def usage
puts " --dataset: File containing RDF graph or dataset"
puts " --debug: Display detailed debug output"
puts " --execute,-e: Use option argument as the SPARQL input if no query-file given"
puts " --format: Output format for results"
puts " --format: Output format for results (json, xml, csv, tsv, html, sparql, sse, or another RDF format)"
puts " --port,-p Port on which to run server; defaults to 9292"
puts " --sse: Query input is in SSE format"
puts " --update: Process query as a SPARQL Update"
Expand Down Expand Up @@ -151,7 +160,7 @@ end

case cmd
when 'execute', 'parse'
options[:to_sse] = true if cmd == 'parse'
options[:parse_only] = true if cmd == 'parse'
input ||= ARGV.empty? ? $stdin.read : RDF::Util::File.open_file(ARGV.first).read
run(input, **options)
when 'query'
Expand Down
126 changes: 93 additions & 33 deletions lib/sparql/algebra/extensions.rb
Expand Up @@ -43,6 +43,15 @@ def optimize(**options)
def deep_dup
dup
end

##
#
# Returns a partial SPARQL grammar for this term.
#
# @return [String]
def to_sparql(**options)
to_sxp(**options)
end
end

##
Expand All @@ -56,6 +65,16 @@ def to_sxp_bin
map {|x| x.to_sxp_bin}
end

##
#
# Returns a partial SPARQL grammar for this array.
#
# @param [String] delimiter (" ")
# @return [String]
def to_sparql(delimiter: " ", **options)
map {|e| e.to_sparql(**options)}.join(delimiter)
end

##
# Evaluates the array using the given variable `bindings`.
#
Expand Down Expand Up @@ -216,15 +235,6 @@ def deep_dup
##
# Extensions for Ruby's `Hash` class.
class Hash
##
# Returns the SXP representation of this object, defaults to `self`.
#
# @return [String]
def to_sxp_bin
to_a.to_sxp_bin
end
def to_sxp; to_sxp_bin; end

##
# A duplicate of this hash.
#
Expand Down Expand Up @@ -278,24 +288,20 @@ def vars
# @see SPARQL::Algebra::Expression#optimize
def optimize(**options)
optimized = self.deep_dup
optimized.lexical = nil if optimized.respond_to?(:lexical=)
optimized
#optimized.lexical = nil if optimized.respond_to?(:lexical=)
#optimized
end
end # RDF::Term

class RDF::Literal::Double
##
# Returns the SXP representation of this object.
#
# Returns a partial SPARQL grammar for this term.
#
# @return [String]
def to_sxp
case
when nan? then 'nan.0'
when infinite? then (infinite? > 0 ? '+inf.0' : '-inf.0')
else canonicalize.to_s.downcase
end
def to_sparql(**options)
to_sxp(**options)
end
end
end # RDF::Term


# Override RDF::Queryable to execute against SPARQL::Algebra::Query elements as well as RDF::Query and RDF::Pattern
module RDF::Queryable
Expand Down Expand Up @@ -343,6 +349,15 @@ def query(pattern, **options, &block)
query_without_sparql(pattern, **options, &block)
end
end

##
#
# Returns a partial SPARQL grammar for this term.
#
# @return [String]
def to_sparql(**options)
raise NotImplementedError, "SPARQL::Algebra '#{first}' operator not implemented"
end
end

class RDF::Statement
Expand All @@ -361,9 +376,29 @@ def to_sxp_bin
##
# Returns an S-Expression (SXP) representation
#
# @param [Hash{Symbol => RDF::URI}] prefixes (nil)
# @param [RDF::URI] base_uri (nil)
# @return [String]
def to_sxp
to_sxp_bin.to_sxp
def to_sxp(prefixes: nil, base_uri: nil)
to_sxp_bin.to_sxp(prefixes: prefixes, base_uri: base_uri)
end

##
#
# Returns a partial SPARQL grammar for this term.
#
# @param [Boolean] as_statement (false) serialize as < ... >, otherwise TRIPLE(...)
# @return [String]
def to_sparql(as_statement: false, **options)
return "TRIPLE(#{to_triple.to_sparql(as_statement: true, **options)})" unless as_statement

to_triple.map do |term|
if term.is_a?(::RDF::Statement)
"<<" + term.to_sparql(**options) + ">>"
else
term.to_sparql(**options)
end
end.join(" ") + " ."
end

##
Expand Down Expand Up @@ -411,6 +446,19 @@ def to_sxp_bin
end
end

##
#
# Returns a partial SPARQL grammar for this term.
#
# @param [Boolean] top_level (true)
# Treat this as a top-level, generating SELECT ... WHERE {}
# @return [String]
def to_sparql(top_level: true, **options)
str = @patterns.map { |e| e.to_sparql(as_statement: true, top_level: false, **options) }.join("\n")
str = "GRAPH #{graph_name.to_sparql(**options)} {\n#{str}\n}\n" if graph_name
top_level ? SPARQL::Algebra::Operator.to_sparql(str, **options) : str
end

##
# Binds the pattern to a solution, making it no longer variable if all variables are resolved to bound variables
#
Expand Down Expand Up @@ -462,11 +510,11 @@ def vars
def optimize!(**options)
@patterns = @patterns.map do |pattern|
components = pattern.to_quad.map do |term|
if term.respond_to?(:lexical=)
term.dup.instance_eval {@lexical = nil; self}
else
#if term.respond_to?(:lexical=)
# term.dup.instance_eval {@lexical = nil; self}
#else
term
end
#end
end
RDF::Query::Pattern.from(components, **pattern.options)
end
Expand Down Expand Up @@ -530,11 +578,15 @@ def optimize(**options)
self
end

# Display variable as SXP
# @return [Array]
def to_sxp
prefix = distinguished? ? (existential? ? '$' : '?') : (existential? ? '$$' : '??')
unbound? ? "#{prefix}#{name}".to_sym.to_sxp : ["#{prefix}#{name}".to_sym, value].to_sxp
##
#
# Returns a partial SPARQL grammar for this term.
#
# The Non-distinguished form (`??xxx`) is not part of the grammar, so replace with a blank-node
#
# @return [String]
def to_sparql(**options)
self.distinguished? ? super : "_:_nd#{self.name}"
end
end # RDF::Query::Variable

Expand Down Expand Up @@ -576,5 +628,13 @@ class RDF::Query::Solution
def to_sxp_bin
to_a.to_sxp_bin
end
def to_sxp; to_sxp_bin; end

# Transform Solution into an SXP
#
# @param [Hash{Symbol => RDF::URI}] prefixes (nil)
# @param [RDF::URI] base_uri (nil)
# @return [String]
def to_sxp(prefixes: nil, base_uri: nil)
to_sxp_bin.to_sxp(prefixes: prefixes, base_uri: base_uri)
end
end # RDF::Query::Solution

0 comments on commit bf2fb1e

Please sign in to comment.