glossarist 2.4.0 → 2.5.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/.gitignore +1 -0
- data/.rubocop_todo.yml +50 -146
- data/CLAUDE.md +85 -0
- data/Gemfile +26 -5
- data/README.adoc +383 -7
- data/TODO.integration/01-gcr-package-cli.md +180 -0
- data/exe/glossarist +1 -53
- data/glossarist.gemspec +3 -2
- data/lib/glossarist/asset.rb +1 -1
- data/lib/glossarist/citation.rb +1 -1
- data/lib/glossarist/cli/package_command.rb +32 -0
- data/lib/glossarist/cli/upgrade_command.rb +34 -0
- data/lib/glossarist/cli/validate_command.rb +56 -0
- data/lib/glossarist/cli.rb +105 -0
- data/lib/glossarist/collection_config.rb +23 -0
- data/lib/glossarist/collections/concept_source_collection.rb +9 -0
- data/lib/glossarist/collections/detailed_definition_collection.rb +18 -0
- data/lib/glossarist/collections/localization_collection.rb +37 -0
- data/lib/glossarist/collections/typed_collection.rb +26 -0
- data/lib/glossarist/collections.rb +21 -4
- data/lib/glossarist/concept.rb +1 -1
- data/lib/glossarist/concept_collector.rb +153 -0
- data/lib/glossarist/concept_data.rb +15 -8
- data/lib/glossarist/concept_date.rb +1 -1
- data/lib/glossarist/concept_document.rb +29 -0
- data/lib/glossarist/concept_enricher.rb +34 -0
- data/lib/glossarist/concept_manager.rb +31 -49
- data/lib/glossarist/concept_reference.rb +45 -0
- data/lib/glossarist/concept_source.rb +1 -1
- data/lib/glossarist/concept_validator.rb +114 -0
- data/lib/glossarist/custom_locality.rb +1 -1
- data/lib/glossarist/dataset_validator.rb +69 -0
- data/lib/glossarist/designation/abbreviation.rb +1 -1
- data/lib/glossarist/designation/base.rb +11 -4
- data/lib/glossarist/designation/expression.rb +1 -1
- data/lib/glossarist/designation/grammar_info.rb +1 -1
- data/lib/glossarist/designation/graphical_symbol.rb +1 -1
- data/lib/glossarist/designation/letter_symbol.rb +1 -1
- data/lib/glossarist/designation/symbol.rb +2 -2
- data/lib/glossarist/designation.rb +8 -11
- data/lib/glossarist/detailed_definition.rb +1 -1
- data/lib/glossarist/error.rb +2 -5
- data/lib/glossarist/gcr_metadata.rb +87 -0
- data/lib/glossarist/gcr_package.rb +223 -0
- data/lib/glossarist/gcr_statistics.rb +35 -0
- data/lib/glossarist/gcr_validator.rb +98 -0
- data/lib/glossarist/locality.rb +1 -1
- data/lib/glossarist/localized_concept.rb +12 -1
- data/lib/glossarist/managed_concept.rb +1 -1
- data/lib/glossarist/managed_concept_data.rb +8 -5
- data/lib/glossarist/non_verb_rep.rb +1 -1
- data/lib/glossarist/reference_extractor.rb +227 -0
- data/lib/glossarist/reference_resolver.rb +169 -0
- data/lib/glossarist/register_data.rb +39 -0
- data/lib/glossarist/related_concept.rb +1 -1
- data/lib/glossarist/resolution_adapter/local.rb +73 -0
- data/lib/glossarist/resolution_adapter/package.rb +22 -0
- data/lib/glossarist/resolution_adapter/remote.rb +60 -0
- data/lib/glossarist/resolution_adapter/route.rb +34 -0
- data/lib/glossarist/resolution_adapter.rb +14 -0
- data/lib/glossarist/schema_migration.rb +334 -0
- data/lib/glossarist/urn_resolver.rb +71 -0
- data/lib/glossarist/utilities.rb +6 -2
- data/lib/glossarist/v1/concept.rb +81 -0
- data/lib/glossarist/v1/cross_references.rb +41 -0
- data/lib/glossarist/v1/register.rb +50 -0
- data/lib/glossarist/v1.rb +9 -0
- data/lib/glossarist/validation_result.rb +38 -0
- data/lib/glossarist/version.rb +1 -1
- data/lib/glossarist.rb +54 -24
- metadata +62 -6
|
@@ -3,7 +3,7 @@ module Glossarist
|
|
|
3
3
|
attribute :path, :string
|
|
4
4
|
attribute :localized_concepts_path, :string
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
key_value do
|
|
7
7
|
map :path, to: :path
|
|
8
8
|
map %i[localized_concepts_path localizedConceptsPath],
|
|
9
9
|
to: :localized_concepts_path
|
|
@@ -33,62 +33,43 @@ module Glossarist
|
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
def
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
def load_concept_from_file(filename) # rubocop:disable Metrics/CyclomaticComplexity
|
|
37
|
+
raw = File.read(filename, encoding: "utf-8")
|
|
38
|
+
doc = ConceptDocument.from_yamls(raw)
|
|
39
|
+
concept = doc.concept
|
|
40
|
+
unless concept
|
|
41
|
+
raise Glossarist::ParseError.new(filename: filename)
|
|
40
42
|
end
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
[concept_hashes, localized_concept_hashes]
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def load_concept_from_file(filename)
|
|
51
|
-
mixed_hashes = YAML.load_stream(File.read(filename, encoding: "utf-8"))
|
|
52
|
-
concepts = []
|
|
53
|
-
|
|
54
|
-
concept_hashes, localized_concept_hashes =
|
|
55
|
-
group_concept_hashes(mixed_hashes)
|
|
56
|
-
|
|
57
|
-
concept_hashes.each do |concept_hash|
|
|
58
|
-
concept_hash["uuid"] = concept_hash["id"] ||
|
|
59
|
-
File.basename(filename, ".*")
|
|
60
|
-
concept = Config.class_for(:managed_concept).of_yaml(concept_hash)
|
|
44
|
+
concept_uuid = concept.identifier || concept.data&.id || File.basename(
|
|
45
|
+
filename, ".*"
|
|
46
|
+
)
|
|
47
|
+
concept.instance_variable_set(:@uuid, concept_uuid)
|
|
61
48
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
concept.add_l10n(localized_concept)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
concepts << concept
|
|
49
|
+
concept.data.localized_concepts.each_value do |id|
|
|
50
|
+
localized_concept = load_localized_concept(id, doc.localizations)
|
|
51
|
+
concept.add_l10n(localized_concept)
|
|
69
52
|
end
|
|
70
53
|
|
|
71
|
-
|
|
54
|
+
[concept]
|
|
72
55
|
rescue Psych::SyntaxError => e
|
|
73
56
|
raise Glossarist::ParseError.new(filename: filename, line: e.line)
|
|
74
57
|
end
|
|
75
58
|
|
|
76
|
-
def load_localized_concept(id,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
concept_hash["uuid"] = id
|
|
91
|
-
Config.class_for(:localized_concept).of_yaml(concept_hash)
|
|
59
|
+
def load_localized_concept(id, inline_localizations = nil)
|
|
60
|
+
if inline_localizations
|
|
61
|
+
l10n = inline_localizations.find { |l| l.id == id }
|
|
62
|
+
if l10n
|
|
63
|
+
l10n.instance_variable_set(:@uuid, id)
|
|
64
|
+
return l10n
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
l10n = LocalizedConcept.from_yaml(
|
|
69
|
+
File.read(localized_concept_path(id), encoding: "utf-8"),
|
|
70
|
+
)
|
|
71
|
+
l10n.instance_variable_set(:@uuid, id)
|
|
72
|
+
l10n
|
|
92
73
|
rescue Psych::SyntaxError => e
|
|
93
74
|
raise Glossarist::ParseError.new(filename: filename, line: e.line)
|
|
94
75
|
end
|
|
@@ -107,7 +88,8 @@ module Glossarist
|
|
|
107
88
|
|
|
108
89
|
concept.localized_concepts.each do |lang, uuid|
|
|
109
90
|
filename = File.join(localized_concept_dir, "#{uuid}.yaml")
|
|
110
|
-
File.write(filename, concept.localization(lang).to_yaml,
|
|
91
|
+
File.write(filename, concept.localization(lang).to_yaml,
|
|
92
|
+
encoding: "utf-8")
|
|
111
93
|
end
|
|
112
94
|
end
|
|
113
95
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
class ConceptReference < Lutaml::Model::Serializable
|
|
5
|
+
attribute :term, :string
|
|
6
|
+
attribute :concept_id, :string
|
|
7
|
+
attribute :source, :string
|
|
8
|
+
attribute :ref_type, :string
|
|
9
|
+
|
|
10
|
+
key_value do
|
|
11
|
+
map :term, to: :term
|
|
12
|
+
map :concept_id, to: :concept_id
|
|
13
|
+
map :source, to: :source
|
|
14
|
+
map :ref_type, to: :ref_type
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def local?
|
|
18
|
+
%w[local designation].include?(ref_type) ||
|
|
19
|
+
(ref_type.nil? && (source.nil? || source.empty?))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def external?
|
|
23
|
+
!local?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_urn
|
|
27
|
+
return nil unless external?
|
|
28
|
+
return nil unless source && concept_id
|
|
29
|
+
|
|
30
|
+
case source
|
|
31
|
+
when /\Aurn:iec/ then "#{source}-#{concept_id}"
|
|
32
|
+
when /\Aurn:iso/ then "#{source}:term:#{concept_id}"
|
|
33
|
+
else "#{source}/#{concept_id}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def to_gcr_hash
|
|
38
|
+
h = { "term" => term }
|
|
39
|
+
h["concept_id"] = concept_id if concept_id
|
|
40
|
+
h["source"] = source if source
|
|
41
|
+
h["ref_type"] = ref_type if ref_type
|
|
42
|
+
h.compact
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
class ConceptValidator
|
|
5
|
+
LANG_CODES = Glossarist::LANG_CODES
|
|
6
|
+
VALID_ENTRY_STATUSES = %w[valid superseded withdrawn draft].freeze
|
|
7
|
+
|
|
8
|
+
attr_reader :path, :errors, :warnings
|
|
9
|
+
|
|
10
|
+
def initialize(path)
|
|
11
|
+
@path = path
|
|
12
|
+
@errors = []
|
|
13
|
+
@warnings = []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def validate_all
|
|
17
|
+
seen_ids = {}
|
|
18
|
+
file_idx = 0
|
|
19
|
+
|
|
20
|
+
ConceptCollector.each_concept(@path) do |concept|
|
|
21
|
+
fname = concept_file_name(concept, file_idx)
|
|
22
|
+
validate_concept(concept, fname, seen_ids)
|
|
23
|
+
file_idx += 1
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
if file_idx.zero?
|
|
27
|
+
yaml_files = find_yaml_files
|
|
28
|
+
if yaml_files.any?
|
|
29
|
+
@errors << "YAML files found but no parseable concepts"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
ValidationResult.new(errors: @errors, warnings: @warnings)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def find_yaml_files
|
|
39
|
+
concepts_dir = if File.directory?(File.join(@path, "concepts"))
|
|
40
|
+
File.join(@path, "concepts")
|
|
41
|
+
else
|
|
42
|
+
@path
|
|
43
|
+
end
|
|
44
|
+
Dir.glob(File.join(concepts_dir, "*.yaml"))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def concept_file_name(concept, idx)
|
|
48
|
+
id = concept.data&.id
|
|
49
|
+
id ? "concept-#{id}.yaml" : "concept-#{idx}.yaml"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def validate_concept(concept, fname, seen_ids)
|
|
53
|
+
validate_id(concept, fname, seen_ids)
|
|
54
|
+
validate_localizations(concept, fname)
|
|
55
|
+
validate_definitions(concept, fname)
|
|
56
|
+
validate_entry_statuses(concept, fname)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def validate_id(concept, fname, seen_ids)
|
|
60
|
+
id = concept.data&.id
|
|
61
|
+
unless id
|
|
62
|
+
@errors << "#{fname}: missing concept id"
|
|
63
|
+
return
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
id_str = id.to_s
|
|
67
|
+
if seen_ids[id_str]
|
|
68
|
+
@errors << "#{fname}: duplicate id '#{id_str}' (first seen in #{seen_ids[id_str]})"
|
|
69
|
+
else
|
|
70
|
+
seen_ids[id_str] = fname
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def validate_localizations(concept, fname)
|
|
75
|
+
l10ns = concept.localizations&.values || []
|
|
76
|
+
if l10ns.empty?
|
|
77
|
+
@errors << "#{fname}: no localizations found"
|
|
78
|
+
return
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
l10ns.each do |l10n|
|
|
82
|
+
lang = l10n.language_code || "unknown"
|
|
83
|
+
terms = l10n.data&.terms
|
|
84
|
+
unless terms.is_a?(Array) && terms.any?
|
|
85
|
+
@errors << "#{fname}/#{lang}: must have at least 1 term"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def validate_definitions(concept, fname)
|
|
91
|
+
(concept.localizations&.values || []).each do |l10n|
|
|
92
|
+
lang = l10n.language_code || "unknown"
|
|
93
|
+
next unless l10n.data&.definition
|
|
94
|
+
|
|
95
|
+
defs = l10n.data.definition
|
|
96
|
+
if defs.empty?
|
|
97
|
+
@errors << "#{fname}/#{lang}: definition is empty"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def validate_entry_statuses(concept, fname)
|
|
103
|
+
(concept.localizations&.values || []).each do |l10n|
|
|
104
|
+
lang = l10n.language_code || "unknown"
|
|
105
|
+
status = l10n.data&.entry_status
|
|
106
|
+
next unless status
|
|
107
|
+
|
|
108
|
+
unless VALID_ENTRY_STATUSES.include?(status)
|
|
109
|
+
@errors << "#{fname}/#{lang}: invalid entry_status '#{status}' (expected one of: #{VALID_ENTRY_STATUSES.join(', ')})"
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
class DatasetValidator
|
|
5
|
+
def validate(path, strict: false, reference_path: nil)
|
|
6
|
+
result = validate_path(path)
|
|
7
|
+
|
|
8
|
+
if reference_path
|
|
9
|
+
ref_result = validate_cross_references(path, reference_path)
|
|
10
|
+
result.merge(ref_result)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
result
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def validate_path(path)
|
|
19
|
+
if File.extname(path).downcase == ".gcr"
|
|
20
|
+
validate_gcr(path)
|
|
21
|
+
else
|
|
22
|
+
validate_directory(path)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def validate_gcr(path)
|
|
27
|
+
GcrValidator.new.validate(path)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def validate_directory(path)
|
|
31
|
+
ConceptValidator.new(path).validate_all
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def validate_cross_references(path, reference_path)
|
|
35
|
+
extractor = ReferenceExtractor.new
|
|
36
|
+
resolver = build_resolver(reference_path)
|
|
37
|
+
|
|
38
|
+
if File.extname(path).downcase == ".gcr"
|
|
39
|
+
validate_gcr_refs(resolver, path, extractor)
|
|
40
|
+
else
|
|
41
|
+
validate_directory_refs(resolver, path, extractor)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def build_resolver(reference_path)
|
|
46
|
+
resolver = ReferenceResolver.new
|
|
47
|
+
Dir.glob(File.join(reference_path, "*.gcr")).each do |gcr_path|
|
|
48
|
+
pkg = GcrPackage.load(gcr_path)
|
|
49
|
+
uri_prefix = pkg.metadata&.dig("uri_prefix") || pkg.metadata&.dig("shortname")
|
|
50
|
+
resolver.register_package(pkg, uri_prefix: uri_prefix)
|
|
51
|
+
end
|
|
52
|
+
resolver
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def validate_gcr_refs(resolver, path, extractor)
|
|
56
|
+
pkg = GcrPackage.load(path)
|
|
57
|
+
uri_prefix = pkg.metadata&.dig("uri_prefix") || pkg.metadata&.dig("shortname")
|
|
58
|
+
resolver.register_self(pkg.concepts)
|
|
59
|
+
resolver.register_package(pkg, uri_prefix: uri_prefix)
|
|
60
|
+
resolver.validate_all(pkg, extractor: extractor)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def validate_directory_refs(resolver, path, extractor)
|
|
64
|
+
concepts = ConceptCollector.collect(path)
|
|
65
|
+
resolver.register_self(concepts)
|
|
66
|
+
resolver.validate_all(concepts, extractor: extractor)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -7,7 +7,7 @@ module Glossarist
|
|
|
7
7
|
values: Glossarist::GlossaryDefinition::DESIGNATION_BASE_NORMATIVE_STATUSES
|
|
8
8
|
attribute :type, :string
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
key_value do
|
|
11
11
|
map :type, to: :type
|
|
12
12
|
map %i[normative_status normativeStatus], to: :normative_status
|
|
13
13
|
map %i[geographical_area geographicalArea], to: :geographical_area
|
|
@@ -18,14 +18,13 @@ module Glossarist
|
|
|
18
18
|
type = hash["type"]
|
|
19
19
|
|
|
20
20
|
if type.nil? || /\w/ !~ type
|
|
21
|
-
|
|
21
|
+
type = infer_designation_type(hash)
|
|
22
|
+
hash["type"] = type
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
if self == Base
|
|
25
|
-
# called on Base class, delegate it to proper subclass
|
|
26
26
|
SERIALIZED_TYPES[type].of_yaml(hash)
|
|
27
27
|
else
|
|
28
|
-
# called on subclass, instantiate object
|
|
29
28
|
unless SERIALIZED_TYPES[self] == type
|
|
30
29
|
raise ArgumentError, "unexpected designation type: #{type}"
|
|
31
30
|
end
|
|
@@ -33,6 +32,14 @@ module Glossarist
|
|
|
33
32
|
super
|
|
34
33
|
end
|
|
35
34
|
end
|
|
35
|
+
|
|
36
|
+
def self.infer_designation_type(hash)
|
|
37
|
+
if hash["international"] || hash["abbreviation_type"]
|
|
38
|
+
"symbol"
|
|
39
|
+
else
|
|
40
|
+
"expression"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
36
43
|
end
|
|
37
44
|
end
|
|
38
45
|
end
|
|
@@ -13,7 +13,7 @@ module Glossarist
|
|
|
13
13
|
attribute :grammar_info, GrammarInfo, collection: true
|
|
14
14
|
attribute :type, :string, default: -> { "expression" }
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
key_value do
|
|
17
17
|
map :type, to: :type, render_default: true
|
|
18
18
|
map :prefix, to: :prefix
|
|
19
19
|
map %i[usage_info usageInfo], to: :usage_info
|
|
@@ -2,9 +2,9 @@ module Glossarist
|
|
|
2
2
|
module Designation
|
|
3
3
|
class Symbol < Base
|
|
4
4
|
attribute :international, :boolean
|
|
5
|
-
attribute :type, :string
|
|
5
|
+
attribute :type, :string, default: -> { "symbol" }
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
key_value do
|
|
8
8
|
map :international, to: :international
|
|
9
9
|
map :type, to: :type, render_default: true
|
|
10
10
|
end
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# (c) Copyright 2021 Ribose Inc.
|
|
4
|
-
#
|
|
5
|
-
|
|
6
|
-
require_relative "designation/base"
|
|
7
|
-
require_relative "designation/expression"
|
|
8
|
-
require_relative "designation/abbreviation"
|
|
9
|
-
require_relative "designation/grammar_info"
|
|
10
|
-
require_relative "designation/symbol"
|
|
11
|
-
require_relative "designation/graphical_symbol"
|
|
12
|
-
require_relative "designation/letter_symbol"
|
|
13
|
-
|
|
14
3
|
module Glossarist
|
|
15
4
|
module Designation
|
|
5
|
+
autoload :Base, "glossarist/designation/base"
|
|
6
|
+
autoload :Expression, "glossarist/designation/expression"
|
|
7
|
+
autoload :Abbreviation, "glossarist/designation/abbreviation"
|
|
8
|
+
autoload :GrammarInfo, "glossarist/designation/grammar_info"
|
|
9
|
+
autoload :Symbol, "glossarist/designation/symbol"
|
|
10
|
+
autoload :GraphicalSymbol, "glossarist/designation/graphical_symbol"
|
|
11
|
+
autoload :LetterSymbol, "glossarist/designation/letter_symbol"
|
|
12
|
+
|
|
16
13
|
# Bi-directional class-to-string mapping for STI-like serialization.
|
|
17
14
|
SERIALIZED_TYPES = {
|
|
18
15
|
Expression => "expression",
|
data/lib/glossarist/error.rb
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Glossarist
|
|
2
4
|
class Error < StandardError
|
|
3
5
|
end
|
|
4
6
|
end
|
|
5
|
-
|
|
6
|
-
require_relative "error/invalid_type_error"
|
|
7
|
-
require_relative "error/invalid_language_code_error"
|
|
8
|
-
require_relative "error/parse_error"
|
|
9
|
-
require_relative "error/cache_version_mismatch_error"
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
class GcrMetadata < Lutaml::Model::Serializable
|
|
5
|
+
attribute :shortname, :string
|
|
6
|
+
attribute :version, :string
|
|
7
|
+
attribute :title, :string
|
|
8
|
+
attribute :description, :string
|
|
9
|
+
attribute :owner, :string
|
|
10
|
+
attribute :tags, :string, collection: true
|
|
11
|
+
attribute :concept_count, :integer
|
|
12
|
+
attribute :languages, :string, collection: true
|
|
13
|
+
attribute :created_at, :string
|
|
14
|
+
attribute :glossarist_version, :string
|
|
15
|
+
attribute :schema_version, :string, default: -> { "1.0.0" }
|
|
16
|
+
attribute :statistics, GcrStatistics
|
|
17
|
+
attribute :homepage, :string
|
|
18
|
+
attribute :repository, :string
|
|
19
|
+
attribute :license, :string
|
|
20
|
+
attribute :uri_prefix, :string
|
|
21
|
+
attribute :concept_uri_template, :string
|
|
22
|
+
attribute :external_references, :hash, collection: true
|
|
23
|
+
|
|
24
|
+
key_value do
|
|
25
|
+
map :shortname, to: :shortname
|
|
26
|
+
map :version, to: :version
|
|
27
|
+
map :title, to: :title
|
|
28
|
+
map :description, to: :description
|
|
29
|
+
map :owner, to: :owner
|
|
30
|
+
map :tags, to: :tags
|
|
31
|
+
map :concept_count, to: :concept_count
|
|
32
|
+
map :languages, to: :languages
|
|
33
|
+
map :created_at, to: :created_at
|
|
34
|
+
map :glossarist_version, to: :glossarist_version
|
|
35
|
+
map :schema_version, to: :schema_version
|
|
36
|
+
map :statistics, to: :statistics
|
|
37
|
+
map :homepage, to: :homepage
|
|
38
|
+
map :repository, to: :repository
|
|
39
|
+
map :license, to: :license
|
|
40
|
+
map :uri_prefix, to: :uri_prefix
|
|
41
|
+
map :concept_uri_template, to: :concept_uri_template
|
|
42
|
+
map :external_references, to: :external_references
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.from_concepts(concepts, register_data: nil, options: {})
|
|
46
|
+
stats = GcrStatistics.from_concepts(concepts)
|
|
47
|
+
new(
|
|
48
|
+
shortname: options[:shortname] || register_data&.dig("shortname") || register_data&.dig("id"),
|
|
49
|
+
version: options[:version] || register_data&.dig("version"),
|
|
50
|
+
title: options[:title] || register_data&.dig("name"),
|
|
51
|
+
description: options[:description] || register_data&.dig("description"),
|
|
52
|
+
owner: options[:owner],
|
|
53
|
+
tags: options[:tags] || [],
|
|
54
|
+
concept_count: concepts.length,
|
|
55
|
+
languages: stats.languages,
|
|
56
|
+
created_at: Time.now.utc.iso8601,
|
|
57
|
+
glossarist_version: Glossarist::VERSION,
|
|
58
|
+
schema_version: register_data&.dig("schema_version") || SchemaMigration::CURRENT_SCHEMA_VERSION,
|
|
59
|
+
statistics: stats,
|
|
60
|
+
uri_prefix: options[:uri_prefix],
|
|
61
|
+
concept_uri_template: options[:concept_uri_template],
|
|
62
|
+
external_references: derive_external_references(concepts),
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.derive_external_references(concepts)
|
|
67
|
+
sources = Set.new
|
|
68
|
+
concepts.each do |concept|
|
|
69
|
+
concept.localizations.each do |l10n|
|
|
70
|
+
l10n.data.references&.each do |ref|
|
|
71
|
+
src = ref.source
|
|
72
|
+
sources.add(src) if src && !src.empty?
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
sources.map { |uri| { "uri" => uri } }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def [](key)
|
|
80
|
+
to_yaml_hash[key]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def dig(*keys)
|
|
84
|
+
to_yaml_hash.dig(*keys)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|