shacl 0.2.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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