shacl 0.1.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.
@@ -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: []