glossarist 2.8.18 → 2.9.1
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.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +1 -0
- data/.rubocop_todo.yml +175 -8
- data/Rakefile +4 -0
- data/config.yml +2 -3
- data/data/concept-model/README.md +35 -0
- data/data/concept-model/SOURCE.json +5 -0
- data/data/concept-model/glossarist.context.jsonld +261 -0
- data/data/concept-model/glossarist.ttl +1327 -0
- data/data/concept-model/prefixes.ttl +27 -0
- data/data/concept-model/shapes/glossarist.shacl.ttl +815 -0
- data/glossarist.gemspec +2 -0
- data/lib/glossarist/asset_reference.rb +2 -0
- data/lib/glossarist/bibliographic_reference.rb +13 -0
- data/lib/glossarist/cli/export_command.rb +130 -7
- data/lib/glossarist/cli.rb +11 -2
- data/lib/glossarist/concept_document.rb +13 -0
- data/lib/glossarist/concept_reference.rb +6 -0
- data/lib/glossarist/concept_store.rb +2 -3
- data/lib/glossarist/dataset_register.rb +2 -2
- data/lib/glossarist/figure_reference.rb +0 -4
- data/lib/glossarist/formula_reference.rb +0 -4
- data/lib/glossarist/non_verbal_reference.rb +10 -0
- data/lib/glossarist/rdf/gloss_detailed_definition.rb +6 -1
- data/lib/glossarist/rdf/gloss_localized_concept.rb +65 -0
- data/lib/glossarist/rdf/lutaml_ext.rb +33 -0
- data/lib/glossarist/rdf.rb +2 -0
- data/lib/glossarist/reference.rb +33 -0
- data/lib/glossarist/table_reference.rb +0 -4
- data/lib/glossarist/tasks/shacl.rake +30 -0
- data/lib/glossarist/tasks/sync.rake +14 -0
- data/lib/glossarist/tasks/sync_model.rb +96 -0
- data/lib/glossarist/transforms/concept_to_gloss_transform.rb +3 -2
- data/lib/glossarist/utilities/uuid.rb +5 -1
- data/lib/glossarist/validation/rules/related_concept_cycle_rule.rb +15 -1
- data/lib/glossarist/validation/shacl_validator.rb +97 -0
- data/lib/glossarist/validation.rb +1 -0
- data/lib/glossarist/version.rb +1 -1
- data/lib/glossarist.rb +1 -0
- metadata +42 -2
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "net/http"
|
|
6
|
+
require "uri"
|
|
7
|
+
|
|
8
|
+
module Glossarist
|
|
9
|
+
module Tasks
|
|
10
|
+
# Syncs vendored concept-model data artifacts from glossarist/concept-model.
|
|
11
|
+
#
|
|
12
|
+
# concept-model is a data-only repo (TTL, JSON-LD, YAML schemas). It is
|
|
13
|
+
# not a gem. We vendor the small set of artifacts we need (shapes,
|
|
14
|
+
# context, ontology) and load them at runtime via ShaclValidator.
|
|
15
|
+
module SyncModel
|
|
16
|
+
REPO = "glossarist/concept-model"
|
|
17
|
+
OUT_DIR = File.expand_path("data/concept-model", File.join(__dir__, "..", "..", ".."))
|
|
18
|
+
|
|
19
|
+
TARGETS = {
|
|
20
|
+
"prefixes.ttl" => %w[
|
|
21
|
+
ontologies/prefixes.ttl
|
|
22
|
+
prefixes.ttl
|
|
23
|
+
].freeze,
|
|
24
|
+
"glossarist.context.jsonld" => %w[
|
|
25
|
+
ontologies/glossarist.context.jsonld
|
|
26
|
+
glossarist.context.jsonld
|
|
27
|
+
].freeze,
|
|
28
|
+
"glossarist.ttl" => %w[
|
|
29
|
+
ontologies/glossarist.ttl
|
|
30
|
+
glossarist.ttl
|
|
31
|
+
].freeze,
|
|
32
|
+
"shapes/glossarist.shacl.ttl" => %w[
|
|
33
|
+
ontologies/shapes/glossarist.shacl.ttl
|
|
34
|
+
shapes/glossarist.shacl.ttl
|
|
35
|
+
].freeze,
|
|
36
|
+
}.freeze
|
|
37
|
+
|
|
38
|
+
class << self
|
|
39
|
+
def call(ref: nil)
|
|
40
|
+
ref ||= latest_tag
|
|
41
|
+
FileUtils.mkdir_p(File.join(OUT_DIR, "shapes"))
|
|
42
|
+
|
|
43
|
+
TARGETS.each do |out_rel, candidates|
|
|
44
|
+
content = fetch_any(ref, candidates)
|
|
45
|
+
out_path = File.join(OUT_DIR, out_rel)
|
|
46
|
+
FileUtils.mkdir_p(File.dirname(out_path))
|
|
47
|
+
File.write(out_path, content)
|
|
48
|
+
puts " ✓ #{out_rel} (#{content.length} bytes)"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
write_source_manifest(ref)
|
|
52
|
+
puts "\nSynced #{TARGETS.length} file(s) from #{REPO}@#{ref}."
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def latest_tag
|
|
58
|
+
url = URI("https://api.github.com/repos/#{REPO}/releases/latest")
|
|
59
|
+
req = Net::HTTP::Get.new(url)
|
|
60
|
+
req["Accept"] = "application/vnd.github+json"
|
|
61
|
+
JSON.parse(Net::HTTP.start(url.hostname, url.port, use_ssl: true) { |http| http.request(req) }.body)
|
|
62
|
+
.fetch("tag_name")
|
|
63
|
+
rescue StandardError => e
|
|
64
|
+
warn "Could not determine latest concept-model tag: #{e.message}"
|
|
65
|
+
exit 1
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def fetch_any(ref, candidates)
|
|
69
|
+
candidates.each do |path|
|
|
70
|
+
begin
|
|
71
|
+
return fetch_file(ref, path)
|
|
72
|
+
rescue StandardError
|
|
73
|
+
next
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
raise "Could not fetch any of: #{candidates.join(', ')}"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def fetch_file(ref, path)
|
|
80
|
+
url = URI("https://raw.githubusercontent.com/#{REPO}/#{ref}/#{path}")
|
|
81
|
+
Net::HTTP.get(url)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def write_source_manifest(ref)
|
|
85
|
+
manifest = {
|
|
86
|
+
"repo" => REPO,
|
|
87
|
+
"ref" => ref,
|
|
88
|
+
"syncedAt" => Time.now.utc.iso8601,
|
|
89
|
+
}
|
|
90
|
+
File.write(File.join(OUT_DIR, "SOURCE.json"),
|
|
91
|
+
JSON.pretty_generate(manifest) + "\n")
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -122,10 +122,11 @@ module Glossarist
|
|
|
122
122
|
|
|
123
123
|
dd_attrs = if data
|
|
124
124
|
data.class.detailed_definition_fields.to_h do |field|
|
|
125
|
-
|
|
125
|
+
key = field == :definition ? :definitions : field
|
|
126
|
+
[key, build_gloss_definitions(data.public_send(field))]
|
|
126
127
|
end
|
|
127
128
|
else
|
|
128
|
-
{
|
|
129
|
+
{ definitions: [], notes: [], examples: [] }
|
|
129
130
|
end
|
|
130
131
|
|
|
131
132
|
sources = build_gloss_sources(data&.sources)
|
|
@@ -65,7 +65,11 @@ module Glossarist
|
|
|
65
65
|
else
|
|
66
66
|
match_data = namespace.match(/\A(\h{8})-(\h{4})-(\h{4})-(\h{4})-(\h{4})(\h{8})\z/)
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
# `match_data` is a MatchData on success, nil on failure. Do not
|
|
69
|
+
# use ActiveSupport's `#present?` here — ActiveSupport is not a
|
|
70
|
+
# declared dependency of this gem, and `#present?` raises
|
|
71
|
+
# NoMethodError in bundles where it isn't loaded.
|
|
72
|
+
unless match_data
|
|
69
73
|
raise ArgumentError,
|
|
70
74
|
"Only UUIDs are valid namespace identifiers"
|
|
71
75
|
end
|
|
@@ -54,11 +54,25 @@ module Glossarist
|
|
|
54
54
|
graph
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
+
# Returns a graph node identifier for the target of a relationship.
|
|
58
|
+
#
|
|
59
|
+
# For intra-edition references (ref.source is nil), the target is
|
|
60
|
+
# just ref.id — a clause identifier unique within the dataset.
|
|
61
|
+
#
|
|
62
|
+
# Returns nil for cross-edition references (ref.source is a URN,
|
|
63
|
+
# e.g. a +supersedes+ edge pointing at the previous edition's
|
|
64
|
+
# concept with the same clause id). Such edges cannot form
|
|
65
|
+
# cycles within the current dataset and are excluded from the
|
|
66
|
+
# graph. Without this exclusion, a cross-edition edge from
|
|
67
|
+
# concept 3.1.1.1 to its predecessor in another edition (which
|
|
68
|
+
# also has identifier 3.1.1.1) would look like a self-loop and
|
|
69
|
+
# trip the cycle detector with a false positive.
|
|
57
70
|
def resolve_target_id(rel)
|
|
58
71
|
ref = rel.ref
|
|
59
72
|
return nil unless ref
|
|
73
|
+
return nil if ref.source
|
|
60
74
|
|
|
61
|
-
ref.id
|
|
75
|
+
ref.id
|
|
62
76
|
end
|
|
63
77
|
|
|
64
78
|
def detect_cycles(graph)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rdf/turtle"
|
|
4
|
+
require "shacl"
|
|
5
|
+
require "pathname"
|
|
6
|
+
|
|
7
|
+
module Glossarist
|
|
8
|
+
module Validation
|
|
9
|
+
# Validates Turtle output against SHACL shapes.
|
|
10
|
+
#
|
|
11
|
+
# Shapes resolution order:
|
|
12
|
+
# 1. Explicit :shapes_path option
|
|
13
|
+
# 2. Vendored shapes at data/concept-model/shapes/glossarist.shacl.ttl
|
|
14
|
+
# (shipped with the gem, relative to the gem root)
|
|
15
|
+
class ShaclValidator
|
|
16
|
+
VENDORED_SHAPES_PATH =
|
|
17
|
+
File.expand_path(
|
|
18
|
+
"../../../data/concept-model/shapes/glossarist.shacl.ttl",
|
|
19
|
+
__dir__
|
|
20
|
+
).freeze
|
|
21
|
+
|
|
22
|
+
attr_reader :shapes_path
|
|
23
|
+
|
|
24
|
+
def initialize(shapes_path: nil)
|
|
25
|
+
@shapes_path = shapes_path || self.class.default_shapes_path
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def validate_files(paths)
|
|
29
|
+
validate_graphs(Array(paths).map { |p| load_graph(p) })
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def validate_graphs(graphs)
|
|
33
|
+
shapes = SHACL::Shapes.from_graph(load_graph(@shapes_path))
|
|
34
|
+
failures = []
|
|
35
|
+
|
|
36
|
+
graphs.each do |graph|
|
|
37
|
+
report = shapes.execute(graph)
|
|
38
|
+
next if report.conform?
|
|
39
|
+
|
|
40
|
+
failures << Report.new(results: report.results)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
AggregateReport.new(failures: failures)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class << self
|
|
47
|
+
def default_shapes_path
|
|
48
|
+
return VENDORED_SHAPES_PATH if File.exist?(VENDORED_SHAPES_PATH)
|
|
49
|
+
|
|
50
|
+
raise ArgumentError,
|
|
51
|
+
"No SHACL shapes path provided and the vendored shapes " \
|
|
52
|
+
"file (#{VENDORED_SHAPES_PATH}) is missing. Pass " \
|
|
53
|
+
"shapes_path: '/path/to/glossarist.shacl.ttl'."
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def load_graph(path)
|
|
60
|
+
return path if path.is_a?(RDF::Graph)
|
|
61
|
+
|
|
62
|
+
graph = RDF::Graph.new
|
|
63
|
+
RDF::Turtle::Reader.new(File.read(path)) do |reader|
|
|
64
|
+
reader.each_statement { |stmt| graph << stmt }
|
|
65
|
+
end
|
|
66
|
+
graph
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
Report = Struct.new(:results, keyword_init: true) do
|
|
71
|
+
def conformant?
|
|
72
|
+
results.empty?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def to_s
|
|
76
|
+
" #{results.length} violation(s)"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
AggregateReport = Struct.new(:failures, keyword_init: true) do
|
|
81
|
+
def conformant?
|
|
82
|
+
failures.empty?
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def to_s
|
|
86
|
+
lines = ["SHACL validation failed: #{failures.length} file(s) with violations"]
|
|
87
|
+
failures.each do |failure|
|
|
88
|
+
lines << failure.to_s
|
|
89
|
+
failure.results.each do |result|
|
|
90
|
+
lines << " #{result.path}: #{result.message}"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
lines.join("\n")
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -8,5 +8,6 @@ module Glossarist
|
|
|
8
8
|
autoload :Rules, "glossarist/validation/rules"
|
|
9
9
|
autoload :BibliographyIndex, "glossarist/validation/bibliography_index"
|
|
10
10
|
autoload :AssetIndex, "glossarist/validation/asset_index"
|
|
11
|
+
autoload :ShaclValidator, "glossarist/validation/shacl_validator"
|
|
11
12
|
end
|
|
12
13
|
end
|
data/lib/glossarist/version.rb
CHANGED
data/lib/glossarist.rb
CHANGED
|
@@ -23,6 +23,7 @@ module Glossarist
|
|
|
23
23
|
autoload :ConceptData, "glossarist/concept_data"
|
|
24
24
|
autoload :ConceptRef, "glossarist/concept_ref"
|
|
25
25
|
autoload :ConceptReference, "glossarist/concept_reference"
|
|
26
|
+
autoload :Reference, "glossarist/reference"
|
|
26
27
|
autoload :ReferenceExtractor, "glossarist/reference_extractor"
|
|
27
28
|
autoload :ReferenceResolver, "glossarist/reference_resolver"
|
|
28
29
|
autoload :ResolutionAdapter, "glossarist/resolution_adapter"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: glossarist
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.9.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-07-
|
|
11
|
+
date: 2026-07-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: lutaml-model
|
|
@@ -52,6 +52,20 @@ dependencies:
|
|
|
52
52
|
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '2.3'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rdf-turtle
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '3.3'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '3.3'
|
|
55
69
|
- !ruby/object:Gem::Dependency
|
|
56
70
|
name: relaton
|
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -92,6 +106,20 @@ dependencies:
|
|
|
92
106
|
- - "<"
|
|
93
107
|
- !ruby/object:Gem::Version
|
|
94
108
|
version: '3'
|
|
109
|
+
- !ruby/object:Gem::Dependency
|
|
110
|
+
name: shacl
|
|
111
|
+
requirement: !ruby/object:Gem::Requirement
|
|
112
|
+
requirements:
|
|
113
|
+
- - "~>"
|
|
114
|
+
- !ruby/object:Gem::Version
|
|
115
|
+
version: '0.4'
|
|
116
|
+
type: :runtime
|
|
117
|
+
prerelease: false
|
|
118
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
119
|
+
requirements:
|
|
120
|
+
- - "~>"
|
|
121
|
+
- !ruby/object:Gem::Version
|
|
122
|
+
version: '0.4'
|
|
95
123
|
- !ruby/object:Gem::Dependency
|
|
96
124
|
name: sts
|
|
97
125
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -170,6 +198,12 @@ files:
|
|
|
170
198
|
- README.adoc
|
|
171
199
|
- Rakefile
|
|
172
200
|
- config.yml
|
|
201
|
+
- data/concept-model/README.md
|
|
202
|
+
- data/concept-model/SOURCE.json
|
|
203
|
+
- data/concept-model/glossarist.context.jsonld
|
|
204
|
+
- data/concept-model/glossarist.ttl
|
|
205
|
+
- data/concept-model/prefixes.ttl
|
|
206
|
+
- data/concept-model/shapes/glossarist.shacl.ttl
|
|
173
207
|
- exe/glossarist
|
|
174
208
|
- glossarist.gemspec
|
|
175
209
|
- lib/glossarist.rb
|
|
@@ -276,6 +310,7 @@ files:
|
|
|
276
310
|
- lib/glossarist/rdf/gloss_pronunciation.rb
|
|
277
311
|
- lib/glossarist/rdf/gloss_reference.rb
|
|
278
312
|
- lib/glossarist/rdf/localized_literal.rb
|
|
313
|
+
- lib/glossarist/rdf/lutaml_ext.rb
|
|
279
314
|
- lib/glossarist/rdf/namespaces.rb
|
|
280
315
|
- lib/glossarist/rdf/namespaces/dcterms_namespace.rb
|
|
281
316
|
- lib/glossarist/rdf/namespaces/glossarist_namespace.rb
|
|
@@ -288,6 +323,7 @@ files:
|
|
|
288
323
|
- lib/glossarist/rdf/relationship_predicates.rb
|
|
289
324
|
- lib/glossarist/rdf/v3.rb
|
|
290
325
|
- lib/glossarist/rdf/v3/configuration.rb
|
|
326
|
+
- lib/glossarist/reference.rb
|
|
291
327
|
- lib/glossarist/reference_extractor.rb
|
|
292
328
|
- lib/glossarist/reference_resolver.rb
|
|
293
329
|
- lib/glossarist/register_data.rb
|
|
@@ -313,6 +349,9 @@ files:
|
|
|
313
349
|
- lib/glossarist/sts/term_mapper.rb
|
|
314
350
|
- lib/glossarist/table.rb
|
|
315
351
|
- lib/glossarist/table_reference.rb
|
|
352
|
+
- lib/glossarist/tasks/shacl.rake
|
|
353
|
+
- lib/glossarist/tasks/sync.rake
|
|
354
|
+
- lib/glossarist/tasks/sync_model.rb
|
|
316
355
|
- lib/glossarist/transforms.rb
|
|
317
356
|
- lib/glossarist/transforms/concept_to_gloss_transform.rb
|
|
318
357
|
- lib/glossarist/transforms/concept_to_tbx_transform.rb
|
|
@@ -402,6 +441,7 @@ files:
|
|
|
402
441
|
- lib/glossarist/validation/rules/source_urn_format_rule.rb
|
|
403
442
|
- lib/glossarist/validation/rules/terms_presence_rule.rb
|
|
404
443
|
- lib/glossarist/validation/rules/uuid_format_rule.rb
|
|
444
|
+
- lib/glossarist/validation/shacl_validator.rb
|
|
405
445
|
- lib/glossarist/validation/validation_issue.rb
|
|
406
446
|
- lib/glossarist/validation_result.rb
|
|
407
447
|
- lib/glossarist/version.rb
|