From 76f617ef0a3e6c969537d022a969cd5051c2727e Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Sun, 10 Jul 2016 11:19:33 -0700 Subject: [PATCH 01/12] Update minimum ruby version to 2.2.2. See https://github.com/ruby-rdf/rdf/issues/307. --- .travis.yml | 8 +++----- README.md | 2 +- rdf-reasoner.gemspec | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7640dbb..0e2444f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,10 +4,8 @@ script: "bundle exec rspec spec" env: - CI=true rvm: - - 2.0 - - 2.1 - - 2.2.4 - - 2.3.0 - - jruby-9.0.4.0 + - 2.2.5 + - 2.3.1 + - jruby-9.0.5.0 cache: bundler sudo: false diff --git a/README.md b/README.md index 00a8712..24235a3 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ Domain and Range entailment include specific rules for schema.org vocabularies. ## Dependencies -* [Ruby](http://ruby-lang.org/) (>= 2.0) +* [Ruby](http://ruby-lang.org/) (>= 2.2.2) * [RDF.rb](http://rubygems.org/gems/rdf) (>= 2.0) ## Mailing List diff --git a/rdf-reasoner.gemspec b/rdf-reasoner.gemspec index 8a54d8c..50f1237 100755 --- a/rdf-reasoner.gemspec +++ b/rdf-reasoner.gemspec @@ -24,7 +24,7 @@ Gem::Specification.new do |gem| the vocabulary ruleset. This can be used to implement SPARQL Entailment Regimes.).gsub(/\s+/m, ' ') - gem.required_ruby_version = '>= 2.0' + gem.required_ruby_version = '>= 2.2.2' gem.requirements = [] gem.add_runtime_dependency 'rdf', '~> 2.0' gem.add_runtime_dependency 'rdf-vocab', '~> 2.0' From a1572999a6f411a0e4a3a930f38cb1f9aac38f7c Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Mon, 11 Jul 2016 14:32:09 -0700 Subject: [PATCH 02/12] Minor tweak to specs because of schema.org change. --- spec/schema_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/schema_spec.rb b/spec/schema_spec.rb index 1865ac1..c5752bb 100644 --- a/spec/schema_spec.rb +++ b/spec/schema_spec.rb @@ -239,7 +239,7 @@ "schema:Text with datatyped literal" => %( @prefix schema: . @prefix xsd: . - a schema:Thing; schema:activeIngredient "foo"^^xsd:token . + a schema:Thing; schema:recipeIngredient "foo"^^xsd:token . ), "schema:URL with non-conforming plain literal" => %( @prefix schema: . From 85264a6c17998fb213602c63cc0463c47d30b34e Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Thu, 28 Jul 2016 11:52:14 -0400 Subject: [PATCH 03/12] Update sxp repo in Gemfile. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 7abd4d8..35b508c 100644 --- a/Gemfile +++ b/Gemfile @@ -12,7 +12,7 @@ group :development, :test do gem 'rdf-isomorphic', github: "ruby-rdf/rdf-isomorphic", branch: "develop" gem "rdf-spec", github: "ruby-rdf/rdf-spec", branch: "develop" gem 'rdf-turtle', github: "ruby-rdf/rdf-turtle", branch: "develop" - gem 'sxp', github: "gkellogg/sxp-ruby", branch: "develop" + gem 'sxp', github: "dryruby/sxp.rb", branch: "develop" gem 'rake' gem 'simplecov', require: false gem 'ruby-prof', platform: :mri From daa2e97c9cc37e610c814a198a1096c5fcc355a2 Mon Sep 17 00:00:00 2001 From: Justin Coyne Date: Wed, 10 Aug 2016 15:31:32 -0500 Subject: [PATCH 04/12] rdf-spec is a development dependency --- rdf-reasoner.gemspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rdf-reasoner.gemspec b/rdf-reasoner.gemspec index 50f1237..b232538 100755 --- a/rdf-reasoner.gemspec +++ b/rdf-reasoner.gemspec @@ -30,11 +30,11 @@ Gem::Specification.new do |gem| gem.add_runtime_dependency 'rdf-vocab', '~> 2.0' gem.add_runtime_dependency 'rdf-xsd', '~> 2.0' - gem.add_runtime_dependency 'rdf-spec', '~> 2.0' + gem.add_development_dependency 'rdf-spec', '~> 2.0' gem.add_development_dependency 'json-ld', '~> 2.0' gem.add_development_dependency 'rdf-turtle', '~> 2.0' gem.add_development_dependency 'equivalent-xml', '~> 0.4' gem.add_development_dependency 'rspec', '~> 3.4' gem.add_development_dependency 'yard' , '~> 0.8' gem.post_install_message = nil -end \ No newline at end of file +end From c66f193f45af4339c2459aae0816594d36f0bcdc Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Fri, 12 Aug 2016 18:14:28 -0700 Subject: [PATCH 05/12] Remove wirble from Gemfile, as dependency-ci objects that it has no license and it's not really neccessary. --- .travis.yml | 2 +- Gemfile | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0e2444f..d0341e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,6 @@ env: rvm: - 2.2.5 - 2.3.1 - - jruby-9.0.5.0 + - jruby-9.1.2.0 cache: bundler sudo: false diff --git a/Gemfile b/Gemfile index 35b508c..0622181 100644 --- a/Gemfile +++ b/Gemfile @@ -19,7 +19,6 @@ group :development, :test do end group :debug do - gem "wirble" gem "redcarpet", platforms: :ruby gem "byebug", platforms: :mri end From 7b1ee0d0ef10cf4a581f7b2cbd609c52bb69fe26 Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Sat, 13 Aug 2016 15:21:51 -0700 Subject: [PATCH 06/12] Change Travis JRuby to default and allow failures. --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d0341e3..19ec7b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,9 @@ env: rvm: - 2.2.5 - 2.3.1 - - jruby-9.1.2.0 + - jruby cache: bundler sudo: false +matrix: + allow_failures: + - rvm: jruby From 54ce87875ef74023d17215081449f2aa0695816a Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Mon, 22 Aug 2016 11:25:17 -0700 Subject: [PATCH 07/12] Extract common entailed_types for schema. --- lib/rdf/reasoner/schema.rb | 40 +++++++++++++++----------------------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/lib/rdf/reasoner/schema.rb b/lib/rdf/reasoner/schema.rb index 75976e9..f663e5a 100644 --- a/lib/rdf/reasoner/schema.rb +++ b/lib/rdf/reasoner/schema.rb @@ -27,13 +27,7 @@ def domain_compatible_schema?(resource, queryable, options = {}) domains = Array(self.domainIncludes) - [RDF::OWL.Thing] # Fully entailed types of the resource - types = options.fetch(:types) do - queryable.query(:subject => resource, :predicate => RDF.type). - map {|s| (t = (RDF::Vocabulary.find_term(s.object) rescue nil)) && t.entail(:subClassOf)}. - flatten. - uniq. - compact - end unless domains.empty? + types = entailed_types(resource, queryable, options) unless domains.empty? # Every domain must match some entailed type resource_acceptable = Array(types).empty? || domains.any? {|d| types.include?(d)} @@ -109,7 +103,7 @@ def range_compatible_schema?(resource, queryable, options = {}) resource.datatype == RDF::XSD.anyURI || resource.plain? && RDF::Literal::AnyURI.new(resource.value).valid? else - # If this is an XSD range, look for appropriate literal + # If may be an XSD range, look for appropriate literal if range.start_with?(RDF::XSD.to_s) if resource.datatype == RDF::URI(range) true @@ -129,26 +123,12 @@ def range_compatible_schema?(resource, queryable, options = {}) true # schema:URL matches URI resources elsif ranges.include?(RDF::Vocab::SCHEMA.Text) && resource.uri? # Allowed if resource is untyped - # Fully entailed types of the resource - types = options.fetch(:types) do - queryable.query(:subject => resource, :predicate => RDF.type). - map {|s| (t = (RDF::Vocabulary.find_term(s.object) rescue nil)) && t.entail(:subClassOf)}. - flatten. - uniq. - compact - end - types.empty? + entailed_types(resource, queryable, options).empty? elsif literal_range?(ranges) false # If resource isn't literal, this is a range violation else # Fully entailed types of the resource - types = options.fetch(:types) do - queryable.query(:subject => resource, :predicate => RDF.type). - map {|s| (t = (RDF::Vocabulary.find_term(s.object) rescue nil)) && t.entail(:subClassOf)}. - flatten. - uniq. - compact - end + types = entailed_types(resource, queryable, options) # Every range must match some entailed type resource_acceptable = Array(types).empty? || ranges.any? {|d| types.include?(d)} @@ -191,6 +171,18 @@ def literal_range?(ranges) def self.included(mod) end + + private + # Fully entailed types + def entailed_types(resource, queryable, options = {}) + options.fetch(:types) do + queryable.query(:subject => resource, :predicate => RDF.type). + map {|s| (t = (RDF::Vocabulary.find_term(s.object) rescue nil)) && t.entail(:subClassOf)}. + flatten. + uniq. + compact + end + end end # Extend the Term with this methods From 7da646f503af0a582781f1348059a75ac646402c Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Mon, 22 Aug 2016 11:26:14 -0700 Subject: [PATCH 08/12] Relax checking if any range includes schema:Text, to be just if range exactly includes schema:Text. This ends up being implemented later on anyway. Fixes #3. --- dependencyci.yml | 5 +++++ lib/rdf/reasoner/schema.rb | 2 +- spec/schema_spec.rb | 17 +++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 dependencyci.yml diff --git a/dependencyci.yml b/dependencyci.yml new file mode 100644 index 0000000..0c67c1b --- /dev/null +++ b/dependencyci.yml @@ -0,0 +1,5 @@ +platform: + Rubygems: + rdf-isomorphic: + tests: + unmaintained: skip \ No newline at end of file diff --git a/lib/rdf/reasoner/schema.rb b/lib/rdf/reasoner/schema.rb index f663e5a..f0c3962 100644 --- a/lib/rdf/reasoner/schema.rb +++ b/lib/rdf/reasoner/schema.rb @@ -121,7 +121,7 @@ def range_compatible_schema?(resource, queryable, options = {}) true # Special case for schema boolean resources elsif ranges.include?(RDF::Vocab::SCHEMA.URL) && resource.uri? true # schema:URL matches URI resources - elsif ranges.include?(RDF::Vocab::SCHEMA.Text) && resource.uri? + elsif ranges == [RDF::Vocab::SCHEMA.Text] && resource.uri? # Allowed if resource is untyped entailed_types(resource, queryable, options).empty? elsif literal_range?(ranges) diff --git a/spec/schema_spec.rb b/spec/schema_spec.rb index c5752bb..49151ad 100644 --- a/spec/schema_spec.rb +++ b/spec/schema_spec.rb @@ -98,6 +98,18 @@ @prefix schema: . schema:height . a schema:Distance; schema:name "20 3/4 inches" . ), + "schema:CreativeWork with itemListElement (IRI)" => %( + @prefix schema: . + schema:itemListElement . a schema:CreativeWork . + ), + "schema:CreativeWork with itemListElement (BNode)" => %( + @prefix schema: . + schema:itemListElement [ a schema:CreativeWork ] . + ), + "text literal with itemListElement" => %( + @prefix schema: . + schema:itemListElement "Foo" . + ), }.each do |name, input| it name do graph = RDF::Graph.new << RDF::Turtle::Reader.new(input) @@ -187,6 +199,7 @@ end end end + context "object range violations" do { "object of wrong type" => %( @@ -249,6 +262,10 @@ @prefix schema: . a schema:CreativeWork; schema:isFamilyFriendly "bar" . ), + "date with itemListElement" => %( + @prefix schema: . + schema:itemListElement "2016-08-22"^^schema:Date . + ), }.each do |name, input| it name do graph = RDF::Graph.new << RDF::Turtle::Reader.new(input) From f181ca153cbb558906cf5e8c40b681bd5bfa02ac Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Mon, 5 Sep 2016 14:46:45 -0700 Subject: [PATCH 09/12] Mark entailed statements as `inferred`. --- lib/rdf/reasoner/owl.rb | 4 ++-- lib/rdf/reasoner/rdfs.rb | 8 ++++---- spec/owl_spec.rb | 2 ++ spec/rdfs_spec.rb | 5 +++++ 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/rdf/reasoner/owl.rb b/lib/rdf/reasoner/owl.rb index 82c752b..7b6bd05 100644 --- a/lib/rdf/reasoner/owl.rb +++ b/lib/rdf/reasoner/owl.rb @@ -50,7 +50,7 @@ def _entail_equivalentClass if self.predicate == RDF.type if term = (RDF::Vocabulary.find_term(self.object) rescue nil) term._entail_equivalentClass do |t| - statements << RDF::Statement(self.to_hash.merge(object: t)) + statements << RDF::Statement(self.to_hash.merge(object: t, inferred: true)) end end end @@ -89,7 +89,7 @@ def _entail_equivalentProperty statements = [] if term = (RDF::Vocabulary.find_term(self.predicate) rescue nil) term._entail_equivalentProperty do |t| - statements << RDF::Statement(self.to_hash.merge(predicate: t)) + statements << RDF::Statement(self.to_hash.merge(predicate: t, inferred: true)) end end statements.each {|s| yield s} if block_given? diff --git a/lib/rdf/reasoner/rdfs.rb b/lib/rdf/reasoner/rdfs.rb index cfd4978..4d46dd2 100644 --- a/lib/rdf/reasoner/rdfs.rb +++ b/lib/rdf/reasoner/rdfs.rb @@ -58,7 +58,7 @@ def _entail_subClassOf if self.predicate == RDF.type if term = (RDF::Vocabulary.find_term(self.object) rescue nil) term._entail_subClassOf do |t| - statements << RDF::Statement(self.to_hash.merge(object: t)) + statements << RDF::Statement(self.to_hash.merge(object: t, inferred: true)) end end #$stderr.puts("subClassf(#{self.predicate.pname}): #{statements.map(&:object).map {|r| r.respond_to?(:pname) ? r.pname : r.to_ntriples}}}") @@ -127,7 +127,7 @@ def _entail_subPropertyOf statements = [] if term = (RDF::Vocabulary.find_term(self.predicate) rescue nil) term._entail_subPropertyOf do |t| - statements << RDF::Statement(self.to_hash.merge(predicate: t)) + statements << RDF::Statement(self.to_hash.merge(predicate: t, inferred: true)) end #$stderr.puts("subPropertyOf(#{self.predicate.pname}): #{statements.map(&:object).map {|r| r.respond_to?(:pname) ? r.pname : r.to_ntriples}}}") end @@ -146,7 +146,7 @@ def _entail_domain statements = [] if term = (RDF::Vocabulary.find_term(self.predicate) rescue nil) term.domain.each do |t| - statements << RDF::Statement(self.to_hash.merge(predicate: RDF.type, object: t)) + statements << RDF::Statement(self.to_hash.merge(predicate: RDF.type, object: t, inferred: true)) end end #$stderr.puts("domain(#{self.predicate.pname}): #{statements.map(&:object).map {|r| r.respond_to?(:pname) ? r.pname : r.to_ntriples}}}") @@ -165,7 +165,7 @@ def _entail_range statements = [] if object.resource? && term = (RDF::Vocabulary.find_term(self.predicate) rescue nil) term.range.each do |t| - statements << RDF::Statement(self.to_hash.merge(subject: self.object, predicate: RDF.type, object: t)) + statements << RDF::Statement(self.to_hash.merge(subject: self.object, predicate: RDF.type, object: t, inferred: true)) end end #$stderr.puts("range(#{self.predicate.pname}): #{statements.map(&:object).map {|r| r.respond_to?(:pname) ? r.pname : r.to_ntriples}}") diff --git a/spec/owl_spec.rb b/spec/owl_spec.rb index 6c8ab20..3a31233 100644 --- a/spec/owl_spec.rb +++ b/spec/owl_spec.rb @@ -24,6 +24,7 @@ subject {RDF::Statement(RDF::URI("a"), RDF.type, cls)} let(:results) {entails.map {|r| RDF::Statement(RDF::URI("a"), RDF.type, r)}} specify {expect(subject.entail(:equivalentClass)).to include(*results)} + specify {expect(subject.entail(:equivalentClass)).to all(be_inferred)} specify {expect {|b| subject.entail(:equivalentClass, &b)}.to yield_control.at_least(entails.length)} end @@ -65,6 +66,7 @@ subject {RDF::Statement(RDF::URI("a"), prop, RDF::URI("b"))} let(:results) {entails.map {|r| RDF::Statement(RDF::URI("a"), r, RDF::URI("b"))}} specify {expect(subject.entail(:equivalentProperty)).to include(*results)} + specify {expect(subject.entail(:equivalentProperty)).to all(be_inferred)} specify {expect {|b| subject.entail(:equivalentProperty, &b)}.to yield_control.at_least(entails.length)} end diff --git a/spec/rdfs_spec.rb b/spec/rdfs_spec.rb index d1978d5..8461b06 100644 --- a/spec/rdfs_spec.rb +++ b/spec/rdfs_spec.rb @@ -24,6 +24,7 @@ subject {RDF::Statement(RDF::URI("a"), RDF.type, cls)} let(:results) {entails.map {|r| RDF::Statement(RDF::URI("a"), RDF.type, r)}} specify {expect(subject.entail(:subClassOf)).to include(*results)} + specify {expect(subject.entail(:subClassOf)).to all(be_inferred)} specify {expect {|b| subject.entail(:subClassOf, &b)}.to yield_control.at_least(entails.length)} end @@ -65,6 +66,7 @@ subject {RDF::Statement(RDF::URI("a"), RDF.type, cls)} let(:results) {entails.map {|r| RDF::Statement(RDF::URI("a"), RDF.type, r)}} specify {expect(subject.entail(:subClass)).to be_empty} + specify {expect(subject.entail(:subClass)).to all(be_inferred)} specify {expect {|b| subject.entail(:subClass, &b)}.not_to yield_control} end @@ -103,6 +105,7 @@ subject {RDF::Statement(RDF::URI("a"), prop, RDF::URI("b"))} let(:results) {entails.map {|r| RDF::Statement(RDF::URI("a"), r, RDF::URI("b"))}} specify {expect(subject.entail(:subPropertyOf)).to include(*results)} + specify {expect(subject.entail(:subPropertyOf)).to all(be_inferred)} specify {expect {|b| subject.entail(:subPropertyOf, &b)}.to yield_control.at_least(entails.length)} end @@ -143,6 +146,7 @@ subject {RDF::Statement(RDF::URI("a"), prop, RDF::URI("b"))} let(:results) {entails.map {|r| RDF::Statement(RDF::URI("a"), RDF.type, r)}} specify {expect(subject.entail(:domain)).to include(*results)} + specify {expect(subject.entail(:domain)).to all(be_inferred)} specify {expect {|b| subject.entail(:domain, &b)}.to yield_control.at_least(entails.length)} end @@ -183,6 +187,7 @@ subject {RDF::Statement(RDF::URI("a"), prop, RDF::URI("b"))} let(:results) {entails.map {|r| RDF::Statement(RDF::URI("b"), RDF.type, r)}} specify {expect(subject.entail(:range)).to include(*results)} + specify {expect(subject.entail(:range)).to all(be_inferred)} specify {expect {|b| subject.entail(:range, &b)}.to yield_control.at_least(entails.length)} end From d04fb790638fa34d7bc23071e97dddb3a0fe0575 Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Sun, 11 Dec 2016 17:08:57 -0800 Subject: [PATCH 10/12] Boilerplate for RDF-MT tests. --- .travis.yml | 4 +- spec/.gitignore | 1 + spec/suite_helper.rb | 162 +++++++++++++++++++++++++++++++++++++++++++ spec/suite_spec.rb | 47 +++++++++++++ 4 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 spec/.gitignore create mode 100644 spec/suite_helper.rb create mode 100644 spec/suite_spec.rb diff --git a/.travis.yml b/.travis.yml index 19ec7b4..eee139d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,8 @@ script: "bundle exec rspec spec" env: - CI=true rvm: - - 2.2.5 - - 2.3.1 + - 2.2.6 + - 2.3.3 - jruby cache: bundler sudo: false diff --git a/spec/.gitignore b/spec/.gitignore new file mode 100644 index 0000000..6cca534 --- /dev/null +++ b/spec/.gitignore @@ -0,0 +1 @@ +/w3c-rdf diff --git a/spec/suite_helper.rb b/spec/suite_helper.rb new file mode 100644 index 0000000..f64b2e0 --- /dev/null +++ b/spec/suite_helper.rb @@ -0,0 +1,162 @@ +# Spira class for manipulating test-manifest style test suites. +# Used for Turtle tests +require 'rdf/turtle' +require 'json/ld' + +# For now, override RDF::Utils::File.open_file to look for the file locally before attempting to retrieve it +module RDF::Util + module File + REMOTE_PATH = "http://www.w3.org/2013/rdf-mt-tests/" + LOCAL_PATH = ::File.expand_path("../w3c-rdf/rdf-mt", __FILE__) + '/' + + class << self + alias_method :original_open_file, :open_file + end + + ## + # Override to use Patron for http and https, Kernel.open otherwise. + # + # @param [String] filename_or_url to open + # @param [Hash{Symbol => Object}] options + # @option options [Array, String] :headers + # HTTP Request headers. + # @return [IO] File stream + # @yield [IO] File stream + def self.open_file(filename_or_url, options = {}, &block) + case + when filename_or_url.to_s =~ /^file:/ + path = filename_or_url[5..-1] + Kernel.open(path.to_s, options, &block) + when (filename_or_url.to_s =~ %r{^#{REMOTE_PATH}} && Dir.exist?(LOCAL_PATH)) + #puts "attempt to open #{filename_or_url} locally" + localpath = filename_or_url.to_s.sub(REMOTE_PATH, LOCAL_PATH) + response = begin + ::File.open(localpath) + rescue Errno::ENOENT => e + raise IOError, e.message + end + document_options = { + base_uri: RDF::URI(filename_or_url), + charset: Encoding::UTF_8, + code: 200, + headers: {} + } + #puts "use #{filename_or_url} locally" + document_options[:headers][:content_type] = case filename_or_url.to_s + when /\.ttl$/ then 'text/turtle' + when /\.nt$/ then 'application/n-triples' + when /\.jsonld$/ then 'application/ld+json' + else 'unknown' + end + + document_options[:headers][:content_type] = response.content_type if response.respond_to?(:content_type) + # For overriding content type from test data + document_options[:headers][:content_type] = options[:contentType] if options[:contentType] + + remote_document = RDF::Util::File::RemoteDocument.new(response.read, document_options) + if block_given? + yield remote_document + else + remote_document + end + else + original_open_file(filename_or_url, options, &block) + end + end + end +end + +module Fixtures + module SuiteTest + BASE = "http://www.w3.org/2013/rdf-mt-tests/" + FRAME = JSON.parse(%q({ + "@context": { + "@vocab": "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + "rdfs": "http://www.w3.org/2000/01/rdf-schema#", + "mf": "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#", + "mq": "http://www.w3.org/2001/sw/DataAccess/tests/test-query#", + + "comment": "rdfs:comment", + "entries": {"@id": "mf:entries", "@container": "@list"}, + "name": "mf:name", + "action": {"@type": "@id"}, + "result": {"@type": "@id"}, + "recognizedDatatypes": {"@container": "@list"}, + "unrecognizedDatatypes": {"@container": "@list"}, + "approval": {"@id": "rdft:approval", "@type": "@vocab"} + }, + "@type": "mf:Manifest", + "entries": { + "@type": [ + "mf:PositiveEntailmentTest", + "mf:NegativeEntailmentTest" + ] + } + })) + + class Manifest < JSON::LD::Resource + def self.open(file) + #puts "open: #{file}" + g = RDF::Repository.load(file, format: :ttl) + JSON::LD::API.fromRDF(g) do |expanded| + JSON::LD::API.frame(expanded, FRAME) do |framed| + yield Manifest.new(framed['@graph'].first) + end + end + end + + # @param [Hash] json framed JSON-LD + # @return [Array] + def self.from_jsonld(json) + json['@graph'].map {|e| Manifest.new(e)} + end + + def entries + # Map entries to resources + attributes['entries'].map {|e| Entry.new(e)} + end + end + + class Entry < JSON::LD::Resource + attr_accessor :logger + + def base + BASE + action.split('/').last + end + + # Alias data and query + def input + @input ||= RDF::Util::File.open_file(action) {|f| f.read} + end + + def result + attributes[:result].is_a?(Hash) ? attributes[:result]['@value'] : attributes[:result] + end + + def expected + @expected ||= RDF::Util::File.open_file(result) {|f| f.read} + end + + def entailment? + Array(attributes['@type']).join(" ").match(/Entailment/) + end + + def positive_test? + !Array(attributes['@type']).join(" ").match(/Negative/) + end + + def negative_test? + !positive_test? + end + + def inspect + super.sub('>', "\n" + + " positive?: #{positive_test?.inspect}\n" + + " entailment?: #{entailment?.inspect}\n" + + ">" + ) + end + end + end +end \ No newline at end of file diff --git a/spec/suite_spec.rb b/spec/suite_spec.rb new file mode 100644 index 0000000..9bca81a --- /dev/null +++ b/spec/suite_spec.rb @@ -0,0 +1,47 @@ +$:.unshift "." +require 'spec_helper' +require 'rdf/spec' + +describe RDF::Turtle::Reader do + # W3C RDF Semantics Test suite from https://dvcs.w3.org/hg/rdf/file/default/rdf-mt/tests/ + describe "w3c turtle tests" do + require 'suite_helper' + + %w(manifest.ttl).each do |man| + Fixtures::SuiteTest::Manifest.open(Fixtures::SuiteTest::BASE + man) do |m| + describe m.comment do + m.entries.each do |t| + specify "#{t.name}: #{t.comment}" do + t.logger = RDF::Spec.logger + t.logger.info t.inspect + t.logger.info "action:\n#{t.input}" if t.input + t.logger.info "action:\n#{t.input}" if t.input + t.logger.info "result:\n#{t.result.is_a?(String) ? RDF::Util::File.open_file(t.result).read : t.result}" + + action_graph = t.action ? RDF::Repository.load(t.action, base_uri: t.base) : false + result_graph = t.result.is_a?(String) ? RDF::Repository.load(t.result, base_uri: t.base) : false + + # FIXME, graphs aren't equivalent, but action should entail result, either of which may be false + begin + if t.positive_test? + pending "PositiveEntailment" + action_graph.entail!(:rdfs) + else + pending "NegativeEntailment" + end + #rescue + # if t.action == false + # fail "don't know how to deal with false premise" + # elsif t.result == false + # fail "don't know how to deal with false result" + # else + # raise + # end + end + end + end + end + end + end + end +end unless ENV['CI'] \ No newline at end of file From 406ff6371eb2c5e5e061e2471fc93aa255e15e61 Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Sat, 31 Dec 2016 13:06:28 -0800 Subject: [PATCH 11/12] Updates for RDF.rb 2.2. Fix some CLI testing issues. --- .travis.yml | 1 + README.md | 5 ++++- lib/rdf/reasoner/owl.rb | 4 ++-- lib/rdf/reasoner/rdfs.rb | 8 ++++---- rdf-reasoner.gemspec | 2 +- spec/format_spec.rb | 7 +++++-- spec/suite_spec.rb | 6 +++--- 7 files changed, 20 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index eee139d..10bc93e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ env: rvm: - 2.2.6 - 2.3.3 + - 2.4.0 - jruby cache: bundler sudo: false diff --git a/README.md b/README.md index 24235a3..fc8a2c9 100644 --- a/README.md +++ b/README.md @@ -98,10 +98,13 @@ Domain and Range entailment include specific rules for schema.org vocabularies. end end +## Command-Line interface +The `rdf` command-line interface is extended with `entail` and `lint` commands. `Entail` can be used in combination, with `serialize` to generate an output graph representation including entailed triples. + ## Dependencies * [Ruby](http://ruby-lang.org/) (>= 2.2.2) -* [RDF.rb](http://rubygems.org/gems/rdf) (>= 2.0) +* [RDF.rb](http://rubygems.org/gems/rdf) (>= 2.1.1) ## Mailing List diff --git a/lib/rdf/reasoner/owl.rb b/lib/rdf/reasoner/owl.rb index 7b6bd05..d409115 100644 --- a/lib/rdf/reasoner/owl.rb +++ b/lib/rdf/reasoner/owl.rb @@ -50,7 +50,7 @@ def _entail_equivalentClass if self.predicate == RDF.type if term = (RDF::Vocabulary.find_term(self.object) rescue nil) term._entail_equivalentClass do |t| - statements << RDF::Statement(self.to_hash.merge(object: t, inferred: true)) + statements << RDF::Statement(self.to_h.merge(object: t, inferred: true)) end end end @@ -89,7 +89,7 @@ def _entail_equivalentProperty statements = [] if term = (RDF::Vocabulary.find_term(self.predicate) rescue nil) term._entail_equivalentProperty do |t| - statements << RDF::Statement(self.to_hash.merge(predicate: t, inferred: true)) + statements << RDF::Statement(self.to_h.merge(predicate: t, inferred: true)) end end statements.each {|s| yield s} if block_given? diff --git a/lib/rdf/reasoner/rdfs.rb b/lib/rdf/reasoner/rdfs.rb index 4d46dd2..eb5a866 100644 --- a/lib/rdf/reasoner/rdfs.rb +++ b/lib/rdf/reasoner/rdfs.rb @@ -58,7 +58,7 @@ def _entail_subClassOf if self.predicate == RDF.type if term = (RDF::Vocabulary.find_term(self.object) rescue nil) term._entail_subClassOf do |t| - statements << RDF::Statement(self.to_hash.merge(object: t, inferred: true)) + statements << RDF::Statement(self.to_h.merge(object: t, inferred: true)) end end #$stderr.puts("subClassf(#{self.predicate.pname}): #{statements.map(&:object).map {|r| r.respond_to?(:pname) ? r.pname : r.to_ntriples}}}") @@ -127,7 +127,7 @@ def _entail_subPropertyOf statements = [] if term = (RDF::Vocabulary.find_term(self.predicate) rescue nil) term._entail_subPropertyOf do |t| - statements << RDF::Statement(self.to_hash.merge(predicate: t, inferred: true)) + statements << RDF::Statement(self.to_h.merge(predicate: t, inferred: true)) end #$stderr.puts("subPropertyOf(#{self.predicate.pname}): #{statements.map(&:object).map {|r| r.respond_to?(:pname) ? r.pname : r.to_ntriples}}}") end @@ -146,7 +146,7 @@ def _entail_domain statements = [] if term = (RDF::Vocabulary.find_term(self.predicate) rescue nil) term.domain.each do |t| - statements << RDF::Statement(self.to_hash.merge(predicate: RDF.type, object: t, inferred: true)) + statements << RDF::Statement(self.to_h.merge(predicate: RDF.type, object: t, inferred: true)) end end #$stderr.puts("domain(#{self.predicate.pname}): #{statements.map(&:object).map {|r| r.respond_to?(:pname) ? r.pname : r.to_ntriples}}}") @@ -165,7 +165,7 @@ def _entail_range statements = [] if object.resource? && term = (RDF::Vocabulary.find_term(self.predicate) rescue nil) term.range.each do |t| - statements << RDF::Statement(self.to_hash.merge(subject: self.object, predicate: RDF.type, object: t, inferred: true)) + statements << RDF::Statement(self.to_h.merge(subject: self.object, predicate: RDF.type, object: t, inferred: true)) end end #$stderr.puts("range(#{self.predicate.pname}): #{statements.map(&:object).map {|r| r.respond_to?(:pname) ? r.pname : r.to_ntriples}}") diff --git a/rdf-reasoner.gemspec b/rdf-reasoner.gemspec index b232538..640c2a2 100755 --- a/rdf-reasoner.gemspec +++ b/rdf-reasoner.gemspec @@ -26,7 +26,7 @@ Gem::Specification.new do |gem| gem.required_ruby_version = '>= 2.2.2' gem.requirements = [] - gem.add_runtime_dependency 'rdf', '~> 2.0' + gem.add_runtime_dependency 'rdf', '~> 2.1', '>= 2.1.1' gem.add_runtime_dependency 'rdf-vocab', '~> 2.0' gem.add_runtime_dependency 'rdf-xsd', '~> 2.0' diff --git a/spec/format_spec.rb b/spec/format_spec.rb index 20ac390..8454eb6 100644 --- a/spec/format_spec.rb +++ b/spec/format_spec.rb @@ -25,13 +25,16 @@ describe ".cli_commands" do require 'rdf/cli' let(:ttl) {File.expand_path("../../etc/doap.ttl", __FILE__)} + let(:capture) {StringIO.new} it "entails" do - expect {RDF::CLI.exec(["entail", ttl])}.to write.to(:output) + expect {RDF::CLI.exec(["entail", "serialize", ttl], format: :ttl, output: capture)}.to write.to(:output) + expect(capture.string).not_to be_empty end it "lints" do - expect {RDF::CLI.exec(["lint", ttl])}.to write.to(:output) + expect {RDF::CLI.exec(["lint", ttl], format: :ttl, output: capture)}.to write.to(:output) + expect(capture.string).not_to be_empty end end end diff --git a/spec/suite_spec.rb b/spec/suite_spec.rb index 9bca81a..e4ede10 100644 --- a/spec/suite_spec.rb +++ b/spec/suite_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' require 'rdf/spec' -describe RDF::Turtle::Reader do +describe RDF::Reasoner do # W3C RDF Semantics Test suite from https://dvcs.w3.org/hg/rdf/file/default/rdf-mt/tests/ describe "w3c turtle tests" do require 'suite_helper' @@ -24,10 +24,10 @@ # FIXME, graphs aren't equivalent, but action should entail result, either of which may be false begin if t.positive_test? - pending "PositiveEntailment" + skip "PositiveEntailment" action_graph.entail!(:rdfs) else - pending "NegativeEntailment" + skip "NegativeEntailment" end #rescue # if t.action == false From f8e9406291372beb288433f9c4fa72851f4e4bd2 Mon Sep 17 00:00:00 2001 From: Gregg Kellogg Date: Sat, 31 Dec 2016 13:11:12 -0800 Subject: [PATCH 12/12] Version 0.4.1. --- VERSION | 2 +- spec/format_spec.rb | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index 1d0ba9e..267577d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.4.0 +0.4.1 diff --git a/spec/format_spec.rb b/spec/format_spec.rb index 8454eb6..a802d0b 100644 --- a/spec/format_spec.rb +++ b/spec/format_spec.rb @@ -33,8 +33,7 @@ end it "lints" do - expect {RDF::CLI.exec(["lint", ttl], format: :ttl, output: capture)}.to write.to(:output) - expect(capture.string).not_to be_empty + expect {RDF::CLI.exec(["lint", ttl], format: :ttl)}.to write(/Linted in .* seconds/).to(:output) end end end