Class: SHACL::Shapes

Inherits:
Array
  • Object
show all
Includes:
RDF::Util::Logger
Defined in:
lib/shacl/shapes.rb

Overview

The set of shapes loaded from a graph.

Constant Summary collapse

SHAPES_FRAME =
JSON.parse(%({
  "@context": {
    "id": "@id",
    "type": {"@id": "@type", "@container": "@set"},
    "@vocab": "http://www.w3.org/ns/shacl#",
    "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
    "shacl": "http://www.w3.org/ns/shacl#",
    "sh": "http://www.w3.org/ns/shacl#",
    "xsd": "http://www.w3.org/2001/XMLSchema#",
    "and": {"@type": "@id", "@container": "@list"},
    "annotationProperty": {"@type": "@id"},
    "class": {"@type": "@id"},
    "comment": "http://www.w3.org/2000/01/rdf-schema#comment",
    "condition": {"@type": "@id"},
    "datatype": {"@type": "@vocab"},
    "declare": {"@type": "@id"},
    "disjoint": {"@type": "@id"},
    "entailment": {"@type": "@id"},
    "equals": {"@type": "@id"},
    "ignoredProperties": {"@type": "@id", "@container": "@list"},
    "in": {"@type": "@none", "@container": "@list"},
    "inversePath": {"@type": "@id"},
    "label": "http://www.w3.org/2000/01/rdf-schema#label",
    "languageIn": {"@container": "@list"},
    "lessThan": {"@type": "@id"},
    "lessThanOrEquals": {"@type": "@id"},
    "nodeKind": {"@type": "@vocab"},
    "or": {"@type": "@id", "@container": "@list"},
    "path": {"@type": "@none"},
    "property": {"@type": "@id"},
    "severity": {"@type": "@vocab"},
    "targetClass": {"@type": "@id"},
    "targetNode": {"@type": "@none"},
    "xone": {"@type": "@id", "@container": "@list"}
  },
  "and": {},
  "class": {},
  "datatype": {},
  "in": {},
  "node": {},
  "nodeKind": {},
  "not": {},
  "or": {},
  "property": {},
  "targetClass": {},
  "targetNode": {},
  "targetObjectsOf": {},
  "xone": {},
  "targetSubjectsOf": {}
})).freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#loaded_graphsArray<RDF::URI> (readonly)

The graphs which have been loaded as shapes

Returns:

  • (Array<RDF::URI>)


15
16
17
# File 'lib/shacl/shapes.rb', line 15

def loaded_graphs
  @loaded_graphs
end

#shape_jsonArray<Hash> (readonly)

The JSON used to instantiate shapes

Returns:

  • (Array<Hash>)


20
21
22
# File 'lib/shacl/shapes.rb', line 20

def shape_json
  @shape_json
end

Class Method Details

.from_graph(graph, loaded_graphs: [], **options) ⇒ Shapes

Initializes the shapes from ‘graph`loading `owl:imports` until all references are loaded.

The shapes come from the following:

  • Instances of ‘sh:NodeShape` or `sh:PropertyShape`

  • resources that have any of the properties ‘sh:targetClass`, `sh:targetNode`, `sh:targetObjectsOf`, or `sh:targetSubjectsOf`.

Parameters:

  • graph (RDF::Graph)
  • loaded_graphs (Array<RDF::URI>) (defaults to: [])

    []

  • options (Hash{Symbol => Object})

Returns:

Raises:



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/shacl/shapes.rb', line 34

def self.from_graph(graph, loaded_graphs: [], **options)
  @loded_graphs = loaded_graphs

  import_count = 0
  while (imports = graph.query({predicate: RDF::OWL.imports}).map(&:object)).count > import_count
    # Load each imported graph
    imports.each do |ref|
      graph.load(imports)
      loaded_graphs << ref
      import_count += 1
    end
  end

  # Serialize the graph as framed JSON-LD and initialize patterns, recursively.
  shape_json = JSON::LD::API.fromRdf(graph, useNativeTypes: true) do |expanded|
    JSON::LD::API.frame(expanded, SHAPES_FRAME, omitGraph: false, embed: '@always', expanded: true)
  end['@graph']

  # Create an array of the framed shapes
  shapes = self.new(shape_json.map {|o| Algebra.from_json(o, **options)})
  shapes.instance_variable_set(:@shape_json, shape_json)
  shapes
end

.from_queryable(queryable, **options) ⇒ Shapes

Retrieve shapes from a sh:shapesGraph reference within the queryable

Parameters:

  • queryable (RDF::Queryable)

    The data graph which may contain references to the shapes graph

  • options (Hash{Symbol => Object})

Returns:

Raises:



66
67
68
69
70
71
72
73
# File 'lib/shacl/shapes.rb', line 66

def self.from_queryable(queryable, **options)
  # Query queryable to find one ore more shapes graphs
  graphs = queryable.query({predicate: RDF::Vocab::SHACL.shapesGraph}).objects
  graph = RDF::Graph.new do |g|
    graphs.each {|iri| g.load(iri)}
  end
  from_graph(graph, loaded_graphs: graphs, **options)
end

Instance Method Details

#execute(graph, depth: 0, **options) ⇒ Hash{RDF::Term => Array<ValidationResult>}, SHACL::ValidationReport

Match on schema. Finds appropriate shape for node, and matches that shape.

Parameters:

  • graph (RDF::Queryable)
  • options (Hash{Symbol => Object})

Options Hash (**options):

  • :focus (RDF::Term)

    An explicit focus node, overriding any defined on the top-level shaps.

Returns:



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/shacl/shapes.rb', line 84

def execute(graph, depth: 0, **options)
  self.each do |shape|
    shape.graph = graph
    shape.each_descendant do |op|
      op.graph = graph
    end
  end

  # Execute all shapes against their target nodes
  ValidationReport.new(self.map do |shape|
    nodes = Array(options.fetch(:focus, shape.targetNodes))
    nodes.map do |node|
      shape.conforms(node, depth: depth + 1)
    end
  end.flatten)
end

#to_sxpObject



105
106
107
# File 'lib/shacl/shapes.rb', line 105

def to_sxp
  to_sxp_bin.to_sxp
end

#to_sxp_binObject



101
102
103
# File 'lib/shacl/shapes.rb', line 101

def to_sxp_bin
  [:shapes, super]
end