shacl 0.2.1 → 0.4.0

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.
data/lib/shacl/shapes.rb CHANGED
@@ -9,6 +9,12 @@ module SHACL
9
9
  class Shapes < Array
10
10
  include RDF::Util::Logger
11
11
 
12
+ # The original shapes graph
13
+ #
14
+ # @return [RDF::Graph]
15
+ attr_reader :shapes_graph
16
+
17
+
12
18
  # The graphs which have been loaded as shapes
13
19
  #
14
20
  # @return [Array<RDF::URI>]
@@ -28,6 +34,7 @@ module SHACL
28
34
  #
29
35
  # @param [RDF::Graph] graph
30
36
  # @param [Array<RDF::URI>] loaded_graphs = []
37
+ # The graphs which have been loaded as shapes
31
38
  # @param [Hash{Symbol => Object}] options
32
39
  # @return [Shapes]
33
40
  # @raise [SHACL::Error]
@@ -38,20 +45,43 @@ module SHACL
38
45
  while (imports = graph.query({predicate: RDF::OWL.imports}).map(&:object)).count > import_count
39
46
  # Load each imported graph
40
47
  imports.each do |ref|
41
- graph.load(imports)
42
- loaded_graphs << ref
48
+ # Don't try import if the import subject is already in the graph
49
+ unless graph.subject?(ref)
50
+ begin
51
+ options[:logger].info('Shapes') {"load import #{ref}"} if options[:logger].respond_to?(:info)
52
+ graph.load(ref)
53
+ loaded_graphs << ref
54
+ rescue IOError => e
55
+ # Skip import
56
+ options[:logger].warn('Shapes') {"load import #{ref}"} if options[:logger].respond_to?(:warn)
57
+ end
58
+ end
43
59
  import_count += 1
44
60
  end
45
61
  end
46
62
 
47
63
  # Serialize the graph as framed JSON-LD and initialize patterns, recursively.
48
- shape_json = JSON::LD::API.fromRdf(graph, useNativeTypes: true) do |expanded|
49
- JSON::LD::API.frame(expanded, SHAPES_FRAME, omitGraph: false, embed: '@always', expanded: true)
50
- end['@graph']
64
+ expanded = JSON::LD::API.fromRdf(graph, useNativeTypes: true)
65
+
66
+ # Node and Property constraints
67
+ shape_json = JSON::LD::API.frame(expanded, SHAPES_FRAME,
68
+ omitGraph: false,
69
+ embed: '@always',
70
+ expanded: true)['@graph']
71
+
72
+ # Any defined Constraint Components
73
+ components = JSON::LD::API.frame(expanded, COMPONENTS_FRAME,
74
+ omitGraph: false,
75
+ embed: '@always',
76
+ expanded: true)['@graph']
77
+
78
+ # Extract any constraint components and merge to top-level
79
+ shape_json = components + shape_json
51
80
 
52
81
  # Create an array of the framed shapes
53
82
  shapes = self.new(shape_json.map {|o| Algebra.from_json(o, **options)})
54
83
  shapes.instance_variable_set(:@shape_json, shape_json)
84
+ shapes.instance_variable_set(:@shapes_graph, graph)
55
85
  shapes
56
86
  end
57
87
 
@@ -65,11 +95,11 @@ module SHACL
65
95
  # @raise [SHACL::Error]
66
96
  def self.from_queryable(queryable, **options)
67
97
  # Query queryable to find one ore more shapes graphs
68
- graphs = queryable.query({predicate: RDF::Vocab::SHACL.shapesGraph}).objects
69
- graph = RDF::Graph.new do |g|
70
- graphs.each {|iri| g.load(iri)}
98
+ graph_names = queryable.query({predicate: RDF::Vocab::SHACL.shapesGraph}).objects
99
+ graph = RDF::Graph.new(graph_name: graph_names.first, data: RDF::Repository.new) do |g|
100
+ graph_names.each {|iri| g.load(iri, graph_name: graph_names.first)}
71
101
  end
72
- from_graph(graph, loaded_graphs: graphs, **options)
102
+ from_graph(graph, loaded_graphs: graph_names, **options)
73
103
  end
74
104
 
75
105
  ##
@@ -80,12 +110,18 @@ module SHACL
80
110
  # @param [Hash{Symbol => Object}] options
81
111
  # @option options [RDF::Term] :focus
82
112
  # An explicit focus node, overriding any defined on the top-level shaps.
113
+ # @option options [Logger, #write, #<<] :logger
114
+ # Record error/info/debug output
83
115
  # @return [SHACL::ValidationReport]
84
116
  def execute(graph, depth: 0, **options)
85
117
  self.each do |shape|
86
118
  shape.graph = graph
119
+ shape.shapes_graph = shapes_graph
87
120
  shape.each_descendant do |op|
88
- op.graph = graph
121
+ op.instance_variable_set(:@logger, options[:logger]) if
122
+ options[:logger] && op.respond_to?(:execute)
123
+ op.graph = graph if op.respond_to?(:graph=)
124
+ op.shapes_graph = shapes_graph if op.respond_to?(:shapes_graph=)
89
125
  end
90
126
  end
91
127
 
@@ -115,11 +151,12 @@ module SHACL
115
151
  "id": "@id",
116
152
  "type": {"@id": "@type", "@container": "@set"},
117
153
  "@vocab": "http://www.w3.org/ns/shacl#",
154
+ "owl": "http://www.w3.org/2002/07/owl#",
118
155
  "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
119
156
  "shacl": "http://www.w3.org/ns/shacl#",
120
157
  "sh": "http://www.w3.org/ns/shacl#",
121
158
  "xsd": "http://www.w3.org/2001/XMLSchema#",
122
- "and": {"@type": "@id", "@container": "@list"},
159
+ "and": {"@type": "@id"},
123
160
  "annotationProperty": {"@type": "@id"},
124
161
  "class": {"@type": "@id"},
125
162
  "comment": "http://www.w3.org/2000/01/rdf-schema#comment",
@@ -130,35 +167,83 @@ module SHACL
130
167
  "entailment": {"@type": "@id"},
131
168
  "equals": {"@type": "@id"},
132
169
  "ignoredProperties": {"@type": "@id", "@container": "@list"},
170
+ "imports": {"@id": "owl:imports", "@type": "@id"},
133
171
  "in": {"@type": "@none", "@container": "@list"},
134
172
  "inversePath": {"@type": "@id"},
135
173
  "label": "http://www.w3.org/2000/01/rdf-schema#label",
136
174
  "languageIn": {"@container": "@list"},
137
175
  "lessThan": {"@type": "@id"},
138
176
  "lessThanOrEquals": {"@type": "@id"},
177
+ "namespace": {"@type": "xsd:anyURI"},
139
178
  "nodeKind": {"@type": "@vocab"},
140
- "or": {"@type": "@id", "@container": "@list"},
179
+ "or": {"@type": "@id"},
141
180
  "path": {"@type": "@none"},
181
+ "prefixes": {"@type": "@id"},
142
182
  "property": {"@type": "@id"},
143
183
  "severity": {"@type": "@vocab"},
184
+ "sparql": {"@type": "@id"},
144
185
  "targetClass": {"@type": "@id"},
145
186
  "targetNode": {"@type": "@none"},
146
- "xone": {"@type": "@id", "@container": "@list"}
187
+ "xone": {"@type": "@id"}
147
188
  },
148
189
  "and": {},
149
190
  "class": {},
150
191
  "datatype": {},
151
- "in": {},
192
+ "in": {"@embed": "@never"},
152
193
  "node": {},
153
194
  "nodeKind": {},
154
195
  "not": {},
155
196
  "or": {},
156
197
  "property": {},
198
+ "sparql": {},
157
199
  "targetClass": {},
158
200
  "targetNode": {},
159
201
  "targetObjectsOf": {},
160
202
  "xone": {},
161
203
  "targetSubjectsOf": {}
162
204
  })).freeze
205
+
206
+ COMPONENTS_FRAME = JSON.parse(%({
207
+ "@context": {
208
+ "id": "@id",
209
+ "type": {"@id": "@type", "@container": "@set"},
210
+ "@vocab": "http://www.w3.org/ns/shacl#",
211
+ "owl": "http://www.w3.org/2002/07/owl#",
212
+ "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
213
+ "shacl": "http://www.w3.org/ns/shacl#",
214
+ "sh": "http://www.w3.org/ns/shacl#",
215
+ "xsd": "http://www.w3.org/2001/XMLSchema#",
216
+ "and": {"@type": "@id"},
217
+ "annotationProperty": {"@type": "@id"},
218
+ "class": {"@type": "@id"},
219
+ "comment": "http://www.w3.org/2000/01/rdf-schema#comment",
220
+ "condition": {"@type": "@id"},
221
+ "datatype": {"@type": "@vocab"},
222
+ "declare": {"@type": "@id"},
223
+ "disjoint": {"@type": "@id"},
224
+ "entailment": {"@type": "@id"},
225
+ "equals": {"@type": "@id"},
226
+ "ignoredProperties": {"@type": "@id", "@container": "@list"},
227
+ "imports": {"@id": "owl:imports", "@type": "@id"},
228
+ "in": {"@type": "@none", "@container": "@list"},
229
+ "inversePath": {"@type": "@id"},
230
+ "label": "http://www.w3.org/2000/01/rdf-schema#label",
231
+ "languageIn": {"@container": "@list"},
232
+ "lessThan": {"@type": "@id"},
233
+ "lessThanOrEquals": {"@type": "@id"},
234
+ "namespace": {"@type": "xsd:anyURI"},
235
+ "nodeKind": {"@type": "@vocab"},
236
+ "or": {"@type": "@id"},
237
+ "path": {"@type": "@id"},
238
+ "prefixes": {"@type": "@id"},
239
+ "property": {"@type": "@id"},
240
+ "severity": {"@type": "@vocab"},
241
+ "sparql": {"@type": "@id"},
242
+ "targetClass": {"@type": "@id"},
243
+ "targetNode": {"@type": "@none"},
244
+ "xone": {"@type": "@id"}
245
+ },
246
+ "@type": "ConstraintComponent"
247
+ })).freeze
163
248
  end
164
249
  end
@@ -115,26 +115,29 @@ module SHACL
115
115
  block.call(RDF::Statement(subject, RDF::Vocab::SHACL.resultMessage, RDF::Literal(message))) if message
116
116
  end
117
117
 
118
- # Transform a JSON representation of a result, into a native representation
119
- # @param [Hash] input
120
- # @return [ValidationResult]
121
- def self.from_json(input, **options)
122
- input = JSON.parse(input) if input.is_a?(String)
123
- input = JSON::LD::API.compact(input,
124
- "http://github.com/ruby-rdf/shacl/",
125
- expandContext: "http://github.com/ruby-rdf/shacl/")
126
- raise ArgumentError, "Expect report to be a hash" unless input.is_a?(Hash)
127
- result = self.new
118
+ # Class Methods
119
+ class << self
120
+ # Transform a JSON representation of a result, into a native representation
121
+ # @param [Hash] input
122
+ # @return [ValidationResult]
123
+ def from_json(input, **options)
124
+ input = JSON.parse(input) if input.is_a?(String)
125
+ input = JSON::LD::API.compact(input,
126
+ "http://github.com/ruby-rdf/shacl/",
127
+ expandContext: "http://github.com/ruby-rdf/shacl/")
128
+ raise ArgumentError, "Expect report to be a hash" unless input.is_a?(Hash)
129
+ result = self.new
128
130
 
129
- result.focus = Algebra::Operator.to_rdf(:focus, input['focusNode'], base: nil, vocab: false) if input['focusNode']
130
- result.path = Algebra::Operator.parse_path(input['resultPath'], **options) if input['resultPath']
131
- result.resultSeverity = Algebra::Operator.iri(input['resultSeverity'], **options) if input['resultSeverity']
132
- result.component = Algebra::Operator.iri(input['sourceConstraintComponent'], **options) if input['sourceConstraintComponent']
133
- result.shape = Algebra::Operator.iri(input['sourceShape'], **options) if input['sourceShape']
134
- result.value = Algebra::Operator.to_rdf(:value, input['value'], **options) if input['value']
135
- result.details = Algebra::Operator.to_rdf(:details, input['details'], **options) if input['details']
136
- result.message = Algebra::Operator.to_rdf(:message, input['message'], **options) if input['message']
137
- result
131
+ result.focus = Algebra::Operator.to_rdf(:focus, input['focusNode'], base: nil, vocab: false) if input['focusNode']
132
+ result.path = Algebra::Operator.parse_path(input['resultPath'], **options) if input['resultPath']
133
+ result.resultSeverity = Algebra::Operator.iri(input['resultSeverity'], **options) if input['resultSeverity']
134
+ result.component = Algebra::Operator.iri(input['sourceConstraintComponent'], **options) if input['sourceConstraintComponent']
135
+ result.shape = Algebra::Operator.iri(input['sourceShape'], **options) if input['sourceShape']
136
+ result.value = Algebra::Operator.to_rdf(:value, input['value'], **options) if input['value']
137
+ result.details = Algebra::Operator.to_rdf(:details, input['details'], **options) if input['details']
138
+ result.message = Algebra::Operator.to_rdf(:message, input['message'], **options) if input['message']
139
+ result
140
+ end
138
141
  end
139
142
 
140
143
  # To results are eql? if their overlapping properties are equal
data/lib/shacl.rb CHANGED
@@ -21,8 +21,8 @@ module SHACL
21
21
  # @option (see Shapes#from_graph)
22
22
  # @return (see Shapes#from_graph)
23
23
  # @raise (see Shapes#from_graph)
24
- def self.get_shapes(shape_graph, **options)
25
- Shapes.from_graph(shape_graph, **options)
24
+ def self.get_shapes(shapes_graph, **options)
25
+ Shapes.from_graph(shapes_graph, **options)
26
26
  end
27
27
 
28
28
  ##
@@ -33,8 +33,13 @@ module SHACL
33
33
  # @return (see Shapes#from_graph)
34
34
  # @raise (see Shapes#from_graph)
35
35
  def self.open(input, **options)
36
- graph = RDF::Graph.load(input, **options)
37
- self.get_shapes(graph, loaded_graphs: [RDF::URI(input, canonicalize: true)], **options)
36
+ # Create graph backed by repo to allow a graph_name
37
+ graph = RDF::Graph.load(input,
38
+ graph_name: RDF::URI(input),
39
+ data: RDF::Repository.new)
40
+ self.get_shapes(graph,
41
+ loaded_graphs: [RDF::URI(input, canonicalize: true)],
42
+ **options)
38
43
  end
39
44
 
40
45
  ##
@@ -77,7 +82,7 @@ module SHACL
77
82
  attr_reader :code
78
83
 
79
84
  ##
80
- # Initializes a new patch error instance.
85
+ # Initializes a new error instance.
81
86
  #
82
87
  # @param [String, #to_s] message
83
88
  # @param [Hash{Symbol => Object}] options
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shacl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gregg Kellogg
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-01-27 00:00:00.000000000 Z
11
+ date: 2023-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rdf
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '3.2'
19
+ version: '3.3'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '3.2'
26
+ version: '3.3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: json-ld
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '3.2'
33
+ version: '3.3'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '3.2'
40
+ version: '3.3'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: sxp
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -58,70 +58,84 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '3.2'
61
+ version: '3.3'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '3.2'
68
+ version: '3.3'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rdf-spec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '3.2'
75
+ version: '3.3'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '3.2'
82
+ version: '3.3'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rdf-turtle
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '3.2'
89
+ version: '3.3'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '3.2'
96
+ version: '3.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rdf-vocab
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.3'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.3'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: rdf-xsd
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
115
  - - "~>"
102
116
  - !ruby/object:Gem::Version
103
- version: '3.2'
117
+ version: '3.3'
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
122
  - - "~>"
109
123
  - !ruby/object:Gem::Version
110
- version: '3.2'
124
+ version: '3.3'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: rspec
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
129
  - - "~>"
116
130
  - !ruby/object:Gem::Version
117
- version: '3.10'
131
+ version: '3.12'
118
132
  type: :development
119
133
  prerelease: false
120
134
  version_requirements: !ruby/object:Gem::Requirement
121
135
  requirements:
122
136
  - - "~>"
123
137
  - !ruby/object:Gem::Version
124
- version: '3.10'
138
+ version: '3.12'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: rspec-its
127
141
  requirement: !ruby/object:Gem::Requirement
@@ -164,13 +178,16 @@ files:
164
178
  - lib/shacl.rb
165
179
  - lib/shacl/algebra.rb
166
180
  - lib/shacl/algebra/and.rb
181
+ - lib/shacl/algebra/constraint_component.rb
167
182
  - lib/shacl/algebra/node_shape.rb
168
183
  - lib/shacl/algebra/not.rb
169
184
  - lib/shacl/algebra/operator.rb
170
185
  - lib/shacl/algebra/or.rb
186
+ - lib/shacl/algebra/pattern.rb
171
187
  - lib/shacl/algebra/property_shape.rb
172
- - lib/shacl/algebra/qualified_value_shape.rb
188
+ - lib/shacl/algebra/qualified_value.rb
173
189
  - lib/shacl/algebra/shape.rb
190
+ - lib/shacl/algebra/sparql_constraint.rb
174
191
  - lib/shacl/algebra/xone.rb
175
192
  - lib/shacl/context.rb
176
193
  - lib/shacl/format.rb
@@ -196,14 +213,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
196
213
  requirements:
197
214
  - - ">="
198
215
  - !ruby/object:Gem::Version
199
- version: '2.6'
216
+ version: '3.0'
200
217
  required_rubygems_version: !ruby/object:Gem::Requirement
201
218
  requirements:
202
219
  - - ">="
203
220
  - !ruby/object:Gem::Version
204
221
  version: '0'
205
222
  requirements: []
206
- rubygems_version: 3.3.3
223
+ rubygems_version: 3.2.33
207
224
  signing_key:
208
225
  specification_version: 4
209
226
  summary: Implementation of Shapes Constraint Language (SHACL) for RDF.rb