From 3c6dc896a9a29535df0e7354a22ab8d080a023e7 Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Tue, 2 Feb 2021 14:03:33 -0800 Subject: [PATCH 1/3] Minor updates. --- .github/workflows/ci.yml | 4 +- README.md | 2 +- etc/doap.ttl | 9 +-- etc/earl.ttl | 6 +- etc/template.haml | 5 ++ script/tc | 136 ++++++++++++++++++++++++--------------- shacl.gemspec | 2 +- 7 files changed, 98 insertions(+), 66 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00077ac..866a35f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,8 +24,8 @@ jobs: - 2.5 - 2.6 - 2.7 - #- 3.0 # until net-http-persistent is updaated - #- ruby-head # until net-http-persistent is updaated + - 3.0 + - ruby-head - jruby steps: - name: Clone repository diff --git a/README.md b/README.md index 51b6059..6e669b4 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This is a pure-Ruby library for working with the [Shape Constraint Language][SHA [![Gem Version](https://badge.fury.io/rb/shacl.png)](https://badge.fury.io/rb/shacl) [![Build Status](https://github.com/ruby-rdf/shacl/workflows/CI/badge.svg?branch=develop)](https://github.com/ruby-rdf/shacl/actions?query=workflow%3ACI) -[![Coverage Status](https://coveralls.io/repos/ruby-rdf/shacl/badge.svg)](https://coveralls.io/github/ruby-rdf/shacl) +[![Coverage Status](https://coveralls.io/repos/github/ruby-rdf/shacl/badge.svg?branch=develop)](https://coveralls.io/github/ruby-rdf/shacl?branch=develop) [![Gitter chat](https://badges.gitter.im/ruby-rdf/rdf.png)](https://gitter.im/ruby-rdf/rdf) ## Features diff --git a/etc/doap.ttl b/etc/doap.ttl index 16694c3..eae3262 100644 --- a/etc/doap.ttl +++ b/etc/doap.ttl @@ -10,11 +10,11 @@ a doap:Project, earl:TestSubject, earl:Software ; sh:shapesGraph ; - doap:name "Ruby SHACL" ; + doap:name "SHACL" ; doap:homepage ; doap:license ; - doap:shortdesc "shacl is a Shapes Constraint engine for Ruby."@en ; - doap:description "shacl is an Shape Constraint engine for the RDF.rb library suite."@en ; + doap:shortdesc "SHACL is a Shapes Constraint engine for Ruby."@en ; + doap:description "SHACL is a Shape Constraint engine for the Ruby RDF.rb library suite."@en ; doap:created "2020-11-26"^^xsd:date ; doap:programming-language "Ruby" ; doap:implements ; @@ -28,8 +28,5 @@ doap:maintainer ; doap:documenter ; foaf:maker ; - dc:title "Ruby SHACL" ; - dc:description "shacl is an Shape Constraint engine for the RDF.rb library suite."@en ; - dc:date "2020-11-26"^^xsd:date ; dc:creator ; dc:isPartOf . diff --git a/etc/earl.ttl b/etc/earl.ttl index 1320898..7781008 100644 --- a/etc/earl.ttl +++ b/etc/earl.ttl @@ -12,8 +12,8 @@ doap:name "Ruby SHACL" ; doap:homepage ; doap:license ; - doap:shortdesc "shacl is a Shapes Constraint engine for Ruby."@en ; - doap:description "shacl is an Shape Constraint engine for the RDF.rb library suite."@en ; + doap:shortdesc "SHACL is a Shapes Constraint engine for Ruby."@en ; + doap:description "SHACL is an Shape Constraint engine for the RDF.rb library suite."@en ; doap:created "2020-11-26"^^xsd:date ; doap:programming-language "Ruby" ; doap:implements ; @@ -28,7 +28,7 @@ doap:documenter ; foaf:maker ; dc:title "Ruby SHACL" ; - dc:description "shacl is an Shape Constraint engine for the RDF.rb library suite."@en ; + dc:description "SHACL is an Shape Constraint engine for the RDF.rb library suite."@en ; dc:date "2020-11-26"^^xsd:date ; dc:creator ; dc:isPartOf . diff --git a/etc/template.haml b/etc/template.haml index 70e704b..f223caa 100644 --- a/etc/template.haml +++ b/etc/template.haml @@ -68,6 +68,11 @@ %p This document reports conformance for for %a{property: "doap:name", href: "https://www.w3.org/TR/shacl/"}=tests['name'] + %p + Alternate versions of the report are available in + %a{rel: :alternate, href: "earl.ttl"}="Turtle" + and + %a{rel: :alternate, href: "earl.jsonld"}="JSON-LD" %dl - subjects.each_with_index do |subject, index| - subject_refs[subject['@id']] = "subj_#{index}" diff --git a/script/tc b/script/tc index 2bcf6c1..e25de22 100755 --- a/script/tc +++ b/script/tc @@ -78,8 +78,8 @@ def run_tc(t, **options) "failed" end end - rescue Interrupt - exit(1) + rescue Interupt => e + raise e rescue Exception, NoMethodError => e result = "exception" unless options[:quiet] @@ -123,40 +123,62 @@ ensure end options = { + level: Logger::WARN, output: STDOUT, results: {}, validate: true, verbose: false, } -opts = GetoptLong.new( - ["--debug", GetoptLong::NO_ARGUMENT], - ["--earl", GetoptLong::NO_ARGUMENT], - ["--help", "-?", GetoptLong::NO_ARGUMENT], - ["--output", "-o", GetoptLong::REQUIRED_ARGUMENT], - ["--quiet", "-q", GetoptLong::NO_ARGUMENT], - ["--verbose", "-v", GetoptLong::NO_ARGUMENT], - ["--write-manifests", GetoptLong::NO_ARGUMENT], -) -def help(options) - puts "Usage: #{$0} [options] [test-number ...]" - puts "Options:" - puts " -debug: Display detailed debug output" - puts " -earl: Generate EARL report" - puts " -quiet: Minimal output" - puts " -output: Output to specified file" - puts " -verbose: Verbose processing" - puts " --write-manifests Write out the parsed manifests for earl reporting" - puts " -help,-?: This message" - exit(0) +OPT_ARGS = [ + ["--debug", GetoptLong::NO_ARGUMENT, "Debugging output"], + ["--earl", GetoptLong::NO_ARGUMENT, "Generate EARL report"], + ["--help", "-?", GetoptLong::NO_ARGUMENT, "print this message"], + ["--info", GetoptLong::NO_ARGUMENT, "Show progress on execution"], + ["--live", GetoptLong::NO_ARGUMENT, "Show live parsing results, not buffered"], + ["--output", "-o", GetoptLong::REQUIRED_ARGUMENT, "Output to specified file"], + ["--quiet", "-q", GetoptLong::NO_ARGUMENT, "Minimal output"], + ["--validate", GetoptLong::NO_ARGUMENT, "Validate input"], + ["--verbose", "-v", GetoptLong::NO_ARGUMENT, "Verbose output"], + ["--write-manifests", GetoptLong::NO_ARGUMENT, "Write out the parsed manifests for earl reporting"], +] + +def usage + STDERR.puts %{ + SHACL version #{SHACL::VERSION} + Run SHACL tests. + + Usage: #{$0} [options] [test-number ...] + }.gsub(/^ /, '') + width = OPT_ARGS.map do |o| + l = o.first.length + l += o[1].length + 2 if o[1].is_a?(String) + l + end.max + OPT_ARGS.each do |o| + s = " %-*s " % [width, (o[1].is_a?(String) ? "#{o[0,2].join(', ')}" : o[0])] + s += o.last + STDERR.puts s + end + exit(1) end +opts = GetoptLong.new(*OPT_ARGS.map {|o| o[0..-2]}) + opts.each do |opt, arg| case opt - when '--earl' then options[:quiet] = options[:earl] = true - when '--debug' then options[:debug] = true + when '--debug' then options[:level] = Logger::DEBUG + when '--earl' + options[:quiet] = options[:earl] = true + options[:level] = Logger::FATAL + when '--help' then usage() + when '--info' then options[:level] = Logger::INFO + when '--live' then options[:live] = true when '--output' then options[:output] = File.open(arg, "w") - when '--quiet' then options[:quiet] = true + when '--quiet' + options[:quiet] = true + options[:level] = Logger::FATAL + when '--validate' then options[:validate] = true when '--verbose' then options[:verbose] = true when '--write-manifests' then options[:write_manifests] = true end @@ -174,39 +196,47 @@ if options[:write_manifests] end result_count = {} -%w( - core/node - core/property - core/targets - core/misc - core/path - core/complex - core/validation-reports -).each do |variant| - manifest = TEST_BASE + variant + '/manifest.ttl' - - Fixtures::SuiteTest::Manifest.open(manifest) do |m| - if options[:write_manifests] - options[:output].puts %(\n<#{m.id}> a mf:Manifest ;) - options[:output].puts %( rdfs:label "#{m.label}" ;) if m.label - options[:output].puts %{ mf:entries (<#{m.entries.map(&:id).join('> <')}>)} - options[:output].puts %( .) - end - m.entries.each do |t| +begin + %w( + core/node + core/property + core/targets + core/misc + core/path + core/complex + core/validation-reports + ).each do |variant| + manifest = TEST_BASE + variant + '/manifest.ttl' + + Fixtures::SuiteTest::Manifest.open(manifest) do |m| if options[:write_manifests] - options[:output].puts %(\n<#{t.id}> a sht:Validate ;) - options[:output].puts %( rdfs:label "#{t.label}" ;) if t.label - options[:output].puts %( mf:action [) - options[:output].puts %( sht:dataGraph <#{t.action['dataGraph']}> ;) - options[:output].puts %( sht:shapesGraph <#{t.action['shapesGraph']}> ;) - options[:output].puts %( ] ;) + options[:output].puts %(\n<#{m.id}> a mf:Manifest ;) + options[:output].puts %( rdfs:label "#{m.label}" ;) if m.label + options[:output].puts %( rdfs:comment "#{m.comment}" ;) if m.comment + options[:output].puts %{ mf:entries (<#{m.entries.map(&:id).join('> <')}>)} options[:output].puts %( .) - next end - next unless ARGV.empty? || ARGV.any? {|n| "#{t.id}: #{t.label}".include?(n)} - run_tc(t, **options) + m.entries.each do |t| + if options[:write_manifests] + options[:output].puts %(\n<#{t.id}> a sht:Validate ;) + options[:output].puts %( rdfs:label "#{t.label}" ;) if t.label + options[:output].puts %( rdfs:comment "#{t.label}" ;) if t.comment + options[:output].puts %( mf:action [) + options[:output].puts %( sht:dataGraph <#{t.action['dataGraph']}> ;) + options[:output].puts %( sht:shapesGraph <#{t.action['shapesGraph']}> ;) + options[:output].puts %( ] ;) + options[:output].puts %( .) + next + end + next unless ARGV.empty? || ARGV.any? {|n| "#{t.id}: #{t.label}".include?(n)} + run_tc(t, **options) + end end end +rescue Interrupt => e + STDERR.puts "(interrupt)" + STDERR.puts "Backtrace: " + e.backtrace.join("\n ") if options[:verbose] + exit 1 end STDERR.puts "" if options[:quiet] diff --git a/shacl.gemspec b/shacl.gemspec index 5b259c7..b1b871c 100755 --- a/shacl.gemspec +++ b/shacl.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |gem| gem.homepage = 'https://ruby-rdf.github.com/shacl' gem.license = 'Unlicense' gem.summary = 'Implementation of Shapes Constraint Language (SHACL) for RDF.rb' - gem.description = 'Implements SHACL Core and SHACL-SPARQL within the RDF.rb ecosystem.' + gem.description = 'SHACL is an Shape Constraint engine for the Ruby RDF.rb library suite.' gem.authors = ['Gregg Kellogg'] gem.email = 'public-rdf-ruby@w3.org' From 8306b15dc6c97d974320a578c72001a51557f5c9 Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Tue, 2 Feb 2021 14:29:19 -0800 Subject: [PATCH 2/3] Update some calling sequences for Ruby 3.0. --- lib/shacl/algebra/node_shape.rb | 2 +- lib/shacl/algebra/property_shape.rb | 6 +++--- lib/shacl/algebra/shape.rb | 14 +++++++------- lib/shacl/algebra/xone.rb | 2 +- lib/shacl/shapes.rb | 4 ++-- spec/suite_helper.rb | 2 +- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/shacl/algebra/node_shape.rb b/lib/shacl/algebra/node_shape.rb index 8298fb8..bbfa34c 100644 --- a/lib/shacl/algebra/node_shape.rb +++ b/lib/shacl/algebra/node_shape.rb @@ -43,7 +43,7 @@ def conforms(node, depth: 0, **options) shape_properties = shape_paths.select {|p| p.is_a?(RDF::URI)} shape_properties += Array(@options[:ignoredProperties]) - closed_results = graph.query(subject: node).map do |statement| + closed_results = graph.query({subject: node}).map do |statement| next if shape_properties.include?(statement.predicate) not_satisfied(focus: node, value: statement.object, diff --git a/lib/shacl/algebra/property_shape.rb b/lib/shacl/algebra/property_shape.rb index 80a25d6..a871ab2 100644 --- a/lib/shacl/algebra/property_shape.rb +++ b/lib/shacl/algebra/property_shape.rb @@ -35,7 +35,7 @@ def conforms(node, depth: 0, **options) # Turn the `path` attribute into a SPARQL Property Path and evaluate to find related nodes. value_nodes = if path.is_a?(RDF::URI) - graph.query(subject: node, predicate: path).objects + graph.query({subject: node, predicate: path}).objects elsif path.evaluatable? path.execute(graph, subject: node, @@ -100,7 +100,7 @@ def path # @param [Array] value_nodes # @return [Array] def builtin_lessThan(property, node, path, value_nodes, **options) - terms = graph.query(subject: node, predicate: property).objects + terms = graph.query({subject: node, predicate: property}).objects compare(:<, terms, node, path, value_nodes, RDF::Vocab::SHACL.LessThanConstraintComponent, **options) end @@ -113,7 +113,7 @@ def builtin_lessThan(property, node, path, value_nodes, **options) # @param [Array] value_nodes # @return [Array] def builtin_lessThanOrEquals(property, node, path, value_nodes, **options) - terms = graph.query(subject: node, predicate: property).objects + terms = graph.query({subject: node, predicate: property}).objects compare(:<=, terms, node, path, value_nodes, RDF::Vocab::SHACL.LessThanOrEqualsConstraintComponent, **options) end diff --git a/lib/shacl/algebra/shape.rb b/lib/shacl/algebra/shape.rb index 3021960..f1b876a 100644 --- a/lib/shacl/algebra/shape.rb +++ b/lib/shacl/algebra/shape.rb @@ -15,16 +15,16 @@ class Shape < Operator def targetNodes (Array(@options[:targetNode]) + Array(@options[:targetClass]).map do |cls| - graph.query(predicate: RDF.type, object: cls).subjects + graph.query({predicate: RDF.type, object: cls}).subjects end + Array(@options[:targetSubjectsOf]).map do |pred| - graph.query(predicate: pred).subjects + graph.query({predicate: pred}).subjects end + Array(@options[:targetObjectsOf]).map do |pred| - graph.query(predicate: pred).objects + graph.query({predicate: pred}).objects end + ( Array(type).include?(RDF::RDFS.Class) ? - graph.query(predicate: RDF.type, object: id).subjects : + graph.query({predicate: RDF.type, object: id}).subjects : [] )).flatten.uniq end @@ -52,7 +52,7 @@ def targetNodes def builtin_class(types, node, path, value_nodes, **options) value_nodes.map do |n| has_type = n.resource? && begin - objects = graph.query(subject: n, predicate: RDF.type).objects + objects = graph.query({subject: n, predicate: RDF.type}).objects types.all? {|t| objects.include?(t)} end satisfy(focus: node, path: path, @@ -110,7 +110,7 @@ def builtin_datatype(datatype, node, path, value_nodes, **options) # @return [Array] def builtin_disjoint(properties, node, path, value_nodes, **options) disjoint_nodes = properties.map do |prop| - graph.query(subject: node, predicate: prop).objects + graph.query({subject: node, predicate: prop}).objects end.flatten.compact value_nodes.map do |n| has_value = disjoint_nodes.include?(n) @@ -140,7 +140,7 @@ def builtin_disjoint(properties, node, path, value_nodes, **options) # @param [Array] value_nodes # @return [Array] def builtin_equals(property, node, path, value_nodes, **options) - equal_nodes = graph.query(subject: node, predicate: property).objects + equal_nodes = graph.query({subject: node, predicate: property}).objects (value_nodes.map do |n| has_value = equal_nodes.include?(n) satisfy(focus: node, path: path, diff --git a/lib/shacl/algebra/xone.rb b/lib/shacl/algebra/xone.rb index 692e5d8..0210a44 100644 --- a/lib/shacl/algebra/xone.rb +++ b/lib/shacl/algebra/xone.rb @@ -36,7 +36,7 @@ def conforms(node, path: nil, depth: 0, **options) log_debug(NAME, depth: depth) {SXP::Generator.string({node: node}.to_sxp_bin)} num_conform = operands.inject(0) do |memo, op| results = op.conforms(node, depth: depth + 1, **options) - memo += (results.all?(&:conform?) ? 1 : 0) + memo += (results.flatten.all?(&:conform?) ? 1 : 0) end case num_conform when 0 diff --git a/lib/shacl/shapes.rb b/lib/shacl/shapes.rb index 89e5114..2031c69 100644 --- a/lib/shacl/shapes.rb +++ b/lib/shacl/shapes.rb @@ -35,7 +35,7 @@ def self.from_graph(graph, loaded_graphs: [], **options) @loded_graphs = loaded_graphs import_count = 0 - while (imports = graph.query(predicate: RDF::OWL.imports).map(&:object)).count > import_count + while (imports = graph.query({predicate: RDF::OWL.imports}).map(&:object)).count > import_count # Load each imported graph imports.each do |ref| graph.load(imports) @@ -65,7 +65,7 @@ def self.from_graph(graph, loaded_graphs: [], **options) # @raise [SHACL::Error] def self.from_queryable(queryable, **options) # Query queryable to find one ore more shapes graphs - graphs = queryable.query(predicate: RDF::Vocab::SHACL.shapesGraph).objects + graphs = queryable.query({predicate: RDF::Vocab::SHACL.shapesGraph}).objects graph = RDF::Graph.new do |g| graphs.each {|iri| g.load(iri)} end diff --git a/spec/suite_helper.rb b/spec/suite_helper.rb index 3b93b21..5a021ae 100644 --- a/spec/suite_helper.rb +++ b/spec/suite_helper.rb @@ -110,7 +110,7 @@ def self.open(file) g = RDF::OrderedRepo.load(file) label = g.first_object(predicate: RDF::RDFS.label).to_s - g.query(predicate: RDF::URI("http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#include")).objects.each do |f| + g.query({predicate: RDF::URI("http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#include")}).objects.each do |f| g.load(f) end JSON::LD::API.fromRDF(g, useNativeTypes: true) do |expanded| From 248b04b578d6ea366d2a1f0ccfa45b02bc84b095 Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Tue, 2 Feb 2021 14:34:37 -0800 Subject: [PATCH 3/3] Version 0.1.1. --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 6e8bf73..17e51c3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0 +0.1.1