rdf-reasoner 0.5.3 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +24 -5
- data/VERSION +1 -1
- data/lib/rdf/reasoner.rb +2 -3
- data/lib/rdf/reasoner/extensions.rb +8 -8
- data/lib/rdf/reasoner/owl.rb +2 -2
- data/lib/rdf/reasoner/rdfs.rb +21 -21
- data/lib/rdf/reasoner/schema.rb +91 -35
- metadata +39 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9089d2c2502a1b5bbb19919650e0cf29d447487c219c4f5ebd362b9146204e09
|
4
|
+
data.tar.gz: 9f0ffb2e5ec7b15bbd1a6773bcd7abf0dc3f2f2f55f61b9128affd5ed3ab9fa7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d7ca4b250631e17f869e0c4161765c0af68cace0df7631a292cb9a3da7613140e6176b03d0c6a7692ad0a397e4448779d3f1dba6449d94f9f1ecfdb0a96dac2
|
7
|
+
data.tar.gz: 3b0320de434a264a235423b4e5c5b06b2430861f26931f7c1cdee4dcb78ad9396ecede761fd316219a71e99039f54fd3ac194e3cfe4744be50290064bcaa4308
|
data/README.md
CHANGED
@@ -1,4 +1,13 @@
|
|
1
|
-
#
|
1
|
+
# RDF::Reasoner
|
2
|
+
|
3
|
+
A partial RDFS/OWL/schema.org reasoner for [RDF.rb][].
|
4
|
+
|
5
|
+
[](https://badge.fury.io/rb/rdf-reasoner)
|
6
|
+
[](https://github.com/ruby-rdf/rdf-reasoner/actions?query=workflow%3ACI)
|
7
|
+
[](https://coveralls.io/github/ruby-rdf/rdf-reasoner?branch=develop)
|
8
|
+
[](https://gitter.im/ruby-rdf/rdf)
|
9
|
+
|
10
|
+
## Description
|
2
11
|
|
3
12
|
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
13
|
|
@@ -22,6 +31,14 @@ Domain and Range entailment include specific rules for schema.org vocabularies.
|
|
22
31
|
* If `resource` is of type `schema:Role`, it is range acceptable if it has the same property with an acceptable value.
|
23
32
|
* 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.
|
24
33
|
|
34
|
+
### Limiting vocabularies used for reasoning
|
35
|
+
|
36
|
+
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:
|
37
|
+
|
38
|
+
RDF::Vocabulary.limit_vocabs(:rdf, :rdf, :schema)
|
39
|
+
|
40
|
+
will limit the vocabularies which are returned from `RDF::Vocabulary.each`, which is used for reasoning and other operations over vocabularies and terms.
|
41
|
+
|
25
42
|
## Examples
|
26
43
|
### Determine super-classes of a class
|
27
44
|
|
@@ -104,8 +121,8 @@ The `rdf` command-line interface is extended with `entail` and `lint` commands.
|
|
104
121
|
|
105
122
|
## Dependencies
|
106
123
|
|
107
|
-
* [Ruby](https://ruby-lang.org/) (>= 2.
|
108
|
-
* [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.
|
124
|
+
* [Ruby](https://ruby-lang.org/) (>= 2.4)
|
125
|
+
* [RDF.rb](https://rubygems.org/gems/rdf) (~> 3.1)
|
109
126
|
|
110
127
|
## Mailing List
|
111
128
|
|
@@ -128,7 +145,9 @@ The `rdf` command-line interface is extended with `entail` and `lint` commands.
|
|
128
145
|
list in the the `README`. Alphabetical order applies.
|
129
146
|
* Do note that in order for us to merge any non-trivial changes (as a rule
|
130
147
|
of thumb, additions larger than about 15 lines of code), we need an
|
131
|
-
explicit [public domain dedication][PDD] on record from you
|
148
|
+
explicit [public domain dedication][PDD] on record from you,
|
149
|
+
which you will be asked to agree to on the first commit to a repo within the organization.
|
150
|
+
Note that the agreement applies to all repos in the [Ruby RDF](https://github.com/ruby-rdf/) organization.
|
132
151
|
|
133
152
|
## License
|
134
153
|
|
@@ -139,7 +158,7 @@ see <https://unlicense.org/> or the accompanying {file:UNLICENSE} file.
|
|
139
158
|
[RDF]: https://www.w3.org/RDF/
|
140
159
|
[YARD]: https://yardoc.org/
|
141
160
|
[YARD-GS]: https://rubydoc.info/docs/yard/file/docs/GettingStarted.md
|
142
|
-
[PDD]: https://
|
161
|
+
[PDD]: https://unlicense.org/#unlicensing-contributions
|
143
162
|
[SPARQL]: https://en.wikipedia.org/wiki/SPARQL
|
144
163
|
[SPARQL Query]: https://www.w3.org/TR/2013/REC-sparql11-query-20130321/
|
145
164
|
[SPARQL Entailment]:https://www.w3.org/TR/sparql11-entailment/
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.7.1
|
data/lib/rdf/reasoner.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
require 'rdf'
|
2
2
|
require 'rdf/reasoner/extensions'
|
3
|
-
require 'rdf/vocab'
|
4
3
|
|
5
4
|
module RDF
|
6
5
|
##
|
7
6
|
# RDFS/OWL reasonsing for RDF.rb.
|
8
7
|
#
|
9
|
-
# @see
|
8
|
+
# @see https://www.w3.org/TR/2013/REC-sparql11-entailment-20130321/
|
10
9
|
# @author [Gregg Kellogg](https://greggkellogg.net/)
|
11
10
|
module Reasoner
|
12
11
|
require 'rdf/reasoner/format'
|
@@ -15,7 +14,7 @@ module RDF
|
|
15
14
|
autoload :Schema, 'rdf/reasoner/schema'
|
16
15
|
autoload :VERSION, 'rdf/reasoner/version'
|
17
16
|
|
18
|
-
# See
|
17
|
+
# See https://www.pelagodesign.com/blog/2009/05/20/iso-8601-date-validation-that-doesnt-suck/
|
19
18
|
#
|
20
19
|
#
|
21
20
|
ISO_8601 = %r(^
|
@@ -91,7 +91,7 @@ module RDF
|
|
91
91
|
# Fully entailed types of resource, if not provided, they are queried
|
92
92
|
def domain_compatible?(resource, queryable, options = {})
|
93
93
|
%w(owl rdfs schema).map {|r| "domain_compatible_#{r}?".to_sym}.all? do |meth|
|
94
|
-
!self.respond_to?(meth) || self.send(meth, resource, queryable, options)
|
94
|
+
!self.respond_to?(meth) || self.send(meth, resource, queryable, **options)
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
@@ -233,7 +233,7 @@ module RDF
|
|
233
233
|
messages = {}
|
234
234
|
|
235
235
|
# Check for defined classes in known vocabularies
|
236
|
-
self.query(predicate: RDF.type) do |stmt|
|
236
|
+
self.query({predicate: RDF.type}) do |stmt|
|
237
237
|
vocab = RDF::Vocabulary.find(stmt.object)
|
238
238
|
term = (RDF::Vocabulary.find_term(stmt.object) rescue nil) if vocab
|
239
239
|
pname = term ? term.pname : stmt.object.pname
|
@@ -241,7 +241,7 @@ module RDF
|
|
241
241
|
# Must be a defined term, not in RDF or RDFS vocabularies
|
242
242
|
if term && term.class?
|
243
243
|
# Warn against using a deprecated term
|
244
|
-
superseded = term.
|
244
|
+
superseded = term.properties[:'http://schema.org/supersededBy']
|
245
245
|
superseded = superseded.pname if superseded.respond_to?(:pname)
|
246
246
|
(messages[:class] ||= {})[pname] = ["Term is superseded by #{superseded}"] if superseded
|
247
247
|
else
|
@@ -264,9 +264,9 @@ module RDF
|
|
264
264
|
end
|
265
265
|
|
266
266
|
# Must be a defined property
|
267
|
-
if term && term.property?
|
267
|
+
if term.respond_to?(:property?) && term.property?
|
268
268
|
# Warn against using a deprecated term
|
269
|
-
superseded = term.
|
269
|
+
superseded = term.properties[:'http://schema.org/supersededBy']
|
270
270
|
superseded = superseded.pname if superseded.respond_to?(:pname)
|
271
271
|
(messages[:property] ||= {})[pname] = ["Term is superseded by #{superseded}"] if superseded
|
272
272
|
else
|
@@ -275,7 +275,7 @@ module RDF
|
|
275
275
|
end
|
276
276
|
|
277
277
|
# See if type of the subject is in the domain of this predicate
|
278
|
-
resource_types[stmt.subject] ||= self.query(subject: stmt.subject, predicate: RDF.type).
|
278
|
+
resource_types[stmt.subject] ||= self.query({subject: stmt.subject, predicate: RDF.type}).
|
279
279
|
map {|s| (t = (RDF::Vocabulary.find_term(s.object) rescue nil)) && t.entail(:subClassOf)}.
|
280
280
|
flatten.
|
281
281
|
uniq.
|
@@ -290,7 +290,7 @@ module RDF
|
|
290
290
|
end
|
291
291
|
|
292
292
|
# Make sure that if ranges are defined, the object has an appropriate type
|
293
|
-
resource_types[stmt.object] ||= self.query(subject: stmt.object, predicate: RDF.type).
|
293
|
+
resource_types[stmt.object] ||= self.query({subject: stmt.object, predicate: RDF.type}).
|
294
294
|
map {|s| (t = (RDF::Vocabulary.find_term(s.object) rescue nil)) && t.entail(:subClassOf)}.
|
295
295
|
flatten.
|
296
296
|
uniq.
|
@@ -316,7 +316,7 @@ module RDF
|
|
316
316
|
def show_resource(resource)
|
317
317
|
if resource.node?
|
318
318
|
resource.to_ntriples + '(' +
|
319
|
-
self.query(subject: resource, predicate: RDF.type).
|
319
|
+
self.query({subject: resource, predicate: RDF.type}).
|
320
320
|
map {|s| s.object.uri? ? s.object.pname : s.object.to_ntriples}
|
321
321
|
.join(',') +
|
322
322
|
')'
|
data/lib/rdf/reasoner/owl.rb
CHANGED
@@ -50,7 +50,7 @@ module RDF::Reasoner
|
|
50
50
|
if self.predicate == RDF.type
|
51
51
|
if term = (RDF::Vocabulary.find_term(self.object) rescue nil)
|
52
52
|
term._entail_equivalentClass do |t|
|
53
|
-
statements << RDF::Statement(self.to_h.merge(object: t, inferred: true))
|
53
|
+
statements << RDF::Statement(**self.to_h.merge(object: t, inferred: true))
|
54
54
|
end
|
55
55
|
end
|
56
56
|
end
|
@@ -89,7 +89,7 @@ module RDF::Reasoner
|
|
89
89
|
statements = []
|
90
90
|
if term = (RDF::Vocabulary.find_term(self.predicate) rescue nil)
|
91
91
|
term._entail_equivalentProperty do |t|
|
92
|
-
statements << RDF::Statement(self.to_h.merge(predicate: t, inferred: true))
|
92
|
+
statements << RDF::Statement(**self.to_h.merge(predicate: t, inferred: true))
|
93
93
|
end
|
94
94
|
end
|
95
95
|
statements.each {|s| yield s} if block_given?
|
data/lib/rdf/reasoner/rdfs.rb
CHANGED
@@ -74,7 +74,7 @@ module RDF::Reasoner
|
|
74
74
|
if term = (RDF::Vocabulary.find_term(self.object) rescue nil)
|
75
75
|
term._entail_subClassOf do |t|
|
76
76
|
next if t.node? # Don't entail BNodes
|
77
|
-
statements << RDF::Statement(self.to_h.merge(object: t, inferred: true))
|
77
|
+
statements << RDF::Statement(**self.to_h.merge(object: t, inferred: true))
|
78
78
|
end
|
79
79
|
end
|
80
80
|
#$stderr.puts("subClassf(#{self.predicate.pname}): #{statements.map(&:object).map {|r| r.respond_to?(:pname) ? r.pname : r.to_ntriples}}}")
|
@@ -122,7 +122,7 @@ module RDF::Reasoner
|
|
122
122
|
|
123
123
|
##
|
124
124
|
# 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
|
125
|
-
# For a Statement: yield or return inferred statements having a subPropertyOf relationship to predicate of this
|
125
|
+
# For a Statement: yield or return inferred statements having a subPropertyOf relationship to predicate of this statements
|
126
126
|
# @private
|
127
127
|
def _entail_subPropertyOf
|
128
128
|
case self
|
@@ -143,7 +143,7 @@ module RDF::Reasoner
|
|
143
143
|
statements = []
|
144
144
|
if term = (RDF::Vocabulary.find_term(self.predicate) rescue nil)
|
145
145
|
term._entail_subPropertyOf do |t|
|
146
|
-
statements << RDF::Statement(self.to_h.merge(predicate: t, inferred: true))
|
146
|
+
statements << RDF::Statement(**self.to_h.merge(predicate: t, inferred: true))
|
147
147
|
end
|
148
148
|
#$stderr.puts("subPropertyOf(#{self.predicate.pname}): #{statements.map(&:object).map {|r| r.respond_to?(:pname) ? r.pname : r.to_ntriples}}}")
|
149
149
|
end
|
@@ -208,7 +208,7 @@ module RDF::Reasoner
|
|
208
208
|
if term = (RDF::Vocabulary.find_term(self.predicate) rescue nil)
|
209
209
|
term.domain.each do |t|
|
210
210
|
next if t.node? # Don't entail BNodes
|
211
|
-
statements << RDF::Statement(self.to_h.merge(predicate: RDF.type, object: t, inferred: true))
|
211
|
+
statements << RDF::Statement(**self.to_h.merge(predicate: RDF.type, object: t, inferred: true))
|
212
212
|
end
|
213
213
|
end
|
214
214
|
#$stderr.puts("domain(#{self.predicate.pname}): #{statements.map(&:object).map {|r| r.respond_to?(:pname) ? r.pname : r.to_ntriples}}}")
|
@@ -229,7 +229,7 @@ module RDF::Reasoner
|
|
229
229
|
if object.resource? && term = (RDF::Vocabulary.find_term(self.predicate) rescue nil)
|
230
230
|
term.range.each do |t|
|
231
231
|
next if t.node? # Don't entail BNodes
|
232
|
-
statements << RDF::Statement(self.to_h.merge(subject: self.object, predicate: RDF.type, object: t, inferred: true))
|
232
|
+
statements << RDF::Statement(**self.to_h.merge(subject: self.object, predicate: RDF.type, object: t, inferred: true))
|
233
233
|
end
|
234
234
|
end
|
235
235
|
#$stderr.puts("range(#{self.predicate.pname}): #{statements.map(&:object).map {|r| r.respond_to?(:pname) ? r.pname : r.to_ntriples}}")
|
@@ -255,7 +255,7 @@ module RDF::Reasoner
|
|
255
255
|
|
256
256
|
# Fully entailed types of the resource
|
257
257
|
types = options.fetch(:types) do
|
258
|
-
queryable.query(subject: resource, predicate: RDF.type).
|
258
|
+
queryable.query({subject: resource, predicate: RDF.type}).
|
259
259
|
map {|s| (t = (RDF::Vocabulary.find_term(s.object)) rescue nil) && t.entail(:subClassOf)}.
|
260
260
|
flatten.
|
261
261
|
uniq.
|
@@ -290,12 +290,12 @@ module RDF::Reasoner
|
|
290
290
|
# XSD types are valid if the datatype matches, or they are plain and valid according to the grammar of the range
|
291
291
|
resource.datatype == range ||
|
292
292
|
resource.plain? && RDF::Literal.new(resource.value, datatype: range).valid?
|
293
|
-
elsif range.start_with?(
|
293
|
+
elsif range.start_with?("http://ogp.me/ns/class#")
|
294
294
|
case range
|
295
|
-
when RDF::
|
296
|
-
[RDF::
|
295
|
+
when RDF::URI("http://ogp.me/ns/class#boolean_str")
|
296
|
+
[RDF::URI("http://ogp.me/ns/class#boolean_str"), RDF::XSD.boolean].include?(resource.datatype) ||
|
297
297
|
resource.plain? && RDF::Literal::Boolean.new(resource.value).valid?
|
298
|
-
when RDF::
|
298
|
+
when RDF::URI("http://ogp.me/ns/class#date_time_str")
|
299
299
|
# Schema.org date based on ISO 8601, mapped to appropriate XSD types for validation
|
300
300
|
case resource
|
301
301
|
when RDF::Literal::Date, RDF::Literal::Time, RDF::Literal::DateTime, RDF::Literal::Duration
|
@@ -303,27 +303,27 @@ module RDF::Reasoner
|
|
303
303
|
else
|
304
304
|
ISO_8601.match(resource.value)
|
305
305
|
end
|
306
|
-
when RDF::
|
306
|
+
when RDF::URI("http://ogp.me/ns/class#determiner_str")
|
307
307
|
# The lexical space: "", "the", "a", "an", and "auto".
|
308
308
|
resource.plain? && (%w(the a an auto) + [""]).include?(resource.value)
|
309
|
-
when RDF::
|
309
|
+
when RDF::URI("http://ogp.me/ns/class#float_str")
|
310
310
|
# A string representation of a 64-bit signed floating point number. Example lexical values include "1.234", "-1.234", "1.2e3", "-1.2e3", and "7E-10".
|
311
|
-
[RDF::
|
311
|
+
[RDF::URI("http://ogp.me/ns/class#float_str"), RDF::Literal::Double, RDF::Literal::Float].include?(resource.datatype) ||
|
312
312
|
resource.plain? && RDF::Literal::Double.new(resource.value).valid?
|
313
|
-
when RDF::
|
313
|
+
when RDF::URI("http://ogp.me/ns/class#integer_str")
|
314
314
|
resource.is_a?(RDF::Literal::Integer) ||
|
315
|
-
[RDF::
|
315
|
+
[RDF::URI("http://ogp.me/ns/class#integer_str")].include?(resource.datatype) ||
|
316
316
|
resource.plain? && RDF::Literal::Integer.new(resource.value).valid?
|
317
|
-
when RDF::
|
317
|
+
when RDF::URI("http://ogp.me/ns/class#mime_type_str")
|
318
318
|
# Valid mime type strings \(e.g., "application/mp3"\).
|
319
|
-
[RDF::
|
319
|
+
[RDF::URI("http://ogp.me/ns/class#mime_type_str")].include?(resource.datatype) ||
|
320
320
|
resource.plain? && resource.value =~ %r(^[\w\-\+]+/[\w\-\+]+$)
|
321
|
-
when RDF::
|
321
|
+
when RDF::URI("http://ogp.me/ns/class#string")
|
322
322
|
resource.plain?
|
323
|
-
when RDF::
|
323
|
+
when RDF::URI("http://ogp.me/ns/class#url")
|
324
324
|
# A string of Unicode characters forming a valid URL having the http or https scheme.
|
325
325
|
u = RDF::URI(resource.value)
|
326
|
-
resource.datatype == RDF::
|
326
|
+
resource.datatype == RDF::URI("http://ogp.me/ns/class#url") ||
|
327
327
|
resource.datatype == RDF::XSD.anyURI ||
|
328
328
|
resource.simple? && u.valid? && u.scheme.to_s =~ /^https?$/
|
329
329
|
else
|
@@ -337,7 +337,7 @@ module RDF::Reasoner
|
|
337
337
|
else
|
338
338
|
# Fully entailed types of the resource
|
339
339
|
types = options.fetch(:types) do
|
340
|
-
queryable.query(subject: resource, predicate: RDF.type).
|
340
|
+
queryable.query({subject: resource, predicate: RDF.type}).
|
341
341
|
map {|s| (t = (RDF::Vocabulary.find_term(s.object) rescue nil)) && t.entail(:subClassOf)}.
|
342
342
|
flatten.
|
343
343
|
uniq.
|
data/lib/rdf/reasoner/schema.rb
CHANGED
@@ -24,18 +24,20 @@ module RDF::Reasoner
|
|
24
24
|
# Fully entailed types of resource, if not provided, they are queried
|
25
25
|
def domain_compatible_schema?(resource, queryable, options = {})
|
26
26
|
raise RDF::Reasoner::Error, "#{self} can't get domains" unless property?
|
27
|
-
domains = Array(self.domainIncludes)
|
27
|
+
domains = Array(self.domainIncludes) +
|
28
|
+
Array(self.properties[:'https://schema.org/domainIncludes']) -
|
29
|
+
[RDF::OWL.Thing]
|
28
30
|
|
29
31
|
# Fully entailed types of the resource
|
30
|
-
types = entailed_types(resource, queryable, options) unless domains.empty?
|
32
|
+
types = entailed_types(resource, queryable, **options) unless domains.empty?
|
31
33
|
|
32
34
|
# Every domain must match some entailed type
|
33
35
|
resource_acceptable = Array(types).empty? || domains.any? {|d| types.include?(d)}
|
34
36
|
|
35
37
|
# Resource may still be acceptable if types include schema:Role, and any any other resource references `resource` using this property
|
36
38
|
resource_acceptable ||
|
37
|
-
types.include?(RDF::
|
38
|
-
|
39
|
+
(types.include?(RDF::URI("http://schema.org/Role")) || types.include?(RDF::URI("https://schema.org/Role"))) &&
|
40
|
+
!queryable.query({predicate: self, object: resource}).empty?
|
39
41
|
end
|
40
42
|
|
41
43
|
##
|
@@ -54,16 +56,23 @@ module RDF::Reasoner
|
|
54
56
|
# Fully entailed types of resource, if not provided, they are queried
|
55
57
|
def range_compatible_schema?(resource, queryable, options = {})
|
56
58
|
raise RDF::Reasoner::Error, "#{self} can't get ranges" unless property?
|
57
|
-
if !(ranges = Array(self.rangeIncludes)
|
59
|
+
if !(ranges = Array(self.rangeIncludes) +
|
60
|
+
Array(self.properties[:'https://schema.org/rangeIncludes']) -
|
61
|
+
[RDF::OWL.Thing]).empty?
|
58
62
|
if resource.literal?
|
59
63
|
ranges.any? do |range|
|
60
64
|
case range
|
61
65
|
when RDF::RDFS.Literal then true
|
62
|
-
when RDF::
|
63
|
-
|
64
|
-
|
66
|
+
when RDF::URI("http://schema.org/Text"), RDF::URI("https://schema.org/Text")
|
67
|
+
resource.plain? || resource.datatype == RDF::URI("http://schema.org/Text")
|
68
|
+
when RDF::URI("http://schema.org/Boolean"), RDF::URI("https://schema.org/Boolean")
|
69
|
+
[
|
70
|
+
RDF::URI("http://schema.org/Boolean"),
|
71
|
+
RDF::URI("https://schema.org/Boolean"),
|
72
|
+
RDF::XSD.boolean
|
73
|
+
].include?(resource.datatype) ||
|
65
74
|
resource.plain? && RDF::Literal::Boolean.new(resource.value).valid?
|
66
|
-
when RDF::
|
75
|
+
when RDF::URI("http://schema.org/Date"), RDF::URI("https://schema.org/Date")
|
67
76
|
# Schema.org date based on ISO 8601, mapped to appropriate XSD types for validation
|
68
77
|
case resource
|
69
78
|
when RDF::Literal::Date, RDF::Literal::Time, RDF::Literal::DateTime, RDF::Literal::Duration
|
@@ -71,35 +80,56 @@ module RDF::Reasoner
|
|
71
80
|
else
|
72
81
|
ISO_8601.match(resource.value)
|
73
82
|
end
|
74
|
-
when RDF::
|
75
|
-
resource.datatype == RDF::
|
83
|
+
when RDF::URI("http://schema.org/DateTime"), RDF::URI("https://schema.org/DateTime")
|
84
|
+
resource.datatype == RDF::URI("http://schema.org/DateTime") ||
|
85
|
+
resource.datatype == RDF::URI("https://schema.org/DateTime") ||
|
76
86
|
resource.is_a?(RDF::Literal::DateTime) ||
|
77
87
|
resource.plain? && RDF::Literal::DateTime.new(resource.value).valid?
|
78
|
-
when RDF::
|
88
|
+
when RDF::URI("http://schema.org/Duration"), RDF::URI("https://schema.org/Duration")
|
79
89
|
value = resource.value
|
80
90
|
value = "P#{value}" unless value.start_with?("P")
|
81
|
-
resource.datatype == RDF::
|
91
|
+
resource.datatype == RDF::URI("http://schema.org/Duration") ||
|
92
|
+
resource.datatype == RDF::URI("https://schema.org/Duration") ||
|
82
93
|
resource.is_a?(RDF::Literal::Duration) ||
|
83
94
|
resource.plain? && RDF::Literal::Duration.new(value).valid?
|
84
|
-
when RDF::
|
85
|
-
resource.datatype == RDF::
|
95
|
+
when RDF::URI("http://schema.org/Time"), RDF::URI("https://schema.org/Time")
|
96
|
+
resource.datatype == RDF::URI("http://schema.org/Time") ||
|
97
|
+
resource.datatype == RDF::URI("https://schema.org/Time") ||
|
86
98
|
resource.is_a?(RDF::Literal::Time) ||
|
87
99
|
resource.plain? && RDF::Literal::Time.new(resource.value).valid?
|
88
|
-
when RDF::
|
100
|
+
when RDF::URI("http://schema.org/Number"), RDF::URI("https://schema.org/Number")
|
89
101
|
resource.is_a?(RDF::Literal::Numeric) ||
|
90
|
-
[
|
102
|
+
[
|
103
|
+
RDF::URI("http://schema.org/Number"),
|
104
|
+
RDF::URI("http://schema.org/Float"),
|
105
|
+
RDF::URI("http://schema.org/Integer"),
|
106
|
+
RDF::URI("https://schema.org/Number"),
|
107
|
+
RDF::URI("https://schema.org/Float"),
|
108
|
+
RDF::URI("https://schema.org/Integer"),
|
109
|
+
].include?(resource.datatype) ||
|
91
110
|
resource.plain? && RDF::Literal::Integer.new(resource.value).valid? ||
|
92
111
|
resource.plain? && RDF::Literal::Double.new(resource.value).valid?
|
93
|
-
when RDF::
|
112
|
+
when RDF::URI("http://schema.org/Float"), RDF::URI("https://schema.org/Float")
|
94
113
|
resource.is_a?(RDF::Literal::Double) ||
|
95
|
-
[
|
114
|
+
[
|
115
|
+
RDF::URI("http://schema.org/Number"),
|
116
|
+
RDF::URI("http://schema.org/Float"),
|
117
|
+
RDF::URI("https://schema.org/Number"),
|
118
|
+
RDF::URI("https://schema.org/Float"),
|
119
|
+
].include?(resource.datatype) ||
|
96
120
|
resource.plain? && RDF::Literal::Double.new(resource.value).valid?
|
97
|
-
when RDF::
|
121
|
+
when RDF::URI("http://schema.org/Integer"), RDF::URI("https://schema.org/Integer")
|
98
122
|
resource.is_a?(RDF::Literal::Integer) ||
|
99
|
-
[
|
123
|
+
[
|
124
|
+
RDF::URI("http://schema.org/Number"),
|
125
|
+
RDF::URI("http://schema.org/Integer"),
|
126
|
+
RDF::URI("https://schema.org/Number"),
|
127
|
+
RDF::URI("https://schema.org/Integer"),
|
128
|
+
].include?(resource.datatype) ||
|
100
129
|
resource.plain? && RDF::Literal::Integer.new(resource.value).valid?
|
101
|
-
when RDF::
|
102
|
-
resource.datatype == RDF::
|
130
|
+
when RDF::URI("http://schema.org/URL"), RDF::URI("https://schema.org/URL")
|
131
|
+
resource.datatype == RDF::URI("http://schema.org/URL") ||
|
132
|
+
resource.datatype == RDF::URI("https://schema.org/URL") ||
|
103
133
|
resource.datatype == RDF::XSD.anyURI ||
|
104
134
|
resource.plain? && RDF::Literal::AnyURI.new(resource.value).valid?
|
105
135
|
else
|
@@ -117,18 +147,28 @@ module RDF::Reasoner
|
|
117
147
|
end
|
118
148
|
end
|
119
149
|
end
|
120
|
-
elsif %w(
|
150
|
+
elsif %w(
|
151
|
+
http://schema.org/True
|
152
|
+
http://schema.org/False
|
153
|
+
https://schema.org/True
|
154
|
+
https://schema.org/False
|
155
|
+
).include?(resource) &&
|
156
|
+
(ranges.include?(RDF::URI("http://schema.org/Boolean")) || ranges.include?(RDF::URI("https://schema.org/Boolean")))
|
121
157
|
true # Special case for schema boolean resources
|
122
|
-
elsif ranges.include?(RDF::
|
158
|
+
elsif (ranges.include?(RDF::URI("http://schema.org/URL")) || ranges.include?(RDF::URI("https://schema.org/URL"))) &&
|
159
|
+
resource.uri?
|
123
160
|
true # schema:URL matches URI resources
|
124
|
-
elsif ranges == [RDF::
|
161
|
+
elsif ranges == [RDF::URI("http://schema.org/Text")] && resource.uri?
|
125
162
|
# Allowed if resource is untyped
|
126
|
-
entailed_types(resource, queryable, options).empty?
|
163
|
+
entailed_types(resource, queryable, **options).empty?
|
164
|
+
elsif ranges == [RDF::URI("https://schema.org/Text")] && resource.uri?
|
165
|
+
# Allowed if resource is untyped
|
166
|
+
entailed_types(resource, queryable, **options).empty?
|
127
167
|
elsif literal_range?(ranges)
|
128
168
|
false # If resource isn't literal, this is a range violation
|
129
169
|
else
|
130
170
|
# Fully entailed types of the resource
|
131
|
-
types = entailed_types(resource, queryable, options)
|
171
|
+
types = entailed_types(resource, queryable, **options)
|
132
172
|
|
133
173
|
# Every range must match some entailed type
|
134
174
|
resource_acceptable = Array(types).empty? || ranges.any? {|d| types.include?(d)}
|
@@ -137,8 +177,8 @@ module RDF::Reasoner
|
|
137
177
|
resource_acceptable ||
|
138
178
|
|
139
179
|
# Resource also acceptable if it is a Role, and the Role object contains the same predicate having a compatible object
|
140
|
-
types.include?(RDF::
|
141
|
-
queryable.query(subject: resource, predicate: self).any? do |stmt|
|
180
|
+
(types.include?(RDF::URI("http://schema.org/Role")) || types.include?(RDF::URI("https://schema.org/Role"))) &&
|
181
|
+
queryable.query({subject: resource, predicate: self}).any? do |stmt|
|
142
182
|
acc = self.range_compatible_schema?(stmt.object, queryable)
|
143
183
|
acc
|
144
184
|
end ||
|
@@ -158,9 +198,25 @@ module RDF::Reasoner
|
|
158
198
|
def literal_range?(ranges)
|
159
199
|
ranges.all? do |range|
|
160
200
|
case range
|
161
|
-
when RDF::RDFS.Literal,
|
162
|
-
RDF::
|
163
|
-
RDF::
|
201
|
+
when RDF::RDFS.Literal,
|
202
|
+
RDF::URI("http://schema.org/Text"),
|
203
|
+
RDF::URI("http://schema.org/Boolean"),
|
204
|
+
RDF::URI("http://schema.org/Date"),
|
205
|
+
RDF::URI("http://schema.org/DateTime"),
|
206
|
+
RDF::URI("http://schema.org/Time"),
|
207
|
+
RDF::URI("http://schema.org/URL"),
|
208
|
+
RDF::URI("http://schema.org/Number"),
|
209
|
+
RDF::URI("http://schema.org/Float"),
|
210
|
+
RDF::URI("http://schema.org/Integer"),
|
211
|
+
RDF::URI("https://schema.org/Text"),
|
212
|
+
RDF::URI("https://schema.org/Boolean"),
|
213
|
+
RDF::URI("https://schema.org/Date"),
|
214
|
+
RDF::URI("https://schema.org/DateTime"),
|
215
|
+
RDF::URI("https://schema.org/Time"),
|
216
|
+
RDF::URI("https://schema.org/URL"),
|
217
|
+
RDF::URI("https://schema.org/Number"),
|
218
|
+
RDF::URI("https://schema.org/Float"),
|
219
|
+
RDF::URI("https://schema.org/Integer")
|
164
220
|
true
|
165
221
|
else
|
166
222
|
# If this is an XSD range, look for appropriate literal
|
@@ -174,9 +230,9 @@ module RDF::Reasoner
|
|
174
230
|
|
175
231
|
private
|
176
232
|
# Fully entailed types
|
177
|
-
def entailed_types(resource, queryable, options
|
233
|
+
def entailed_types(resource, queryable, **options)
|
178
234
|
options.fetch(:types) do
|
179
|
-
queryable.query(subject: resource, predicate: RDF.type).
|
235
|
+
queryable.query({subject: resource, predicate: RDF.type}).
|
180
236
|
map {|s| (t = (RDF::Vocabulary.find_term(s.object) rescue nil)) && t.entail(:subClassOf)}.
|
181
237
|
flatten.
|
182
238
|
uniq.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rdf-reasoner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gregg Kellogg
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rdf
|
@@ -16,84 +16,96 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '3.
|
19
|
+
version: '3.1'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 3.1.12
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
27
|
- - "~>"
|
25
28
|
- !ruby/object:Gem::Version
|
26
|
-
version: '3.
|
29
|
+
version: '3.1'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 3.1.12
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
|
-
name: rdf-
|
34
|
+
name: rdf-xsd
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
30
36
|
requirements:
|
31
37
|
- - "~>"
|
32
38
|
- !ruby/object:Gem::Version
|
33
|
-
version: '3.
|
39
|
+
version: '3.1'
|
34
40
|
type: :runtime
|
35
41
|
prerelease: false
|
36
42
|
version_requirements: !ruby/object:Gem::Requirement
|
37
43
|
requirements:
|
38
44
|
- - "~>"
|
39
45
|
- !ruby/object:Gem::Version
|
40
|
-
version: '3.
|
46
|
+
version: '3.1'
|
41
47
|
- !ruby/object:Gem::Dependency
|
42
|
-
name: rdf-
|
48
|
+
name: rdf-spec
|
43
49
|
requirement: !ruby/object:Gem::Requirement
|
44
50
|
requirements:
|
45
51
|
- - "~>"
|
46
52
|
- !ruby/object:Gem::Version
|
47
|
-
version: '3.
|
48
|
-
type: :
|
53
|
+
version: '3.1'
|
54
|
+
type: :development
|
49
55
|
prerelease: false
|
50
56
|
version_requirements: !ruby/object:Gem::Requirement
|
51
57
|
requirements:
|
52
58
|
- - "~>"
|
53
59
|
- !ruby/object:Gem::Version
|
54
|
-
version: '3.
|
60
|
+
version: '3.1'
|
55
61
|
- !ruby/object:Gem::Dependency
|
56
|
-
name: rdf-
|
62
|
+
name: rdf-vocab
|
57
63
|
requirement: !ruby/object:Gem::Requirement
|
58
64
|
requirements:
|
59
65
|
- - "~>"
|
60
66
|
- !ruby/object:Gem::Version
|
61
|
-
version: '3.
|
67
|
+
version: '3.1'
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: 3.1.10
|
62
71
|
type: :development
|
63
72
|
prerelease: false
|
64
73
|
version_requirements: !ruby/object:Gem::Requirement
|
65
74
|
requirements:
|
66
75
|
- - "~>"
|
67
76
|
- !ruby/object:Gem::Version
|
68
|
-
version: '3.
|
77
|
+
version: '3.1'
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 3.1.10
|
69
81
|
- !ruby/object:Gem::Dependency
|
70
82
|
name: rdf-turtle
|
71
83
|
requirement: !ruby/object:Gem::Requirement
|
72
84
|
requirements:
|
73
85
|
- - "~>"
|
74
86
|
- !ruby/object:Gem::Version
|
75
|
-
version: '3.
|
87
|
+
version: '3.1'
|
76
88
|
type: :development
|
77
89
|
prerelease: false
|
78
90
|
version_requirements: !ruby/object:Gem::Requirement
|
79
91
|
requirements:
|
80
92
|
- - "~>"
|
81
93
|
- !ruby/object:Gem::Version
|
82
|
-
version: '3.
|
94
|
+
version: '3.1'
|
83
95
|
- !ruby/object:Gem::Dependency
|
84
96
|
name: json-ld
|
85
97
|
requirement: !ruby/object:Gem::Requirement
|
86
98
|
requirements:
|
87
99
|
- - "~>"
|
88
100
|
- !ruby/object:Gem::Version
|
89
|
-
version: '3.
|
101
|
+
version: '3.1'
|
90
102
|
type: :development
|
91
103
|
prerelease: false
|
92
104
|
version_requirements: !ruby/object:Gem::Requirement
|
93
105
|
requirements:
|
94
106
|
- - "~>"
|
95
107
|
- !ruby/object:Gem::Version
|
96
|
-
version: '3.
|
108
|
+
version: '3.1'
|
97
109
|
- !ruby/object:Gem::Dependency
|
98
110
|
name: equivalent-xml
|
99
111
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,28 +126,28 @@ dependencies:
|
|
114
126
|
requirements:
|
115
127
|
- - "~>"
|
116
128
|
- !ruby/object:Gem::Version
|
117
|
-
version: '3.
|
129
|
+
version: '3.10'
|
118
130
|
type: :development
|
119
131
|
prerelease: false
|
120
132
|
version_requirements: !ruby/object:Gem::Requirement
|
121
133
|
requirements:
|
122
134
|
- - "~>"
|
123
135
|
- !ruby/object:Gem::Version
|
124
|
-
version: '3.
|
136
|
+
version: '3.10'
|
125
137
|
- !ruby/object:Gem::Dependency
|
126
138
|
name: yard
|
127
139
|
requirement: !ruby/object:Gem::Requirement
|
128
140
|
requirements:
|
129
141
|
- - "~>"
|
130
142
|
- !ruby/object:Gem::Version
|
131
|
-
version: 0.9
|
143
|
+
version: '0.9'
|
132
144
|
type: :development
|
133
145
|
prerelease: false
|
134
146
|
version_requirements: !ruby/object:Gem::Requirement
|
135
147
|
requirements:
|
136
148
|
- - "~>"
|
137
149
|
- !ruby/object:Gem::Version
|
138
|
-
version: 0.9
|
150
|
+
version: '0.9'
|
139
151
|
description: Reasons over RDFS/OWL vocabularies to generate statements which are entailed
|
140
152
|
based on base RDFS/OWL rules along with vocabulary information. It can also be used
|
141
153
|
to ask specific questions, such as if a given object is consistent with the vocabulary
|
@@ -160,7 +172,7 @@ homepage: https://github.com/ruby-rdf/rdf-reasoner
|
|
160
172
|
licenses:
|
161
173
|
- Unlicense
|
162
174
|
metadata: {}
|
163
|
-
post_install_message:
|
175
|
+
post_install_message:
|
164
176
|
rdoc_options: []
|
165
177
|
require_paths:
|
166
178
|
- lib
|
@@ -168,15 +180,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
168
180
|
requirements:
|
169
181
|
- - ">="
|
170
182
|
- !ruby/object:Gem::Version
|
171
|
-
version: 2.
|
183
|
+
version: '2.4'
|
172
184
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
173
185
|
requirements:
|
174
186
|
- - ">="
|
175
187
|
- !ruby/object:Gem::Version
|
176
188
|
version: '0'
|
177
189
|
requirements: []
|
178
|
-
rubygems_version: 3.
|
179
|
-
signing_key:
|
190
|
+
rubygems_version: 3.2.3
|
191
|
+
signing_key:
|
180
192
|
specification_version: 4
|
181
193
|
summary: RDFS/OWL Reasoner for RDF.rb
|
182
194
|
test_files: []
|