shacl 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,109 @@
1
+ $:.unshift(File.expand_path("../..", __FILE__))
2
+
3
+ require 'rdf'
4
+ require 'sxp'
5
+ require_relative 'refinements'
6
+ require_relative 'validation_report'
7
+
8
+ module SHACL
9
+ # A SHACL [Validateion Report](https://www.w3.org/TR/shacl/#results-validation-report).
10
+ #
11
+ # Collects the individual {SHACL::ValidationResult} instances and provides a `conforms` boolean accessor.
12
+ #
13
+ # Allows the report to be serialized as a set of RDF Statements
14
+ class ValidationReport
15
+ include RDF::Enumerable
16
+ using SHACL::Refinements
17
+
18
+ ##
19
+ # All results, both conforming and non-conforming
20
+ attr_reader :all_results
21
+
22
+ ##
23
+ # Creates a report from the set of results
24
+ #
25
+ # @param [Array<ValidationResult>] results
26
+ # @return [ValidationReport]
27
+ def initialize(results)
28
+ @all_results = Array(results)
29
+ end
30
+
31
+ ##
32
+ # The non-conforming results
33
+ #
34
+ # @return [Array<ValidationResult>]
35
+ def results
36
+ @all_results.reject(&:conform?)
37
+ end
38
+
39
+ ##
40
+ # The number of non-conforming results
41
+ #
42
+ # @return [Integer]
43
+ def count
44
+ results.length
45
+ end
46
+
47
+ ##
48
+ # Do the individual results indicate conformance?
49
+ #
50
+ # @return [Boolean]
51
+ def conform?
52
+ results.empty?
53
+ end
54
+
55
+ ##
56
+ # The number of results
57
+ #
58
+ def to_sxp_bin
59
+ [:ValidationReport, conform?, results].to_sxp_bin
60
+ end
61
+
62
+ def to_sxp
63
+ self.to_sxp_bin.to_sxp
64
+ end
65
+
66
+ def to_s
67
+ results.map(&:to_s).join("\n")
68
+ end
69
+
70
+ ##
71
+ # Two reports are eq if they have the same number of results and each result equals a result in the other report.
72
+ # @param [ValidationReport] other
73
+ # @return [Boolean]
74
+ def ==(other)
75
+ return false unless other.is_a?(ValidationReport)
76
+ count == other.count && other.results.all? {|r| results.include?(r)}
77
+ end
78
+
79
+ ##
80
+ # Create a hash of messages appropriate for linter-like output.
81
+ #
82
+ # @return [Hash{Symbol => Hash{Symbol => Array<String>}}]
83
+ def linter_messages
84
+ results.inject({}) {|memo, result| memo.deep_merge(result.linter_message)}
85
+ end
86
+
87
+ ##
88
+ # Yields statements for this report
89
+ #
90
+ # @yield [statement]
91
+ # each statement
92
+ # @yieldparam [RDF::Statement] statement
93
+ # @yieldreturn [void] ignored
94
+ # @return [void]
95
+ def each(&block)
96
+ subject = RDF::Node.new
97
+ block.call(RDF::Statement(subject, RDF.type, RDF::Vocab::SHACL.ValidationReport))
98
+ block.call(RDF::Statement(subject, RDF::Vocab::SHACL.conforms, RDF::Literal(conform?)))
99
+ results.each do |result|
100
+ result_subject = nil
101
+ result.each do |statement|
102
+ result_subject ||= statement.subject
103
+ yield(statement)
104
+ end
105
+ yield(RDF::Statement(subject, RDF::Vocab::SHACL.result, result_subject))
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,153 @@
1
+ $:.unshift(File.expand_path("../..", __FILE__))
2
+
3
+ require 'rdf'
4
+ require 'sxp'
5
+ require_relative 'context'
6
+ require_relative 'refinements'
7
+
8
+ module SHACL
9
+ # A SHACL [Validateion Result](https://www.w3.org/TR/shacl/#results-validation-result).
10
+ #
11
+ # Also allows for a successful result, if the `resultSeverity` `nil`.
12
+ ValidationResult = Struct.new(
13
+ :focus,
14
+ :path,
15
+ :shape,
16
+ :resultSeverity,
17
+ :component,
18
+ :details,
19
+ :value,
20
+ :message) do
21
+
22
+ include RDF::Enumerable
23
+ using SHACL::Refinements
24
+
25
+ ##
26
+ # Initializer calculates lexical values for URIs
27
+ def initialize(*args)
28
+ args = args.map do |v|
29
+ if v.respond_to?(:qname) && !v.lexical && v.qname
30
+ v = RDF::URI.new(v.to_s) if v.frozen?
31
+ v.lexical = v.qname.join(':')
32
+ end
33
+ v
34
+ end
35
+ super(*args)
36
+ end
37
+
38
+ # A result conforms if it is not a violation
39
+ #
40
+ # @return [Boolean]
41
+ def conform?
42
+ resultSeverity.nil?
43
+ end
44
+ alias_method :conforms?, :conform?
45
+
46
+ def to_sxp_bin
47
+ %i(value focus path shape resultSeverity component details message).inject([:ValidationResult]) do |memo, sym|
48
+ v = self.send(sym)
49
+ v ? (memo + [[sym, *v]]) : memo
50
+ end.to_sxp_bin
51
+ end
52
+
53
+ def to_sxp
54
+ self.to_sxp_bin.to_sxp
55
+ end
56
+
57
+ ##
58
+ # Create a hash of messages appropriate for linter-like output.
59
+ #
60
+ # @return [Hash{Symbol => Hash{Symbol => Array<String>}}]
61
+ def linter_message
62
+ case
63
+ when path then {path: {path.to_sxp => [to_s]}}
64
+ when focus then {focus: {focus.to_sxp => [to_s]}}
65
+ else {shape: {shape.to_sxp => [to_s]}}
66
+ end
67
+ end
68
+
69
+ ##
70
+ # Some humanized result for the report
71
+ def to_s
72
+ "Result for: " +
73
+ %i(value focus path shape resultSeverity component details message).map do |sym|
74
+ v = self.send(sym)
75
+ if v.respond_to?(:humanize)
76
+ v.humanize
77
+ elsif v.respond_to?(:lexical)
78
+ v.lexical
79
+ else
80
+ v.to_sxp
81
+ end
82
+ (sym == :value ? v.to_sxp : "#{sym}: #{v.to_sxp}") if v
83
+ end.compact.join("\n ")
84
+ end
85
+
86
+ ##
87
+ # Yields statements for this result
88
+ #
89
+ # @yield [statement]
90
+ # each statement
91
+ # @yieldparam [RDF::Statement] statement
92
+ # @yieldreturn [void] ignored
93
+ # @return [void]
94
+ def each(&block)
95
+ subject = RDF::Node.new
96
+ block.call(RDF::Statement(subject, RDF.type, RDF::Vocab::SHACL.ValidationResult))
97
+
98
+ block.call(RDF::Statement(subject, RDF::Vocab::SHACL.focusNode, focus)) if focus
99
+ case path
100
+ when RDF::URI
101
+ block.call(RDF::Statement(subject, RDF::Vocab::SHACL.resultPath, path))
102
+ when SPARQL::Algebra::Expression
103
+ path.each_statement(&block)
104
+ block.call(RDF::Statement(subject, RDF::Vocab::SHACL.resultPath, path.subject))
105
+ end
106
+ block.call(RDF::Statement(subject, RDF::Vocab::SHACL.resultSeverity, resultSeverity)) if resultSeverity
107
+ block.call(RDF::Statement(subject, RDF::Vocab::SHACL.sourceConstraintComponent, component)) if component
108
+ block.call(RDF::Statement(subject, RDF::Vocab::SHACL.sourceShape, shape)) if shape
109
+ block.call(RDF::Statement(subject, RDF::Vocab::SHACL.value, value)) if value
110
+ block.call(RDF::Statement(subject, RDF::Vocab::SHACL.detail, RDF::Literal(details))) if details
111
+ block.call(RDF::Statement(subject, RDF::Vocab::SHACL.resultMessage, RDF::Literal(message))) if message
112
+ end
113
+
114
+ # Transform a JSON representation of a result, into a native representation
115
+ # @param [Hash] input
116
+ # @return [ValidationResult]
117
+ def self.from_json(input, **options)
118
+ input = JSON.parse(input) if input.is_a?(String)
119
+ input = JSON::LD::API.compact(input,
120
+ "http://github.com/ruby-rdf/shacl/",
121
+ expandContext: "http://github.com/ruby-rdf/shacl/")
122
+ raise ArgumentError, "Expect report to be a hash" unless input.is_a?(Hash)
123
+ result = self.new
124
+
125
+ result.focus = Algebra::Operator.to_rdf(:focus, input['focusNode'], base: nil, vocab: false) if input['focusNode']
126
+ result.path = Algebra::Operator.parse_path(input['resultPath'], **options) if input['resultPath']
127
+ result.resultSeverity = Algebra::Operator.iri(input['resultSeverity'], **options) if input['resultSeverity']
128
+ result.component = Algebra::Operator.iri(input['sourceConstraintComponent'], **options) if input['sourceConstraintComponent']
129
+ result.shape = Algebra::Operator.iri(input['sourceShape'], **options) if input['sourceShape']
130
+ result.value = Algebra::Operator.to_rdf(:value, input['value'], **options) if input['value']
131
+ result.details = Algebra::Operator.to_rdf(:details, input['details'], **options) if input['details']
132
+ result.message = Algebra::Operator.to_rdf(:message, input['message'], **options) if input['message']
133
+ result
134
+ end
135
+
136
+ # To results are eql? if their overlapping properties are equal
137
+ # @param [ValidationResult] other
138
+ # @return [Boolean]
139
+ def ==(other)
140
+ return false unless other.is_a?(ValidationResult)
141
+ %i(focus path resultSeverity component shape value).all? do |prop|
142
+ ours = self.send(prop)
143
+ theirs = other.send(prop)
144
+ theirs.nil? || (ours && ours.node? && theirs.node?) || ours && ours.eql?(theirs)
145
+ end
146
+ end
147
+
148
+ # Inspect as SXP
149
+ def inspect
150
+ SXP::Generator.string to_sxp_bin
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,19 @@
1
+ module SHACL
2
+ module VERSION
3
+ FILE = File.expand_path('../../../VERSION', __FILE__)
4
+ MAJOR, MINOR, TINY, EXTRA = File.read(FILE).chomp.split('.')
5
+ STRING = [MAJOR, MINOR, TINY, EXTRA].compact.join('.').freeze
6
+
7
+ ##
8
+ # @return [String]
9
+ def self.to_s() STRING end
10
+
11
+ ##
12
+ # @return [String]
13
+ def self.to_str() STRING end
14
+
15
+ ##
16
+ # @return [Array(String, String, String, String)]
17
+ def self.to_a() [MAJOR, MINOR, TINY, EXTRA].compact end
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,217 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shacl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Gregg Kellogg
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-12-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rdf
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.1'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 3.1.8
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '3.1'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 3.1.8
33
+ - !ruby/object:Gem::Dependency
34
+ name: json-ld
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.1'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 3.1.7
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '3.1'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 3.1.7
53
+ - !ruby/object:Gem::Dependency
54
+ name: sxp
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '1.1'
60
+ type: :runtime
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '1.1'
67
+ - !ruby/object:Gem::Dependency
68
+ name: sparql
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '3.1'
74
+ type: :runtime
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '3.1'
81
+ - !ruby/object:Gem::Dependency
82
+ name: rdf-spec
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '3.1'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '3.1'
95
+ - !ruby/object:Gem::Dependency
96
+ name: rdf-turtle
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '3.1'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '3.1'
109
+ - !ruby/object:Gem::Dependency
110
+ name: rdf-xsd
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: '3.1'
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - "~>"
121
+ - !ruby/object:Gem::Version
122
+ version: '3.1'
123
+ - !ruby/object:Gem::Dependency
124
+ name: rspec
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: '3.10'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - "~>"
135
+ - !ruby/object:Gem::Version
136
+ version: '3.10'
137
+ - !ruby/object:Gem::Dependency
138
+ name: rspec-its
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: '1.3'
144
+ type: :development
145
+ prerelease: false
146
+ version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: '1.3'
151
+ - !ruby/object:Gem::Dependency
152
+ name: yard
153
+ requirement: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - "~>"
156
+ - !ruby/object:Gem::Version
157
+ version: '0.9'
158
+ type: :development
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - "~>"
163
+ - !ruby/object:Gem::Version
164
+ version: '0.9'
165
+ description: Implements SHACL Core and SHACL-SPARQL within the RDF.rb ecosystem.
166
+ email: public-rdf-ruby@w3.org
167
+ executables: []
168
+ extensions: []
169
+ extra_rdoc_files: []
170
+ files:
171
+ - LICENSE
172
+ - README.md
173
+ - VERSION
174
+ - etc/doap.ttl
175
+ - lib/rdf/vocab/shacl.rb
176
+ - lib/shacl.rb
177
+ - lib/shacl/algebra.rb
178
+ - lib/shacl/algebra/and.rb
179
+ - lib/shacl/algebra/node_shape.rb
180
+ - lib/shacl/algebra/not.rb
181
+ - lib/shacl/algebra/operator.rb
182
+ - lib/shacl/algebra/or.rb
183
+ - lib/shacl/algebra/property_shape.rb
184
+ - lib/shacl/algebra/qualified_value_shape.rb
185
+ - lib/shacl/algebra/shape.rb
186
+ - lib/shacl/algebra/xone.rb
187
+ - lib/shacl/context.rb
188
+ - lib/shacl/format.rb
189
+ - lib/shacl/refinements.rb
190
+ - lib/shacl/shapes.rb
191
+ - lib/shacl/validation_report.rb
192
+ - lib/shacl/validation_result.rb
193
+ - lib/shacl/version.rb
194
+ homepage: https://ruby-rdf.github.com/shacl
195
+ licenses:
196
+ - Unlicense
197
+ metadata: {}
198
+ post_install_message:
199
+ rdoc_options: []
200
+ require_paths:
201
+ - lib
202
+ required_ruby_version: !ruby/object:Gem::Requirement
203
+ requirements:
204
+ - - ">="
205
+ - !ruby/object:Gem::Version
206
+ version: '2.4'
207
+ required_rubygems_version: !ruby/object:Gem::Requirement
208
+ requirements:
209
+ - - ">="
210
+ - !ruby/object:Gem::Version
211
+ version: '0'
212
+ requirements: []
213
+ rubygems_version: 3.1.4
214
+ signing_key:
215
+ specification_version: 4
216
+ summary: Implementation of Shapes Constraint Language (SHACL) for RDF.rb
217
+ test_files: []