Module: RDF::Queryable

Defined in:
lib/rdf/reasoner/extensions.rb

Instance Method Summary collapse

Instance Method Details

#lintHash{Symbol => Hash{Symbol => Array<String>}}

Lint a queryable, presuming that it has already had RDFS entailment expansion.

Returns:

  • (Hash{Symbol => Hash{Symbol => Array<String>}})

    messages found for classes and properties by term



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/rdf/reasoner/extensions.rb', line 177

def lint
  messages = {}

  # Check for defined classes in known vocabularies
  self.query(predicate: RDF.type) do |stmt|
    vocab = RDF::Vocabulary.find(stmt.object)
    term = (RDF::Vocabulary.find_term(stmt.object) rescue nil) if vocab
    pname = term ? term.pname : stmt.object.pname
    
    # Must be a defined term, not in RDF or RDFS vocabularies
    if term && term.class?
      # Warn against using a deprecated term
      superseded = term.attributes[:'schema:supersededBy']
      (messages[:class] ||= {})[pname] = ["Term is superseded by #{superseded}"] if superseded
    else
      (messages[:class] ||= {})[pname] = ["No class definition found"] unless vocab.nil? || [RDF::RDFV, RDF::RDFS].include?(vocab)
    end
  end

  # Check for defined predicates in known vocabularies and domain/range
  resource_types = {}
  self.each_statement do |stmt|
    vocab = RDF::Vocabulary.find(stmt.predicate)
    term = (RDF::Vocabulary.find_term(stmt.predicate) rescue nil) if vocab
    pname = term ? term.pname : stmt.predicate.pname

    # Must be a valid statement
    begin
      stmt.validate!
    rescue
      ((messages[:statement] ||= {})[pname] ||= []) << "Triple #{stmt.to_ntriples} is invalid"
    end

    # Must be a defined property
    if term && term.property?
      # Warn against using a deprecated term
      superseded = term.attributes[:'schema:supersededBy']
      (messages[:property] ||= {})[pname] = ["Term is superseded by #{superseded}"] if superseded
    else
      ((messages[:property] ||= {})[pname] ||= []) << "No property definition found" unless vocab.nil?
      next
    end

    # See if type of the subject is in the domain of this predicate
    resource_types[stmt.subject] ||= self.query(subject: stmt.subject, predicate: RDF.type).
    map {|s| (t = (RDF::Vocabulary.find_term(s.object) rescue nil)) && t.entail(:subClassOf)}.
      flatten.
      uniq.
      compact

    unless term.domain_compatible?(stmt.subject, self, types: resource_types[stmt.subject])
      ((messages[:property] ||= {})[pname] ||= []) << if term.respond_to?(:domain)
       "Subject #{show_resource(stmt.subject)} not compatible with domain (#{Array(term.domain).map {|d| d.pname|| d}.join(',')})"
      else
        "Subject #{show_resource(stmt.subject)} not compatible with domainIncludes (#{term.domainIncludes.map {|d| d.pname|| d}.join(',')})"
      end
    end

    # Make sure that if ranges are defined, the object has an appropriate type
    resource_types[stmt.object] ||= self.query(subject: stmt.object, predicate: RDF.type).
      map {|s| (t = (RDF::Vocabulary.find_term(s.object) rescue nil)) && t.entail(:subClassOf)}.
      flatten.
      uniq.
      compact if stmt.object.resource?

    unless term.range_compatible?(stmt.object, self, types: resource_types[stmt.object])
      ((messages[:property] ||= {})[pname] ||= []) << if term.respond_to?(:range)
       "Object #{show_resource(stmt.object)} not compatible with range (#{Array(term.range).map {|d| d.pname|| d}.join(',')})"
      else
        "Object #{show_resource(stmt.object)} not compatible with rangeIncludes (#{term.rangeIncludes.map {|d| d.pname|| d}.join(',')})"
      end
    end
  end

  messages[:class].each {|k, v| messages[:class][k] = v.uniq} if messages[:class]
  messages[:property].each {|k, v| messages[:property][k] = v.uniq} if messages[:property]
  messages
end