rdf-reasoner 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/AUTHORS +1 -0
- data/README.md +59 -0
- data/UNLICENSE +24 -0
- data/VERSION +1 -0
- data/lib/rdf/reasoner.rb +29 -0
- data/lib/rdf/reasoner/extensions.rb +55 -0
- data/lib/rdf/reasoner/owl.rb +347 -0
- data/lib/rdf/reasoner/rdfs.rb +160 -0
- data/lib/rdf/reasoner/schema.rb +149 -0
- data/lib/rdf/reasoner/version.rb +18 -0
- metadata +161 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7b64147d73a5e38f5925417e9f090176cc01dfa1
|
4
|
+
data.tar.gz: cd4660566c88ac7074beec70172b92a6fd220706
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a8eb0b42cbfae2278cf3fe2c3b15aa9c6a985b600e9ab8b79f4b7feb3c6adcd7e89da0df91a6d5717a41d01aa650672c3f6e243f16028f206ac5bb49ec54e6dc
|
7
|
+
data.tar.gz: 35f93debd3072386883f6cdda9761a8aac60158f008ddd058c07420dae06f8d5f2883b5dde1bbab1b72204ef0a58c30afc238e124dba305aed12cf1be88b01ef
|
data/AUTHORS
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
* Gregg Kellogg <gregg@greggkellogg.net>
|
data/README.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# rdf-reasoner
|
2
|
+
|
3
|
+
Reasons over RDFS/OWL vocabularies and schema.org to generate statements which are entailed based on base RDFS/OWL rules along with vocabulary information. It can also be used to ask specific questions, such as if a given object is consistent with the vocabulary ruleset. This can be used to implement [SPARQL Entailment][] Regimes and [RDF Schema][] entailment.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
* Entail `rdfs:subClassOf` generating an array of terms which are ancestors of the subject.
|
8
|
+
* Entail `rdfs:subPropertyOf` generating an array of terms which are ancestors of the subject.
|
9
|
+
* Inverse `rdfs:subClassOf` entailment, to find descendant classes of the subject term.
|
10
|
+
* `domainCompatible?` determines if a particular resource is compatible with the domain definition of a given predicate, based on the intersection of entailed subclasses with the property domain.
|
11
|
+
* `rangeCompatible?` determines if a particular resource is compatible with the range definition of a given predicate, based on the intersection of entailed subclasses or literal types with the property domain.
|
12
|
+
|
13
|
+
Domain and Range entailment include specific rules for schema.org vocabularies.
|
14
|
+
|
15
|
+
## Dependencies
|
16
|
+
|
17
|
+
* [Ruby](http://ruby-lang.org/) (>= 1.9.2)
|
18
|
+
* [RDF.rb](http://rubygems.org/gems/rdf) (>= 1.1)
|
19
|
+
|
20
|
+
## Mailing List
|
21
|
+
|
22
|
+
* <http://lists.w3.org/Archives/Public/public-rdf-ruby/>
|
23
|
+
|
24
|
+
## Authors
|
25
|
+
|
26
|
+
* [Gregg Kellogg](http://github.com/gkellogg) - <http://greggkellogg.net/>
|
27
|
+
|
28
|
+
## Contributing
|
29
|
+
|
30
|
+
* Do your best to adhere to the existing coding conventions and idioms.
|
31
|
+
* Don't use hard tabs, and don't leave trailing whitespace on any line.
|
32
|
+
Before committing, run `git diff --check` to make sure of this.
|
33
|
+
* Do document every method you add using [YARD][] annotations. Read the
|
34
|
+
[tutorial][YARD-GS] or just look at the existing code for examples.
|
35
|
+
* Don't touch the `.gemspec`, `VERSION` or `AUTHORS` files. If you need to
|
36
|
+
change them, do so on your private branch only.
|
37
|
+
* Do feel free to add yourself to the `CREDITS` file and the corresponding
|
38
|
+
list in the the `README`. Alphabetical order applies.
|
39
|
+
* Do note that in order for us to merge any non-trivial changes (as a rule
|
40
|
+
of thumb, additions larger than about 15 lines of code), we need an
|
41
|
+
explicit [public domain dedication][PDD] on record from you.
|
42
|
+
|
43
|
+
## License
|
44
|
+
|
45
|
+
This is free and unencumbered public domain software. For more information,
|
46
|
+
see <http://unlicense.org/> or the accompanying {file:UNLICENSE} file.
|
47
|
+
|
48
|
+
[Ruby]: http://ruby-lang.org/
|
49
|
+
[RDF]: http://www.w3.org/RDF/
|
50
|
+
[YARD]: http://yardoc.org/
|
51
|
+
[YARD-GS]: http://rubydoc.info/docs/yard/file/docs/GettingStarted.md
|
52
|
+
[PDD]: http://lists.w3.org/Archives/Public/public-rdf-ruby/2010May/0013.html
|
53
|
+
[SPARQL]: http://en.wikipedia.org/wiki/SPARQL
|
54
|
+
[SPARQL Query]: http://www.w3.org/TR/2013/REC-sparql11-query-20130321/
|
55
|
+
[SPARQL Entailment]:http://www.w3.org/TR/sparql11-entailment/
|
56
|
+
[RDF 1.1]: http://www.w3.org/TR/rdf11-concepts
|
57
|
+
[RDF.rb]: http://rdf.rubyforge.org/
|
58
|
+
[RDF Schema]: http://www.w3.org/TR/rdf-schema/
|
59
|
+
[Rack]: http://rack.rubyforge.org/
|
data/UNLICENSE
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
This is free and unencumbered software released into the public domain.
|
2
|
+
|
3
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or
|
4
|
+
distribute this software, either in source code form or as a compiled
|
5
|
+
binary, for any purpose, commercial or non-commercial, and by any
|
6
|
+
means.
|
7
|
+
|
8
|
+
In jurisdictions that recognize copyright laws, the author or authors
|
9
|
+
of this software dedicate any and all copyright interest in the
|
10
|
+
software to the public domain. We make this dedication for the benefit
|
11
|
+
of the public at large and to the detriment of our heirs and
|
12
|
+
successors. We intend this dedication to be an overt act of
|
13
|
+
relinquishment in perpetuity of all present and future rights to this
|
14
|
+
software under copyright law.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
20
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
21
|
+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
24
|
+
For more information, please refer to <http://unlicense.org/>
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/lib/rdf/reasoner.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rdf'
|
2
|
+
require 'rdf/reasoner/extensions'
|
3
|
+
|
4
|
+
module RDF
|
5
|
+
##
|
6
|
+
# RDFS/OWL reasonsing for RDF.rb.
|
7
|
+
#
|
8
|
+
# @see http://www.w3.org/TR/2013/REC-sparql11-entailment-20130321/
|
9
|
+
# @author [Gregg Kellogg](http://greggkellogg.net/)
|
10
|
+
module Reasoner
|
11
|
+
autoload :OWL, 'rdf/reasoner/owl'
|
12
|
+
autoload :RDFS, 'rdf/reasoner/rdfs'
|
13
|
+
autoload :SCHEMA, 'rdf/reasoner/schema'
|
14
|
+
autoload :VERSION, 'rdf/reasoner/version'
|
15
|
+
|
16
|
+
##
|
17
|
+
# Add entailment support for the specified regime
|
18
|
+
#
|
19
|
+
# @param [Array<:owl, :rdfs, :schema>] regime
|
20
|
+
def apply(*regime)
|
21
|
+
regime.each {|r| require "rdf/reasoner/#{r.downcase}"}
|
22
|
+
end
|
23
|
+
module_function :apply
|
24
|
+
|
25
|
+
##
|
26
|
+
# A reasoner error
|
27
|
+
class Error < RuntimeError; end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Extensions to RDF core classes to support reasoning
|
2
|
+
require 'rdf'
|
3
|
+
|
4
|
+
class RDF::Vocabulary::Term
|
5
|
+
class << self
|
6
|
+
@@entailments = {}
|
7
|
+
|
8
|
+
##
|
9
|
+
# Add an entailment method. The method accepts no arguments, and returns an array of values associated with the particular entailment method
|
10
|
+
# @param [Symbol] method
|
11
|
+
# @param [Proc] proc
|
12
|
+
def add_entailment(method, proc)
|
13
|
+
@@entailments[method] = proc
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Perform an entailment on this term. Entailments defined within this module are `:subClassOf`, `:subPropertyOf`, and `:subClass`.
|
19
|
+
#
|
20
|
+
# @param [Symbol] method A registered entailment method
|
21
|
+
# @return [Array<Term>]
|
22
|
+
def entail(method)
|
23
|
+
self.send @@entailments.fetch(method)
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Determine if the domain of a property term is consistent with the specified resource in `queryable`.
|
28
|
+
#
|
29
|
+
# @param [RDF::Resource] resource
|
30
|
+
# @param [RDF::Queryable] queryable
|
31
|
+
# @param [Hash{Symbol => Object}] options ({})
|
32
|
+
# @option options [Array<RDF::Vocabulary::Term>] :types
|
33
|
+
# Fully entailed types of resource, if not provided, they are queried
|
34
|
+
def domain_compatible?(resource, queryable, options = {})
|
35
|
+
%w(owl rdfs schema).map {|r| "domain_compatible_#{r}?".to_sym}.all? do |meth|
|
36
|
+
!self.respond_to?(meth) || self.send(meth, resource, queryable, options)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Determine if the range of a property term is consistent with the specified resource in `queryable`.
|
42
|
+
#
|
43
|
+
# Specific entailment regimes should insert themselves before this to apply the appropriate semantic condition
|
44
|
+
#
|
45
|
+
# @param [RDF::Resource] resource
|
46
|
+
# @param [RDF::Queryable] queryable
|
47
|
+
# @param [Hash{Symbol => Object}] options ({})
|
48
|
+
# @option options [Array<RDF::Vocabulary::Term>] :types
|
49
|
+
# Fully entailed types of resource, if not provided, they are queried
|
50
|
+
def range_compatible?(resource, queryable, options = {})
|
51
|
+
%w(owl rdfs schema).map {|r| "range_compatible_#{r}?".to_sym}.all? do |meth|
|
52
|
+
!self.respond_to?(meth) || self.send(meth, resource, queryable, options)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,347 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module RDF::Reasoner
|
4
|
+
##
|
5
|
+
# Rules for generating OWL entailment triples
|
6
|
+
#
|
7
|
+
# Create instances for each owl:Class, owl:ObjectProperty, owl:DatatypeProperty, owl:DataType and owl:Restriction. This allows querying for querying specific entailed relationships of each instance.
|
8
|
+
class OWL
|
9
|
+
# Base class for OWL classes
|
10
|
+
class Base
|
11
|
+
# resource attribute, the IRI or BNode subject of the class
|
12
|
+
# #!attribute [r] resource
|
13
|
+
# @return [RDF::Resource]
|
14
|
+
attr_reader :resource
|
15
|
+
|
16
|
+
# RDF::Enumerable containing entity definition
|
17
|
+
#
|
18
|
+
# #!attribute [r] enumerable
|
19
|
+
# @return [RDF::Enumerable]
|
20
|
+
attr_reader :enumerable
|
21
|
+
|
22
|
+
class << self
|
23
|
+
# Class reader for all defined entities
|
24
|
+
# @!attribute [r] all
|
25
|
+
# @return [Array<Property>]
|
26
|
+
attr_reader :all
|
27
|
+
end
|
28
|
+
|
29
|
+
# Find instance based on this resource
|
30
|
+
# @param [RDF::Resource] resource
|
31
|
+
# @return [Base]
|
32
|
+
def self.find(resource)
|
33
|
+
all.detect {|r| r.resource == resource}
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# Create a new entity based on resource within enumerable
|
38
|
+
#
|
39
|
+
# @param [RDF::Resource] resource
|
40
|
+
# @param [RDF::Enumerable] enumerable
|
41
|
+
def initialize(resource, enumerable)
|
42
|
+
@resource, @enumerable = resource, enumerable
|
43
|
+
(Base.all ||= []) << self
|
44
|
+
end
|
45
|
+
|
46
|
+
# Human label of this class
|
47
|
+
# @return [String]
|
48
|
+
def label
|
49
|
+
@label || @resource.split(/\/\#/).last
|
50
|
+
end
|
51
|
+
|
52
|
+
# Infered ranges of the {Restriction} or {Property}
|
53
|
+
# FIXME: This does not account for intersection/union
|
54
|
+
# @return [Array<OwlClass,DataType>]
|
55
|
+
def ranges
|
56
|
+
values = @on_class || @all_values_from || @some_values_from || @on_data_range || @range
|
57
|
+
return [] unless values
|
58
|
+
values.map do |v|
|
59
|
+
v.one_of || v.union_of || ([v] + v.descendant_classes)
|
60
|
+
end.flatten
|
61
|
+
end
|
62
|
+
|
63
|
+
# Is this entity the same as `cls`, or is it a union containing `cls`?
|
64
|
+
# @param [OwlClass] cls
|
65
|
+
# @return [TrueClass, FalseClass]
|
66
|
+
def class_of?(cls)
|
67
|
+
cls == self or union_of.include?(cls)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Override for actual descendant classes
|
71
|
+
# @return [Array]
|
72
|
+
def descendant_classes; []; end
|
73
|
+
|
74
|
+
# Is this a named entity (i.e., not some OWL construction)
|
75
|
+
# @return [TrueClass, FalseClass]
|
76
|
+
def named?
|
77
|
+
@resource.iri?
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Accessors for entity fields
|
82
|
+
#
|
83
|
+
# @overload all_values_from
|
84
|
+
# @return [Array<Base>] reflects owl:allValuesFrom
|
85
|
+
# @overload _cardinality
|
86
|
+
# @return [Integer]
|
87
|
+
# reflects owl:cardinality and owl:qualifiedCardinality.
|
88
|
+
# @see {Restriction#cardinality}
|
89
|
+
# @overload equivalent_property
|
90
|
+
# @return [Array<Property>] reflects owl:equivalentProperty
|
91
|
+
# ...
|
92
|
+
def method_missing(meth, *args)
|
93
|
+
def _access(preds, how)
|
94
|
+
@access ||= {}
|
95
|
+
# Memoize result
|
96
|
+
@access[meth] ||= begin
|
97
|
+
values = []
|
98
|
+
preds.each do |pred|
|
99
|
+
enumerable.query(:subject => resource, :predicate => pred) do |statement|
|
100
|
+
values << statement.object
|
101
|
+
end
|
102
|
+
end
|
103
|
+
case how
|
104
|
+
when true
|
105
|
+
# Value is simply true
|
106
|
+
true
|
107
|
+
when :ary
|
108
|
+
# Translate IRIs into instances of Base
|
109
|
+
values.map {|v| Base.find(v) || v}
|
110
|
+
when :list
|
111
|
+
# Access as list and translate IRIs into instances of Base
|
112
|
+
RDF::List(values.first, enumerable).to_a.map {|v| Base.find(v) || v}
|
113
|
+
when :obj, :int
|
114
|
+
# Take first element of array, and optionally translate to integer
|
115
|
+
how == :int ? values.first.to_i : values.first
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
case :meth
|
121
|
+
when :all_values_from then _access([RDF::OWL::allValuesFrom], :ary)
|
122
|
+
when :_cardinality then _access([RDF::OWL::cardinality, RDF::OWL::qualifiedCardinality], :int)
|
123
|
+
when :domain then _access([RDF::RDFS::domain], :ary)
|
124
|
+
when :equivalent_property then _access([RDF::OWL::equivalentProperty], :ary)
|
125
|
+
when :has_self then _access([RDF::OWL::hasSelf], true)
|
126
|
+
when :has_value then _access([RDF::OWL::hasValue], :ary)
|
127
|
+
when :intersection_of then _access([RDF::OWL::hasSelf], true)
|
128
|
+
when :inverse_of then _access([RDF::OWL::inverseOf], :obj)
|
129
|
+
when :max_cardinality then _access([RDF::OWL::maxCardinality, RDF::OWL::maxQualifiedCardinality], :int)
|
130
|
+
when :min_cardinality then _access([RDF::OWL::minCardinality, RDF::OWL::minQualifiedCardinality], :int)
|
131
|
+
when :one_of then _access([RDF::OWL::oneOf], :list)
|
132
|
+
when :on_class then _access([RDF::OWL::onClass], :obj)
|
133
|
+
when :on_datarange then _access([RDF::OWL::onDatatype], :obj)
|
134
|
+
when :on_property then _access([RDF::OWL::onProperty], :obj)
|
135
|
+
when :range then _access([RDF::RDFS::range], :ary)
|
136
|
+
when :some_values_from then _access([RDF::OWL::someValuesFrom], :ary)
|
137
|
+
when :sub_class_of then _access([RDF::RDFS::subClassOf], :ary)
|
138
|
+
when :sub_property_of then _access([RDF::RDFS::subPropertyOf], :ary)
|
139
|
+
when :union_of then _access([RDF::OWL::unionOf], :list)
|
140
|
+
when :with_restrictions then _access([RDF::OWL::withRestrictions], :list)
|
141
|
+
else
|
142
|
+
super
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Entries for owl objects which are Object or Datatype Properties
|
148
|
+
class Property < Base
|
149
|
+
class << self
|
150
|
+
# Class reader for all defined properties
|
151
|
+
# @!attribute [r] all
|
152
|
+
# @return [Array<Property>]
|
153
|
+
attr_reader :all
|
154
|
+
end
|
155
|
+
|
156
|
+
##
|
157
|
+
# Create a new property based on resource within enumerable
|
158
|
+
#
|
159
|
+
# @param [RDF::Resource] resource
|
160
|
+
# @param [RDF::Enumerable] enumerable
|
161
|
+
def initialize(resource, enumerable)
|
162
|
+
super
|
163
|
+
(Property.all ||= []) << self
|
164
|
+
end
|
165
|
+
|
166
|
+
# Infered domains of this property
|
167
|
+
# FIXME: does not account for intersection/union, which is uncomon in domains
|
168
|
+
# @return [Array<OwlClass>]
|
169
|
+
def domains
|
170
|
+
self.domain.map do |v|
|
171
|
+
v.one_of || v.union_of || ([v] + v.descendant_classes)
|
172
|
+
end.flatten.uniq
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
# Does this property have a domain including cls?
|
177
|
+
# The JSON is defined to always represent domain as an array
|
178
|
+
# FIXME: this doesn't deal with intersection
|
179
|
+
# @param [OwlClass] cls
|
180
|
+
# @return [TrueClass, FalseClass]
|
181
|
+
def domain_of?(cls)
|
182
|
+
domain.any? {|dom| dom.class_of?(cls)}
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
# OWL Restrictions are similar to classes. They impose a restriction on
|
188
|
+
# values of some property, such as cardinality
|
189
|
+
class Restriction < Base
|
190
|
+
# For restrictions, return any defined cardinality
|
191
|
+
# as an array of \[min, max\] where either max may be `nil`.
|
192
|
+
# Min will always be an integer.
|
193
|
+
# @return [Array<(Integer, Integer)>]
|
194
|
+
def cardinality
|
195
|
+
[(@cardinality || @min_cardinality || 0), (@cardinality || @max_cardinality)]
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Entries for owl objects wich are classes
|
200
|
+
class OwlClass < Base
|
201
|
+
class << self
|
202
|
+
# Class accessor for all defined classes
|
203
|
+
# @!attribute [r] all
|
204
|
+
# @return [Array<OwlClass>]
|
205
|
+
attr_reader :all
|
206
|
+
end
|
207
|
+
|
208
|
+
##
|
209
|
+
# Create a new class based on resource within enumerable
|
210
|
+
#
|
211
|
+
# @param [RDF::Resource] resource
|
212
|
+
# @param [RDF::Enumerable] enumerable
|
213
|
+
def initialize(resource, enumerable)
|
214
|
+
super
|
215
|
+
(OwlClass.all ||= []) << self
|
216
|
+
end
|
217
|
+
|
218
|
+
# Return super classes as an S-Expression
|
219
|
+
# @return [Array<OwlClass>]
|
220
|
+
def super_classes
|
221
|
+
@super_class_cache ||= begin
|
222
|
+
# Note in super-class, that this is a direct sub-class
|
223
|
+
sup = self.sub_class_of
|
224
|
+
|
225
|
+
anded_classes = sup.map do |cls|
|
226
|
+
if cls.is_a?(OwlClass) and cls.named?
|
227
|
+
cls
|
228
|
+
elsif cls.unionOf
|
229
|
+
ored_classes = cls.union_of.select {|c2| c2.is_a?(OwlClass)}.compact
|
230
|
+
case ored_classes.length
|
231
|
+
when 0 then nil
|
232
|
+
when 1 then ored_classes.first
|
233
|
+
else (%w(|) + ored_classes).freeze
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end.compact
|
237
|
+
|
238
|
+
case anded_classes.length
|
239
|
+
when 0 then [].freeze
|
240
|
+
when 1 then anded_classes.first
|
241
|
+
else (%w(&) + anded_classes).freeze
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# Return all direct sub-classes
|
247
|
+
# This counts on each class having had superClasses calculated to
|
248
|
+
# inject the sub-class relation
|
249
|
+
# @return [Array<OwlClass>]
|
250
|
+
def sub_classes
|
251
|
+
@sub_classes_cache ||= OwlClass.all.select do |c|
|
252
|
+
c.sub_class_of.any? {|cl| cl.class_of?(self)}
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# Return all descendant classes
|
257
|
+
# @return [Array<OwlClass>]
|
258
|
+
def descendant_classes
|
259
|
+
@descendant_classes ||= begin
|
260
|
+
(sub_classes + sub_classes.map {|cls| cls.descendant_classes}.flatten).compact.freeze
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
# Return a list of all property restrictions on this class and super-classes
|
265
|
+
# @return [Array<Restriction>]
|
266
|
+
def property_restrictions
|
267
|
+
@property_restrictions_cache ||= begin
|
268
|
+
restrictions = evaluate_sexp(self.super_classes) {|c| c.property_restrictions}.dup
|
269
|
+
restrictions = [restrictions].compact unless restrictions.is_a?(Array)
|
270
|
+
# Add restrictions defined on this class
|
271
|
+
self.sub_class_of.select {|r| r.is_a?(Restriction)}.each do |restriction|
|
272
|
+
prop = restriction.on_property
|
273
|
+
# Remove any existing restriction on the same property
|
274
|
+
restrictions.reject! {|r| r.on_property == prop}
|
275
|
+
#puts "#{resource}: add local restriction: #{restriction.on_property.resource}"
|
276
|
+
restrictions << restriction
|
277
|
+
end
|
278
|
+
|
279
|
+
restrictions.freeze
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
# Return a list of all properties on this class and super-classes
|
284
|
+
# @return [Array<Property>]
|
285
|
+
def properties
|
286
|
+
@properties_cache ||= begin
|
287
|
+
# Inherited properties
|
288
|
+
props = evaluate_sexp(super_classes) {|c| c.properties}
|
289
|
+
|
290
|
+
# Add properties directly referencing this class
|
291
|
+
direct_props = Property.all.select do |prop|
|
292
|
+
prop.domain_of?(self) and
|
293
|
+
!props.include?(prop)
|
294
|
+
end
|
295
|
+
|
296
|
+
(props + direct_props).sort.freeze
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
private
|
301
|
+
# Evaluate a subclass S-Expression
|
302
|
+
def evaluate_sexp(classes)
|
303
|
+
case classes
|
304
|
+
when OWL then yield classes
|
305
|
+
when Array
|
306
|
+
case classes.first
|
307
|
+
when '|' # Union of classes
|
308
|
+
cls = yield(classes[1]).dup
|
309
|
+
classes[2..-1].each {|cls2| cls = cls | yield(cls2)}
|
310
|
+
cls
|
311
|
+
when '&' # Intersection of classes
|
312
|
+
cls = yield(classes[1]).dup
|
313
|
+
classes[2..-1].each {|cls2| cls = cls & yield(cls2)}
|
314
|
+
cls
|
315
|
+
else
|
316
|
+
$logger.error "Unexpected operator in S-Exp for #{@resource}: #{classes.inspect}" unless classes.empty?
|
317
|
+
classes.dup
|
318
|
+
end
|
319
|
+
else
|
320
|
+
$logger.error "Unexpected value in S-Exp for #{@resource}: #{classes.inspect}" if classes
|
321
|
+
nil
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
# Entries for XSD and RDF datatypes
|
327
|
+
class DataType < Base
|
328
|
+
attr_accessor :rdf_literal_class
|
329
|
+
|
330
|
+
def initialize(object)
|
331
|
+
super
|
332
|
+
@rdf_literal_class = RDF::Literal
|
333
|
+
end
|
334
|
+
|
335
|
+
def validate(value)
|
336
|
+
if (rdf_literal_class.new(value).valid? rescue false)
|
337
|
+
case
|
338
|
+
when one_of
|
339
|
+
"#{value.inspect} must be one of #{one_of.inspect}" unless one_of.include?(value)
|
340
|
+
end
|
341
|
+
else
|
342
|
+
"#{value.inspect} is not a valid #{label}(#{resource})"
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
module RDF::Reasoner
|
4
|
+
##
|
5
|
+
# Rules for generating RDFS entailment triples
|
6
|
+
#
|
7
|
+
# Extends `RDF::Vocabulary::Term` with specific entailment capabilities
|
8
|
+
module RDFS
|
9
|
+
##
|
10
|
+
# @return [RDF::Util::Cache]
|
11
|
+
# @private
|
12
|
+
def subClassOf_cache
|
13
|
+
@@subClassOf_cache ||= RDF::Util::Cache.new(-1)
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# @return [RDF::Util::Cache]
|
18
|
+
# @private
|
19
|
+
def subClass_cache
|
20
|
+
@@subClass_cache_cache ||= RDF::Util::Cache.new(-1)
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# @return [RDF::Util::Cache]
|
25
|
+
# @private
|
26
|
+
def descendant_cache
|
27
|
+
@@descendant_cache ||= RDF::Util::Cache.new(-1)
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# @return [RDF::Util::Cache]
|
32
|
+
# @private
|
33
|
+
def subPropertyOf_cache
|
34
|
+
@@subPropertyOf_cache ||= RDF::Util::Cache.new(-1)
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Return inferred subClassOf relationships by recursively applying to named super classes to get a complete set of classes in the ancestor chain of this class
|
39
|
+
# @private
|
40
|
+
def _entail_subClassOf
|
41
|
+
raise RDF::Reasoner::Error, "#{self} Can't entail subClassOf" unless class?
|
42
|
+
subClassOf_cache[self] ||= begin
|
43
|
+
(Array(self.subClassOf).map {|c| c._entail_subClassOf rescue c}.flatten + Array(self)).compact
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
##
|
48
|
+
# Return inferred subClass relationships by recursively applying to named sub classes to get a complete set of classes in the descendant chain of this class
|
49
|
+
# @private
|
50
|
+
def _entail_subClass
|
51
|
+
raise RDF::Reasoner::Error, "#{self} Can't entail subClass" unless class?
|
52
|
+
descendant_cache[self] ||= begin
|
53
|
+
(Array(self.subClass).map {|c| c._entail_subClass rescue c}.flatten + Array(self)).compact
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Get the immediate subclasses of this class
|
59
|
+
# @return [Array<RDF::Vocabulary::Term>]
|
60
|
+
def subClass
|
61
|
+
raise RDF::Reasoner::Error, "#{self} Can't entail subClass" unless class?
|
62
|
+
subClass_cache[self] ||= ::RDF::Vocabulary.map do |v|
|
63
|
+
Array(v.properties).select {|p| p.class? && Array(p.subClassOf).include?(self)}
|
64
|
+
end.flatten.compact
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# 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
|
69
|
+
# @private
|
70
|
+
def _entail_subPropertyOf
|
71
|
+
raise RDF::Reasoner::Error, "#{self} Can't entail subPropertyOf" unless property?
|
72
|
+
subPropertyOf_cache[self] ||= begin
|
73
|
+
(Array(self.subPropertyOf).map {|c| c._entail_subPropertyOf rescue c}.flatten + Array(self)).compact
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
##
|
78
|
+
# RDFS requires that if the property has a domain, and the resource has a type that some type matches every domain.
|
79
|
+
#
|
80
|
+
# Note that this is different than standard entailment, which simply asserts that the resource has every type in the domain, but this is more useful to check if published data is consistent with the vocabulary definition.
|
81
|
+
#
|
82
|
+
# @param [RDF::Resource] resource
|
83
|
+
# @param [RDF::Queryable] queryable
|
84
|
+
# @param [Hash{Symbol => Object}] options ({})
|
85
|
+
# @option options [Array<RDF::Vocabulary::Term>] :types
|
86
|
+
# Fully entailed types of resource, if not provided, they are queried
|
87
|
+
def domain_compatible_rdfs?(resource, queryable, options = {})
|
88
|
+
raise RDF::Reasoner::Error, "#{self} can't get domains" unless property?
|
89
|
+
if respond_to?(:domain)
|
90
|
+
domains = Array(self.domain) - [RDF::OWL.Thing]
|
91
|
+
|
92
|
+
# Fully entailed types of the resource
|
93
|
+
types = options.fetch(:types) do
|
94
|
+
queryable.query(:subject => resource, :predicate => RDF.type).
|
95
|
+
map {|s| (t = RDF::Vocabulary.find_term(s.object)) && t.entail(:subClassOf)}.
|
96
|
+
flatten.
|
97
|
+
uniq.
|
98
|
+
compact
|
99
|
+
end unless domains.empty?
|
100
|
+
|
101
|
+
# Every domain must match some entailed type
|
102
|
+
Array(types).empty? || domains.all? {|d| types.include?(d)}
|
103
|
+
else
|
104
|
+
true
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
# RDFS requires that if the property has a range, and the resource has a type that some type matches every range. If the resource is a datatyped Literal, and the range includes a datatype, the resource must be consistent with that.
|
110
|
+
#
|
111
|
+
# Note that this is different than standard entailment, which simply asserts that the resource has every type in the range, but this is more useful to check if published data is consistent with the vocabulary definition.
|
112
|
+
#
|
113
|
+
# @param [RDF::Resource] resource
|
114
|
+
# @param [RDF::Queryable] queryable
|
115
|
+
# @param [Hash{Symbol => Object}] options ({})
|
116
|
+
# @option options [Array<RDF::Vocabulary::Term>] :types
|
117
|
+
# Fully entailed types of resource, if not provided, they are queried
|
118
|
+
def range_compatible_rdfs?(resource, queryable, options = {})
|
119
|
+
raise RDF::Reasoner::Error, "#{self} can't get ranges" unless property?
|
120
|
+
if respond_to?(:range) && !(ranges = Array(self.range) - [RDF::OWL.Thing]).empty?
|
121
|
+
if resource.literal?
|
122
|
+
ranges.all? do |range|
|
123
|
+
case range
|
124
|
+
when RDF::RDFS.Literal, RDF.XMLLiteral, RDF.HTML then true
|
125
|
+
else
|
126
|
+
if range.start_with?(RDF::XSD)
|
127
|
+
resource.datatype == range ||
|
128
|
+
resource.simple? && RDF::Literal::Boolean.new(resource.value).valid?
|
129
|
+
else
|
130
|
+
false
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
else
|
135
|
+
# Fully entailed types of the resource
|
136
|
+
types = options.fetch(:types) do
|
137
|
+
queryable.query(:subject => resource, :predicate => RDF.type).
|
138
|
+
map {|s| (t = RDF::Vocabulary.find_term(s.object)) && t.entail(:subClassOf)}.
|
139
|
+
flatten.
|
140
|
+
uniq.
|
141
|
+
compact
|
142
|
+
end
|
143
|
+
# Every range must match some entailed type
|
144
|
+
Array(types).empty? || ranges.all? {|d| types.include?(d)}
|
145
|
+
end
|
146
|
+
else
|
147
|
+
true
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.included(mod)
|
152
|
+
mod.add_entailment :subClassOf, :_entail_subClassOf
|
153
|
+
mod.add_entailment :subClass, :_entail_subClass
|
154
|
+
mod.add_entailment :subPropertyOf, :_entail_subPropertyOf
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Extend the Term with this methods
|
159
|
+
::RDF::Vocabulary::Term.send(:include, RDFS)
|
160
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# Also requires RDFS reasoner
|
4
|
+
require 'rdf/reasoner/rdfs'
|
5
|
+
|
6
|
+
module RDF::Reasoner
|
7
|
+
##
|
8
|
+
# Rules for generating RDFS entailment triples
|
9
|
+
#
|
10
|
+
# Extends `RDF::Vocabulary::Term` with specific entailment capabilities
|
11
|
+
module SCHEMA
|
12
|
+
# domain_includes accessor
|
13
|
+
# @return [Array<RDF::Vocabulary::Term>]
|
14
|
+
def domain_includes
|
15
|
+
Array(@attributes["schema:domainIncludes"]).map {|v| RDF::Vocabulary.expand_pname(v)}
|
16
|
+
end
|
17
|
+
alias_method :domainIncludes, :domain_includes
|
18
|
+
|
19
|
+
# range_includes accessor
|
20
|
+
# @return [Array<RDF::Vocabulary::Term>]
|
21
|
+
def range_includes
|
22
|
+
Array(@attributes["schema:rangeIncludes"]).map {|v| RDF::Vocabulary.expand_pname(v)}
|
23
|
+
end
|
24
|
+
alias_method :rangeIncludes, :range_includes
|
25
|
+
|
26
|
+
##
|
27
|
+
# Schema.org requires that if the property has a domain, and the resource has a type that some type matches some domain.
|
28
|
+
#
|
29
|
+
# Note that this is different than standard entailment, which simply asserts that the resource has every type in the domain, but this is more useful to check if published data is consistent with the vocabulary definition.
|
30
|
+
#
|
31
|
+
# @param [RDF::Resource] resource
|
32
|
+
# @param [RDF::Queryable] queryable
|
33
|
+
# @param [Hash{Symbol => Object}] options
|
34
|
+
# @option options [Array<RDF::Vocabulary::Term>] :types
|
35
|
+
# Fully entailed types of resource, if not provided, they are queried
|
36
|
+
def domain_compatible_schema?(resource, queryable, options = {})
|
37
|
+
raise RDF::Reasoner::Error, "#{self} can't get domains" unless property?
|
38
|
+
domains = Array(self.domainIncludes) - [RDF::OWL.Thing]
|
39
|
+
|
40
|
+
# Fully entailed types of the resource
|
41
|
+
types = options.fetch(:types) do
|
42
|
+
queryable.query(:subject => resource, :predicate => RDF.type).
|
43
|
+
map {|s| (t = RDF::Vocabulary.find_term(s.object)) && t.entail(:subClassOf)}.
|
44
|
+
flatten.
|
45
|
+
uniq.
|
46
|
+
compact
|
47
|
+
end unless domains.empty?
|
48
|
+
|
49
|
+
# Every domain must match some entailed type
|
50
|
+
Array(types).empty? || domains.any? {|d| types.include?(d)}
|
51
|
+
end
|
52
|
+
|
53
|
+
##
|
54
|
+
# Schema.org requires that if the property has a range, and the resource has a type that some type matches some range. If the resource is a datatyped Literal, and the range includes a datatype, the resource must be consistent with that.
|
55
|
+
#
|
56
|
+
# Also, a plain literal (or schema:Text) is always compatible with an object range.
|
57
|
+
#
|
58
|
+
# @param [RDF::Resource] resource
|
59
|
+
# @param [RDF::Queryable] queryable
|
60
|
+
# @param [Hash{Symbol => Object}] options ({})
|
61
|
+
# @option options [Array<RDF::Vocabulary::Term>] :types
|
62
|
+
# Fully entailed types of resource, if not provided, they are queried
|
63
|
+
def range_compatible_schema?(resource, queryable, options = {})
|
64
|
+
raise RDF::Reasoner::Error, "#{self} can't get ranges" unless property?
|
65
|
+
if respond_to?(:rangeIncludes) && !(ranges = Array(self.rangeIncludes) - [RDF::OWL.Thing]).empty?
|
66
|
+
if resource.literal?
|
67
|
+
ranges.any? do |range|
|
68
|
+
case range
|
69
|
+
when RDF::RDFS.Literal then true
|
70
|
+
when RDF::SCHEMA.Text then resource.plain? || resource.datatype == RDF::SCHEMA.Text
|
71
|
+
when RDF::SCHEMA.Boolean
|
72
|
+
[RDF::SCHEMA.Boolean, RDF::XSD.boolean].include?(resource.datatype) ||
|
73
|
+
resource.simple? && RDF::Literal::Boolean.new(resource.value).valid?
|
74
|
+
when RDF::SCHEMA.Date
|
75
|
+
resource.datatype == RDF::SCHEMA.Date ||
|
76
|
+
resource.is_a?(RDF::Literal::Date) ||
|
77
|
+
resource.simple? && RDF::Literal::Date.new(resource.value).valid?
|
78
|
+
when RDF::SCHEMA.DateTime
|
79
|
+
resource.datatype == RDF::SCHEMA.DateTime ||
|
80
|
+
resource.is_a?(RDF::Literal::DateTime) ||
|
81
|
+
resource.simple? && RDF::Literal::DateTime.new(resource.value).valid?
|
82
|
+
when RDF::SCHEMA.Duration
|
83
|
+
value = resource.value
|
84
|
+
value = "P#{value}" unless value.start_with?("P")
|
85
|
+
resource.datatype == RDF::SCHEMA.Duration ||
|
86
|
+
resource.is_a?(RDF::Literal::Duration) ||
|
87
|
+
resource.simple? && RDF::Literal::Duration.new(value).valid?
|
88
|
+
when RDF::SCHEMA.Time
|
89
|
+
resource.datatype == RDF::SCHEMA.Time ||
|
90
|
+
resource.is_a?(RDF::Literal::Time) ||
|
91
|
+
resource.simple? && RDF::Literal::Time.new(resource.value).valid?
|
92
|
+
when RDF::SCHEMA.Number
|
93
|
+
resource.is_a?(RDF::Literal::Numeric) ||
|
94
|
+
[RDF::SCHEMA.Number, RDF::SCHEMA.Float, RDF::SCHEMA.Integer].include?(resource.datatype) ||
|
95
|
+
resource.simple? && RDF::Literal::Integer.new(resource.value).valid? ||
|
96
|
+
resource.simple? && RDF::Literal::Double.new(resource.value).valid?
|
97
|
+
when RDF::SCHEMA.Float
|
98
|
+
resource.is_a?(RDF::Literal::Double) ||
|
99
|
+
[RDF::SCHEMA.Number, RDF::SCHEMA.Float].include?(resource.datatype) ||
|
100
|
+
resource.simple? && RDF::Literal::Double.new(resource.value).valid?
|
101
|
+
when RDF::SCHEMA.Integer
|
102
|
+
resource.is_a?(RDF::Literal::Integer) ||
|
103
|
+
[RDF::SCHEMA.Number, RDF::SCHEMA.Integer].include?(resource.datatype) ||
|
104
|
+
resource.simple? && RDF::Literal::Integer.new(resource.value).valid?
|
105
|
+
when RDF::SCHEMA.URL
|
106
|
+
resource.datatype == RDF::SCHEMA.URL ||
|
107
|
+
resource.datatype == RDF::XSD.anyURI ||
|
108
|
+
resource.simple? && RDF::Literal::AnyURI.new(resource.value).valid?
|
109
|
+
else
|
110
|
+
# If this is an XSD range, look for appropriate literal
|
111
|
+
if range.start_with?(RDF::XSD.to_s)
|
112
|
+
if resource.datatype == RDF::URI(range)
|
113
|
+
true
|
114
|
+
else
|
115
|
+
# Valid if cast as datatype
|
116
|
+
resource.simple? && RDF::Literal(resource.value, :datatype => RDF::URI(range)).valid?
|
117
|
+
end
|
118
|
+
else
|
119
|
+
# Otherwise, presume that the range refers to a typed resource
|
120
|
+
false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
elsif %w(True False).map {|v| RDF::SCHEMA[v]}.include?(resource) && ranges.include?(RDF::SCHEMA.Boolean)
|
125
|
+
true # Special case for schema boolean resources
|
126
|
+
else
|
127
|
+
# Fully entailed types of the resource
|
128
|
+
types = options.fetch(:types) do
|
129
|
+
queryable.query(:subject => resource, :predicate => RDF.type).
|
130
|
+
map {|s| (t = RDF::Vocabulary.find_term(s.object)) && t.entail(:subClassOf)}.
|
131
|
+
flatten.
|
132
|
+
uniq.
|
133
|
+
compact
|
134
|
+
end
|
135
|
+
# Every range must match some entailed type
|
136
|
+
Array(types).empty? || ranges.any? {|d| types.include?(d)}
|
137
|
+
end
|
138
|
+
else
|
139
|
+
true
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.included(mod)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Extend the Term with this methods
|
148
|
+
::RDF::Vocabulary::Term.send(:include, SCHEMA)
|
149
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module RDF::Reasoner::VERSION
|
2
|
+
VERSION_FILE = File.join(File.expand_path(File.dirname(__FILE__)), "..", "..", "..", "VERSION")
|
3
|
+
MAJOR, MINOR, TINY, EXTRA = File.read(VERSION_FILE).chop.split(".")
|
4
|
+
|
5
|
+
STRING = [MAJOR, MINOR, TINY, EXTRA].compact.join('.')
|
6
|
+
|
7
|
+
##
|
8
|
+
# @return [String]
|
9
|
+
def self.to_s() STRING end
|
10
|
+
|
11
|
+
##
|
12
|
+
# @return [String]
|
13
|
+
def self.to_str() STRING end
|
14
|
+
|
15
|
+
##
|
16
|
+
# @return [Array(Integer, Integer, Integer)]
|
17
|
+
def self.to_a() STRING.split(".") end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rdf-reasoner
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Gregg Kellogg
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-06-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rdf
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.1'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.1.4
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.1'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.1.4
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rdf-xsd
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.1'
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.1'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rdf-turtle
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.1'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '1.1'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: linkeddata
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '1.1'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '1.1'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: equivalent-xml
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0.4'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0.4'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: rspec
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '2.14'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '2.14'
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: yard
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0.8'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - "~>"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0.8'
|
117
|
+
description: Reasons over RDFS/OWL vocabularies to generate statements which are entailed
|
118
|
+
based on base RDFS/OWL rules along with vocabulary information. It can also be used
|
119
|
+
to ask specific questions, such as if a given object is consistent with the vocabulary
|
120
|
+
ruleset. This can be used to implement SPARQL Entailment Regimes.
|
121
|
+
email: public-rdf-ruby@w3.org
|
122
|
+
executables: []
|
123
|
+
extensions: []
|
124
|
+
extra_rdoc_files: []
|
125
|
+
files:
|
126
|
+
- AUTHORS
|
127
|
+
- README.md
|
128
|
+
- UNLICENSE
|
129
|
+
- VERSION
|
130
|
+
- lib/rdf/reasoner.rb
|
131
|
+
- lib/rdf/reasoner/extensions.rb
|
132
|
+
- lib/rdf/reasoner/owl.rb
|
133
|
+
- lib/rdf/reasoner/rdfs.rb
|
134
|
+
- lib/rdf/reasoner/schema.rb
|
135
|
+
- lib/rdf/reasoner/version.rb
|
136
|
+
homepage: http://github.com/gkellogg/rdf-reasoner
|
137
|
+
licenses:
|
138
|
+
- Public Domain
|
139
|
+
metadata: {}
|
140
|
+
post_install_message:
|
141
|
+
rdoc_options: []
|
142
|
+
require_paths:
|
143
|
+
- lib
|
144
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
145
|
+
requirements:
|
146
|
+
- - ">="
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: 1.9.3
|
149
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - ">="
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
requirements: []
|
155
|
+
rubyforge_project:
|
156
|
+
rubygems_version: 2.2.2
|
157
|
+
signing_key:
|
158
|
+
specification_version: 4
|
159
|
+
summary: RDFS/OWL Reasoner for RDF.rb
|
160
|
+
test_files: []
|
161
|
+
has_rdoc: false
|