Skip to content

Commit

Permalink
Finish 0.6.1
Browse files Browse the repository at this point in the history
  • Loading branch information
gkellogg committed May 30, 2020
2 parents c648baa + 5407c4a commit a951166
Show file tree
Hide file tree
Showing 12 changed files with 78 additions and 38 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ Domain and Range entailment include specific rules for schema.org vocabularies.
* If `resource` is of type `schema:Role`, it is range acceptable if it has the same property with an acceptable value.
* If `resource` is of type `rdf:List` (must be previously entailed), it is range acceptable if all members of the list are otherwise range acceptable on the same property.

### Limiting vocabularies used for reasoning

As loading vocabularies can dominate processing time, the `RDF::Vocabulary.limit_vocabs` method can be used to set a specific set of vocabularies over which to reason. For example:

RDF::Vocabulary.limit_vocabs(:rdf, :rdf, :schema)

will limit the vocabularies which are returned from `RDF::Vocabulary.each`, which is used for reasoning and other operations over vocabularies and terms.

## Examples
### Determine super-classes of a class

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.6.0
0.6.1
9 changes: 5 additions & 4 deletions etc/doap.ttl
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@base <https://rubygems.org/gems/rdf-reasoner> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix dc: <http://purl.org/dc/terms/> .
Expand All @@ -7,10 +8,10 @@
@prefix ex: <http://example.org/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

<https://rubygems.org/gems/rdf-reasoner> a doap:Project, earl:TestSubject, earl:Software ;
<> a doap:Project, earl:TestSubject, earl:Software ;
doap:name "RDF::Reasoner" ;
doap:homepage <https://ruby-rdf.github.com/rdf-reasoner> ;
doap:license <http://creativecommons.org/licenses/publicdomain/> ;
doap:license <https://unlicense.org/1.0/> ;
doap:shortdesc "RDFS/OWL/Schema.org Reasoner for RDF.rb."@en ;
doap:description """
Reasons over RDFS/OWL vocabularies to generate statements which are
Expand All @@ -25,8 +26,8 @@
<http://www.w3.org/TR/rdf-schema/> ;
doap:category <http://dbpedia.org/resource/Resource_Description_Framework>,
<http://dbpedia.org/resource/Ruby_(programming_language)> ;
doap:download-page <https://rubygems.org/gems/rdf-reasoner> ;
doap:mailing-list <http://lists.w3.org/Archives/Public/public-rdf-ruby/> ;
doap:download-page <> ;
doap:mailing-list <https://lists.w3.org/Archives/Public/public-rdf-ruby/> ;
doap:bug-database <https://github.com/ruby-rdf/rdf-reasoner/issues> ;
doap:blog <https://greggkellogg.net/> ;
doap:developer <https://greggkellogg.net/foaf#me> ;
Expand Down
4 changes: 2 additions & 2 deletions lib/rdf/reasoner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module RDF
##
# RDFS/OWL reasonsing for RDF.rb.
#
# @see http://www.w3.org/TR/2013/REC-sparql11-entailment-20130321/
# @see https://www.w3.org/TR/2013/REC-sparql11-entailment-20130321/
# @author [Gregg Kellogg](https://greggkellogg.net/)
module Reasoner
require 'rdf/reasoner/format'
Expand All @@ -15,7 +15,7 @@ module Reasoner
autoload :Schema, 'rdf/reasoner/schema'
autoload :VERSION, 'rdf/reasoner/version'

# See http://www.pelagodesign.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/
# See https://www.pelagodesign.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/
#
#
ISO_8601 = %r(^
Expand Down
14 changes: 7 additions & 7 deletions lib/rdf/reasoner/extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ def entail(method, &block)
# @param [Hash{Symbol => Object}] options ({})
# @option options [Array<RDF::Vocabulary::Term>] :types
# Fully entailed types of resource, if not provided, they are queried
def domain_compatible?(resource, queryable, **options)
def domain_compatible?(resource, queryable, options = {})
%w(owl rdfs schema).map {|r| "domain_compatible_#{r}?".to_sym}.all? do |meth|
!self.respond_to?(meth) || self.send(meth, resource, queryable, **options)
!self.respond_to?(meth) || self.send(meth, resource, queryable, options)
end
end

Expand All @@ -50,9 +50,9 @@ def domain_compatible?(resource, queryable, **options)
# @param [Hash{Symbol => Object}] options ({})
# @option options [Array<RDF::Vocabulary::Term>] :types
# Fully entailed types of resource, if not provided, they are queried
def range_compatible?(resource, queryable, **options)
def range_compatible?(resource, queryable, options = {})
%w(owl rdfs schema).map {|r| "range_compatible_#{r}?".to_sym}.all? do |meth|
!self.respond_to?(meth) || self.send(meth, resource, queryable, **options)
!self.respond_to?(meth) || self.send(meth, resource, queryable, options)
end
end
end
Expand Down Expand Up @@ -89,7 +89,7 @@ def entail(method, &block)
# @param [Hash{Symbol => Object}] options ({})
# @option options [Array<RDF::Vocabulary::Term>] :types
# Fully entailed types of resource, if not provided, they are queried
def domain_compatible?(resource, queryable, **options)
def domain_compatible?(resource, queryable, options = {})
%w(owl rdfs schema).map {|r| "domain_compatible_#{r}?".to_sym}.all? do |meth|
!self.respond_to?(meth) || self.send(meth, resource, queryable, **options)
end
Expand All @@ -105,9 +105,9 @@ def domain_compatible?(resource, queryable, **options)
# @param [Hash{Symbol => Object}] options ({})
# @option options [Array<RDF::Vocabulary::Term>] :types
# Fully entailed types of resource, if not provided, they are queried
def range_compatible?(resource, queryable, **options)
def range_compatible?(resource, queryable, options = {})
%w(owl rdfs schema).map {|r| "range_compatible_#{r}?".to_sym}.all? do |meth|
!self.respond_to?(meth) || self.send(meth, resource, queryable, **options)
!self.respond_to?(meth) || self.send(meth, resource, queryable, options)
end
end
end
Expand Down
6 changes: 3 additions & 3 deletions lib/rdf/reasoner/rdfs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def subClass

##
# For a Term: yield or return inferred subPropertyOf relationships by recursively applying to named super classes to get a complete set of classes in the ancestor chain of this class
# For a Statement: yield or return inferred statements having a subPropertyOf relationship to predicate of this statement
# For a Statement: yield or return inferred statements having a subPropertyOf relationship to predicate of this statements
# @private
def _entail_subPropertyOf
case self
Expand Down Expand Up @@ -249,7 +249,7 @@ def _entail_range
# @param [Hash{Symbol => Object}] options ({})
# @option options [Array<RDF::Vocabulary::Term>] :types
# Fully entailed types of resource, if not provided, they are queried
def domain_compatible_rdfs?(resource, queryable, **options)
def domain_compatible_rdfs?(resource, queryable, options = {})
raise RDF::Reasoner::Error, "#{self} can't get domains" unless property?
domains = Array(self.domain).reject(&:node?) - [RDF::OWL.Thing, RDF::RDFS.Resource]

Expand All @@ -276,7 +276,7 @@ def domain_compatible_rdfs?(resource, queryable, **options)
# @param [Hash{Symbol => Object}] options ({})
# @option options [Array<RDF::Vocabulary::Term>] :types
# Fully entailed types of resource, if not provided, they are queried
def range_compatible_rdfs?(resource, queryable, **options)
def range_compatible_rdfs?(resource, queryable, options = {})
raise RDF::Reasoner::Error, "#{self} can't get ranges" unless property?
if !(ranges = Array(self.range).reject(&:node?) - [RDF::OWL.Thing, RDF::RDFS.Resource]).empty?
if resource.literal?
Expand Down
4 changes: 2 additions & 2 deletions lib/rdf/reasoner/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ module Schema
# @param [Hash{Symbol => Object}] options
# @option options [Array<RDF::Vocabulary::Term>] :types
# Fully entailed types of resource, if not provided, they are queried
def domain_compatible_schema?(resource, queryable, **options)
def domain_compatible_schema?(resource, queryable, options = {})
raise RDF::Reasoner::Error, "#{self} can't get domains" unless property?
domains = Array(self.domainIncludes) - [RDF::OWL.Thing]

Expand Down Expand Up @@ -52,7 +52,7 @@ def domain_compatible_schema?(resource, queryable, **options)
# @param [Hash{Symbol => Object}] options ({})
# @option options [Array<RDF::Vocabulary::Term>] :types
# Fully entailed types of resource, if not provided, they are queried
def range_compatible_schema?(resource, queryable, **options)
def range_compatible_schema?(resource, queryable, options = {})
raise RDF::Reasoner::Error, "#{self} can't get ranges" unless property?
if !(ranges = Array(self.rangeIncludes) - [RDF::OWL.Thing]).empty?
if resource.literal?
Expand Down
4 changes: 2 additions & 2 deletions rdf-reasoner.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ Gem::Specification.new do |gem|

gem.required_ruby_version = '>= 2.4'
gem.requirements = []
gem.add_runtime_dependency 'rdf', '~> 3.1'
gem.add_runtime_dependency 'rdf-vocab', '~> 3.1'
gem.add_runtime_dependency 'rdf', '~> 3.1', '>= 3.1.2'
gem.add_runtime_dependency 'rdf-vocab', '~> 3.1', '>= 3.1.5'
gem.add_runtime_dependency 'rdf-xsd', '~> 3.1'

gem.add_development_dependency 'rdf-spec', '~> 3.1'
Expand Down
56 changes: 45 additions & 11 deletions script/reason
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,43 @@
require 'rubygems'
$:.unshift(File.expand_path("../../lib", __FILE__))
require 'rdf/reasoner'
%w(linkeddata rdf/turtle rdf/vocab).each do |req|
%w(linkeddata rdf/turtle rdf/rdfa rdf/vocab).each do |req|
begin
require req
rescue LoadError
end
end

require 'getoptlong'
require 'ruby-prof'

def run(reader, **options)
repo = RDF::Repository.new << reader
def run(reader, file_name:, **options)
if options[:profile]
repo = RDF::Repository.new << reader

output_dir = File.expand_path("../../doc/profiles/#{File.basename file_name, '.*'}", __FILE__)
FileUtils.mkdir_p(output_dir)
profile = RubyProf::Profile.new
#profile.exclude_methods!(Array, :each, :map)
profile.exclude_method!(Hamster::Hash, :each)
profile.exclude_method!(Hamster::Trie, :each)
#profile.exclude_method!(Kernel, :require)
profile.exclude_method!(Object, :run)
profile.exclude_common_methods!
profile.start
run(repo, file_name: file_name, **options.merge(profile: false))
result = profile.stop

# Print a graph profile to text
printer = RubyProf::MultiPrinter.new(result)
printer.print(path: output_dir, profile: "profile")
puts "output saved in #{output_dir}"
return
end

repo = reader.is_a?(RDF::Queryable) ? reader : RDF::Repository.new << reader
stmt_cnt = repo.count
prefixes = reader.prefixes
prefixes = reader.respond_to?(:prefixes) ? reader.prefixes : {}
start = Time.new
if options[:entail]
repo.entail!
Expand All @@ -34,9 +58,13 @@ def run(reader, **options)
messages.each {|m| options[:output].puts " #{m}"}
end
end
elsif !options[:output_format]
# No output
secs = Time.new - start
STDERR.puts "\nReade #{repo.count} statements in #{secs} seconds" unless options[:quiet]
else
writer_options = options[:parser_options].merge(prefixes: prefixes, standard_prefixes: true)
RDF::Writer.for(options[:output_format] || :ttl).new(options[:output], writer_options) do |w|
RDF::Writer.for(options[:output_format]).new(options[:output], writer_options) do |w|
w << repo
end
end
Expand All @@ -49,7 +77,7 @@ parser_options = {base: nil}
options = {
parser_options: parser_options,
output: STDOUT,
output_format: :ttl,
output_format: nil,
input_format: nil,
}
input = nil
Expand All @@ -59,10 +87,12 @@ OPT_ARGS = [
["--format", GetoptLong::REQUIRED_ARGUMENT,"Specify output format when converting to RDF"],
["--input-format", GetoptLong::REQUIRED_ARGUMENT,"Format of the input document, when converting from RDF."],
["--output", "-o", GetoptLong::REQUIRED_ARGUMENT,"Output to the specified file path"],
["--profile", GetoptLong::NO_ARGUMENT, "Run profiler with output to doc/profiles/"],
["--quiet", GetoptLong::NO_ARGUMENT, "Supress most output other than progress indicators"],
["--uri", GetoptLong::REQUIRED_ARGUMENT,"URI to be used as the document base"],
["--validate", GetoptLong::NO_ARGUMENT, "Validate input graph with reasoner"],
["--help", "-?", GetoptLong::NO_ARGUMENT, "This message"]
['--vocabs', GetoptLong::REQUIRED_ARGUMENT,"Comma-separated list of vocabulary identifiers over which to limit reasoning"],
["--help", "-?", GetoptLong::NO_ARGUMENT, "This message"],
]
def usage
STDERR.puts %{Usage: #{$0} [options] file ...}
Expand All @@ -85,24 +115,28 @@ opts.each do |opt, arg|
case opt
when '--entail' then options[:entail] = true
when '--format' then options[:output_format] = arg.to_sym
when '--input-format' then options[:input_format] = arg.to_sym
when '--input-format' then parser_options[:format] = arg.to_sym
when '--output' then options[:output] = File.open(arg, "w")
when '--profile' then options[:profile] = true
when '--quiet' then options[:quiet] = true
when '--uri' then parser_options[:base] = arg
when '--validate' then options[:validate] = true
when '--vocabs' then (options[:vocabs] ||= []).concat(arg.split(',').map(&:strip))
when '--help' then usage
end
end

RDF::Vocabulary.limit_vocabs(*options[:vocabs]) if options[:vocabs]

if ARGV.empty?
s = input ? input : $stdin.read
RDF::Reader.for(options[:input_format] || :ntriples).new(input, **options) do |reader|
RDF::Reader.for(parser_options[:format] || :ntriples).new(input, file_name: 'stdin', **options) do |reader|
run(reader, **options)
end
else
ARGV.each do |file|
RDF::Reader.open(file, parser_options) do |reader|
run(reader, **options)
RDF::Reader.open(file, **parser_options) do |reader|
run(reader, file_name: file, **options)
end
end
end
Expand Down
6 changes: 1 addition & 5 deletions spec/rdfs_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@
RDF::Vocab::FOAF.aimChatID => [RDF::Vocab::FOAF.aimChatID, RDF::Vocab::FOAF.nick],
RDF::Vocab::FOAF.name => [RDF::Vocab::FOAF.name, RDF::RDFS.label],
RDF::Vocab::CC.license => [RDF::Vocab::CC.license, RDF::Vocab::DC.license],
RDF::Vocab::DC.date => [RDF::Vocab::DC.date, RDF::Vocab::DC11.date],
}.each do |prop, entails|
context prop.pname do
describe RDF::Vocabulary::Term do
Expand Down Expand Up @@ -140,10 +139,7 @@
# XXX this is cribbed from :subClass
describe :subProperty do
{
RDF::Vocab::DC.relation => %w(conformsTo hasFormat hasPart hasVersion
isFormatOf isPartOf isReferencedBy isReplacedBy isRequiredBy
isVersionOf references relation replaces requires source).map {
|t| RDF::Vocab::DC[t] } + %w(derived_from djmix_of mashup_of medley_of
RDF::Vocab::DC.source => %w(derived_from djmix_of mashup_of medley_of
remaster_of remix_of sampled_version_of).map {|t| RDF::Vocab::MO[t] },
RDF::Vocab::SIOC.space_of => %w(host_of).map {|t| RDF::Vocab::SIOC[t] },
}.each do |prop, entails|
Expand Down
2 changes: 1 addition & 1 deletion spec/suite_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def self.open_file(filename_or_url, **options, &block)
remote_document
end
else
original_open_file(filename_or_url, options, &block)
original_open_file(filename_or_url, **options, &block)
end
end
end
Expand Down
1 change: 1 addition & 0 deletions spec/suite_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def extract_vocab(graph, ndx)
if vocab_stmt
vocab_subject = vocab_stmt.subject
base = if vocab_subject.fragment
vocab_subject = vocab_subject.dup
vocab_subject.fragment = ""
vocab_subject
else
Expand Down

0 comments on commit a951166

Please sign in to comment.