glossarist 1.0.9 → 2.0.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.
- checksums.yaml +4 -4
- data/config.yml +2 -0
- data/glossarist.gemspec +1 -1
- data/lib/glossarist/collections/collection.rb +34 -0
- data/lib/glossarist/collections/designation_collection.rb +15 -0
- data/lib/glossarist/collections.rb +2 -0
- data/lib/glossarist/concept.rb +87 -27
- data/lib/glossarist/concept_date.rb +1 -1
- data/lib/glossarist/concept_manager.rb +39 -4
- data/lib/glossarist/concept_source.rb +1 -1
- data/lib/glossarist/error/invalid_language_code_error.rb +15 -0
- data/lib/glossarist/error/invalid_type_error.rb +4 -0
- data/lib/glossarist/error/parse_error.rb +16 -0
- data/lib/glossarist/error.rb +8 -0
- data/lib/glossarist/localized_concept.rb +23 -12
- data/lib/glossarist/managed_concept.rb +86 -18
- data/lib/glossarist/managed_concept_collection.rb +6 -4
- data/lib/glossarist/model.rb +9 -1
- data/lib/glossarist/utilities/common_functions.rb +36 -1
- data/lib/glossarist/utilities/uuid.rb +75 -0
- data/lib/glossarist/utilities.rb +1 -0
- data/lib/glossarist/version.rb +1 -1
- data/lib/glossarist.rb +1 -20
- metadata +11 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2545436241f3fc01daf4879682439d3f4650a44a45af855cc5260ad445caca0b
|
|
4
|
+
data.tar.gz: c62fb0db9a7d1dc9ec4ad4ea9015a53a413bfbc922be77db276e4739926615a6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d611e1a09d99e64b7ad5e745d729fba2fe59d707f7f0469aa43fa3def04286c91128f5a65088f28943df550ef7eb6099d3354cadbafa7c68a93b08873512e2c1
|
|
7
|
+
data.tar.gz: c1e0ec90c6a41c9a4c26f8db587d0cfbb986fb3c2086494554fbde8dfc829abf0406d2bf4e123d85710c2e23c699ac82b0db9c5328f3498f3c378bd509617c81
|
data/config.yml
CHANGED
data/glossarist.gemspec
CHANGED
|
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
|
|
|
30
30
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
31
31
|
spec.require_paths = ["lib"]
|
|
32
32
|
|
|
33
|
-
spec.add_dependency "relaton", "~> 1.
|
|
33
|
+
spec.add_dependency "relaton", "~> 1.16.0"
|
|
34
34
|
spec.add_dependency "thor"
|
|
35
35
|
|
|
36
36
|
spec.add_development_dependency "pry", "~> 0.14.0"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
module Collections
|
|
5
|
+
class Collection
|
|
6
|
+
include Enumerable
|
|
7
|
+
|
|
8
|
+
attr_reader :collection
|
|
9
|
+
|
|
10
|
+
alias :size :count
|
|
11
|
+
|
|
12
|
+
def initialize(klass:)
|
|
13
|
+
@klass = klass
|
|
14
|
+
@collection = []
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def <<(object)
|
|
18
|
+
@collection << @klass.new(object)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def each(&block)
|
|
22
|
+
@collection.each(&block)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def empty?
|
|
26
|
+
@collection.empty?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def clear!
|
|
30
|
+
@collection = []
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Glossarist
|
|
4
|
+
module Collections
|
|
5
|
+
class DesignationCollection < Collection
|
|
6
|
+
def initialize
|
|
7
|
+
super(klass: Designation::Base)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def <<(object)
|
|
11
|
+
@collection << @klass.from_h(object)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/glossarist/concept.rb
CHANGED
|
@@ -7,8 +7,9 @@ module Glossarist
|
|
|
7
7
|
class Concept < Model
|
|
8
8
|
# Concept ID.
|
|
9
9
|
# @return [String]
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
attr_reader :id
|
|
11
|
+
|
|
12
|
+
attr_writer :uuid
|
|
12
13
|
|
|
13
14
|
# Concept designations.
|
|
14
15
|
# @todo Alias +terms+ exists only for legacy reasons and will be removed.
|
|
@@ -43,17 +44,43 @@ module Glossarist
|
|
|
43
44
|
# Contains list of extended attributes
|
|
44
45
|
attr_accessor :extension_attributes
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
attr_accessor :lineage_source
|
|
48
|
+
attr_accessor :lineage_source_similarity
|
|
49
|
+
|
|
50
|
+
attr_accessor :release
|
|
51
|
+
|
|
52
|
+
def initialize(*args)
|
|
47
53
|
@localizations = {}
|
|
48
|
-
@sources =
|
|
49
|
-
@related =
|
|
50
|
-
@
|
|
51
|
-
@
|
|
54
|
+
@sources = Glossarist::Collections::Collection.new(klass: ConceptSource)
|
|
55
|
+
@related = Glossarist::Collections::Collection.new(klass: RelatedConcept)
|
|
56
|
+
@definition = Glossarist::Collections::Collection.new(klass: DetailedDefinition)
|
|
57
|
+
@notes = Glossarist::Collections::Collection.new(klass: DetailedDefinition)
|
|
58
|
+
@examples = Glossarist::Collections::Collection.new(klass: DetailedDefinition)
|
|
59
|
+
@dates = Glossarist::Collections::Collection.new(klass: ConceptDate)
|
|
60
|
+
|
|
61
|
+
@designations = Glossarist::Collections::DesignationCollection.new
|
|
52
62
|
@extension_attributes = {}
|
|
53
63
|
|
|
64
|
+
normalize_args(args)
|
|
65
|
+
|
|
54
66
|
super
|
|
55
67
|
end
|
|
56
68
|
|
|
69
|
+
def uuid
|
|
70
|
+
@uuid ||= Glossarist::Utilities::UUID.uuid_v5(
|
|
71
|
+
Glossarist::Utilities::UUID::OID_NAMESPACE,
|
|
72
|
+
to_h.to_yaml,
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def id=(id)
|
|
77
|
+
raise(Glossarist::Error, "Expect id to be a string, Got #{id.class} (#{id})") unless id.is_a?(String) || id.nil?
|
|
78
|
+
|
|
79
|
+
@id = id
|
|
80
|
+
end
|
|
81
|
+
alias :termid= :id=
|
|
82
|
+
alias :identifier= :id=
|
|
83
|
+
|
|
57
84
|
# List of authorative sources.
|
|
58
85
|
# @todo Alias +authoritative_source+ exists for legacy reasons and may be
|
|
59
86
|
# removed.
|
|
@@ -65,33 +92,40 @@ module Glossarist
|
|
|
65
92
|
attr_reader :dates
|
|
66
93
|
|
|
67
94
|
def examples=(examples)
|
|
68
|
-
@examples
|
|
95
|
+
@examples.clear!
|
|
96
|
+
examples&.each { |example| @examples << example }
|
|
69
97
|
end
|
|
70
98
|
|
|
71
99
|
def notes=(notes)
|
|
72
|
-
@notes
|
|
100
|
+
@notes.clear!
|
|
101
|
+
notes&.each { |note| @notes << note }
|
|
73
102
|
end
|
|
74
103
|
|
|
75
104
|
def definition=(definition)
|
|
76
|
-
@definition
|
|
105
|
+
@definition.clear!
|
|
106
|
+
definition&.each { |definition| @definition << definition }
|
|
77
107
|
end
|
|
78
108
|
|
|
79
109
|
def designations=(designations)
|
|
80
|
-
@designations
|
|
81
|
-
|
|
82
|
-
end
|
|
110
|
+
@designations.clear!
|
|
111
|
+
designations&.each { |designation| @designations << designation }
|
|
83
112
|
end
|
|
84
113
|
|
|
85
114
|
alias :terms= :designations=
|
|
86
115
|
|
|
116
|
+
def preferred_designations
|
|
117
|
+
@designations.select(&:preferred?)
|
|
118
|
+
end
|
|
119
|
+
alias :preferred_terms :preferred_designations
|
|
120
|
+
|
|
87
121
|
def dates=(dates)
|
|
88
|
-
@dates
|
|
122
|
+
@dates.clear!
|
|
123
|
+
dates&.each { |date| @dates << date }
|
|
89
124
|
end
|
|
90
125
|
|
|
91
126
|
def sources=(sources)
|
|
92
|
-
@sources
|
|
93
|
-
|
|
94
|
-
end || []
|
|
127
|
+
@sources.clear!
|
|
128
|
+
sources&.each { |source| @sources << source }
|
|
95
129
|
end
|
|
96
130
|
|
|
97
131
|
def authoritative_source=(sources)
|
|
@@ -102,14 +136,19 @@ module Glossarist
|
|
|
102
136
|
|
|
103
137
|
def to_h
|
|
104
138
|
{
|
|
105
|
-
"
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
139
|
+
"data" => {
|
|
140
|
+
"dates" => dates&.map(&:to_h),
|
|
141
|
+
"definition" => definition&.map(&:to_h),
|
|
142
|
+
"examples" => examples&.map(&:to_h),
|
|
143
|
+
"id" => id,
|
|
144
|
+
"lineage_source_similarity" => lineage_source_similarity,
|
|
145
|
+
"notes" => notes&.map(&:to_h),
|
|
146
|
+
"release" => release,
|
|
147
|
+
"sources" => sources.empty? ? nil : sources&.map(&:to_h),
|
|
148
|
+
"terms" => (terms&.map(&:to_h) || []),
|
|
149
|
+
"related" => related&.map(&:to_h),
|
|
150
|
+
}.compact,
|
|
151
|
+
}.compact
|
|
113
152
|
end
|
|
114
153
|
|
|
115
154
|
# @deprecated For legacy reasons only.
|
|
@@ -119,7 +158,7 @@ module Glossarist
|
|
|
119
158
|
# rubocop:disable Metrics/AbcSize, Style/RescueModifier
|
|
120
159
|
def self.from_h(hash)
|
|
121
160
|
new.tap do |concept|
|
|
122
|
-
concept.id = hash.dig("termid")
|
|
161
|
+
concept.id = hash.dig("termid") || hash.dig("id")
|
|
123
162
|
concept.sources = hash.dig("sources")
|
|
124
163
|
concept.related = hash.dig("related")
|
|
125
164
|
concept.definition = hash.dig("definition")
|
|
@@ -141,7 +180,28 @@ module Glossarist
|
|
|
141
180
|
end
|
|
142
181
|
|
|
143
182
|
def related=(related)
|
|
144
|
-
@related
|
|
183
|
+
@related.clear!
|
|
184
|
+
related&.each { |r| @related << r }
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
Glossarist::GlossaryDefinition::CONCEPT_DATE_TYPES.each do |type|
|
|
188
|
+
# Sets the ConceptDate and add it to dates list of the specified type.
|
|
189
|
+
define_method("date_#{type}=") do |date|
|
|
190
|
+
date_hash = {
|
|
191
|
+
"type" => type,
|
|
192
|
+
"date" => date,
|
|
193
|
+
}
|
|
194
|
+
@dates ||= []
|
|
195
|
+
@dates << ConceptDate.new(date_hash)
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def normalize_args(args)
|
|
200
|
+
args.each do |arg|
|
|
201
|
+
data = arg.delete("data")
|
|
202
|
+
|
|
203
|
+
arg.merge!(data) if data
|
|
204
|
+
end
|
|
145
205
|
end
|
|
146
206
|
end
|
|
147
207
|
end
|
|
@@ -17,7 +17,13 @@ module Glossarist
|
|
|
17
17
|
collection ||= ManagedConceptCollection.new
|
|
18
18
|
|
|
19
19
|
Dir.glob(concepts_glob) do |filename|
|
|
20
|
-
|
|
20
|
+
concept = load_concept_from_file(filename)
|
|
21
|
+
concept.localized_concepts.each do |_lang, id|
|
|
22
|
+
localized_concept = load_localized_concept(id)
|
|
23
|
+
concept.add_l10n(localized_concept)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
collection.store(concept)
|
|
21
27
|
end
|
|
22
28
|
end
|
|
23
29
|
|
|
@@ -27,20 +33,49 @@ module Glossarist
|
|
|
27
33
|
end
|
|
28
34
|
|
|
29
35
|
def load_concept_from_file(filename)
|
|
30
|
-
|
|
36
|
+
concept_hash = Psych.safe_load(File.read(filename), permitted_classes: [Date])
|
|
37
|
+
concept_hash["uuid"] = concept_hash["id"] || File.basename(filename, ".*")
|
|
38
|
+
ManagedConcept.new(concept_hash)
|
|
39
|
+
rescue Psych::SyntaxError => e
|
|
40
|
+
raise Glossarist::ParseError.new(filename: filename, line: e.line)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def load_localized_concept(id)
|
|
44
|
+
concept_hash = Psych.safe_load(
|
|
45
|
+
File.read(localized_concept_path(id)),
|
|
46
|
+
permitted_classes: [Date],
|
|
47
|
+
)
|
|
48
|
+
concept_hash["uuid"] = id
|
|
49
|
+
|
|
50
|
+
Config.class_for(:localized_concept).new(concept_hash)
|
|
31
51
|
rescue Psych::SyntaxError => e
|
|
32
52
|
raise Glossarist::ParseError.new(filename: filename, line: e.line)
|
|
33
53
|
end
|
|
34
54
|
|
|
35
55
|
def save_concept_to_file(concept)
|
|
36
|
-
|
|
56
|
+
concept_dir = File.join(path, "concept")
|
|
57
|
+
localized_concept_dir = File.join(path, "localized_concept")
|
|
58
|
+
|
|
59
|
+
Dir.mkdir(concept_dir) unless Dir.exist?(concept_dir)
|
|
60
|
+
Dir.mkdir(localized_concept_dir) unless Dir.exist?(localized_concept_dir)
|
|
61
|
+
|
|
62
|
+
filename = File.join(concept_dir, "#{concept.uuid}.yaml")
|
|
37
63
|
File.write(filename, Psych.dump(concept.to_h))
|
|
64
|
+
|
|
65
|
+
concept.localized_concepts.each do |lang, uuid|
|
|
66
|
+
filename = File.join(localized_concept_dir, "#{uuid}.yaml")
|
|
67
|
+
File.write(filename, Psych.dump(concept.localization(lang).to_h))
|
|
68
|
+
end
|
|
38
69
|
end
|
|
39
70
|
|
|
40
71
|
private
|
|
41
72
|
|
|
42
73
|
def concepts_glob
|
|
43
|
-
File.join(path, "concept
|
|
74
|
+
File.join(path, "concept", "*.{yaml,yml}")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def localized_concept_path(id)
|
|
78
|
+
Dir.glob(File.join(path, "localized_concept", "#{id}.{yaml,yml}"))&.first
|
|
44
79
|
end
|
|
45
80
|
end
|
|
46
81
|
end
|
|
@@ -37,9 +37,9 @@ module Glossarist
|
|
|
37
37
|
origin_hash = self.origin.to_h.empty? ? nil : self.origin.to_h
|
|
38
38
|
|
|
39
39
|
{
|
|
40
|
+
"origin" => origin_hash,
|
|
40
41
|
"type" => type.to_s,
|
|
41
42
|
"status" => status&.to_s,
|
|
42
|
-
"origin" => origin_hash,
|
|
43
43
|
"modification" => modification,
|
|
44
44
|
}.compact
|
|
45
45
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Glossarist
|
|
2
|
+
class InvalidLanguageCodeError < Error
|
|
3
|
+
attr_reader :code
|
|
4
|
+
|
|
5
|
+
def initialize(code:)
|
|
6
|
+
@code = code
|
|
7
|
+
|
|
8
|
+
super()
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_s
|
|
12
|
+
"Invalid value for language_code: `#{code}`. It must be 3 characters long string."
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Glossarist
|
|
2
|
+
class ParseError < Error
|
|
3
|
+
attr_accessor :line, :filename
|
|
4
|
+
|
|
5
|
+
def initialize(filename:, line: nil)
|
|
6
|
+
@filename = filename
|
|
7
|
+
@line = line
|
|
8
|
+
|
|
9
|
+
super()
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def to_s
|
|
13
|
+
"Unable to parse file: #{filename}, error on line: #{line}"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -8,7 +8,7 @@ module Glossarist
|
|
|
8
8
|
# ISO 639-2 code for terminology.
|
|
9
9
|
# @see https://www.loc.gov/standards/iso639-2/php/code_list.php code list
|
|
10
10
|
# @return [String]
|
|
11
|
-
|
|
11
|
+
attr_reader :language_code
|
|
12
12
|
|
|
13
13
|
# Must be one of the following:
|
|
14
14
|
# +notValid+, +valid+, +superseded+, +retired+.
|
|
@@ -24,20 +24,29 @@ module Glossarist
|
|
|
24
24
|
# @return [String]
|
|
25
25
|
attr_accessor :classification
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
attr_accessor
|
|
27
|
+
# Temporary fields
|
|
28
|
+
# @todo Need to remove these once the isotc211-glossary is fixed
|
|
29
|
+
attr_accessor *%i[
|
|
30
|
+
review_date
|
|
31
|
+
review_decision_date
|
|
32
|
+
review_decision_event
|
|
33
|
+
review_type
|
|
34
|
+
]
|
|
30
35
|
|
|
31
|
-
def
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
def language_code=(language_code)
|
|
37
|
+
if language_code.is_a?(String) && language_code.length == 3
|
|
38
|
+
@language_code = language_code
|
|
39
|
+
else
|
|
40
|
+
raise Glossarist::InvalidLanguageCodeError.new(code: language_code)
|
|
41
|
+
end
|
|
35
42
|
end
|
|
36
43
|
|
|
37
|
-
def to_h # rubocop:disable Metrics/MethodLength
|
|
38
|
-
super
|
|
39
|
-
|
|
44
|
+
def to_h # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
|
45
|
+
hash = super
|
|
46
|
+
|
|
47
|
+
hash["data"].merge!({
|
|
40
48
|
"domain" => domain,
|
|
49
|
+
"language_code" => language_code,
|
|
41
50
|
"entry_status" => entry_status,
|
|
42
51
|
"sources" => sources.empty? ? nil : sources&.map(&:to_h),
|
|
43
52
|
"classification" => classification,
|
|
@@ -46,11 +55,13 @@ module Glossarist
|
|
|
46
55
|
"review_decision_date" => review_decision_date,
|
|
47
56
|
"review_decision_event" => review_decision_event,
|
|
48
57
|
}.compact).merge(@extension_attributes)
|
|
58
|
+
|
|
59
|
+
hash
|
|
49
60
|
end
|
|
50
61
|
|
|
51
62
|
def self.from_h(hash)
|
|
52
63
|
terms = hash["terms"]&.map { |h| Designation::Base.from_h(h) } || []
|
|
53
|
-
sources = hash["authoritative_source"]&.each { |source| source.merge({ "type" => "authoritative"}) }
|
|
64
|
+
sources = hash["authoritative_source"]&.each { |source| source.merge({ "type" => "authoritative" }) }
|
|
54
65
|
|
|
55
66
|
super(hash.merge({"terms" => terms, "sources" => sources}))
|
|
56
67
|
end
|
|
@@ -8,6 +8,9 @@ module Glossarist
|
|
|
8
8
|
# @return [String]
|
|
9
9
|
attr_accessor :id
|
|
10
10
|
alias :termid= :id=
|
|
11
|
+
alias :identifier= :id=
|
|
12
|
+
|
|
13
|
+
attr_accessor :uuid
|
|
11
14
|
|
|
12
15
|
# @return [Array<RelatedConcept>]
|
|
13
16
|
attr_reader :related
|
|
@@ -29,24 +32,27 @@ module Glossarist
|
|
|
29
32
|
#
|
|
30
33
|
# Keys are language codes and values are instances of {LocalizedConcept}.
|
|
31
34
|
# @return [Hash<String, LocalizedConcept>]
|
|
32
|
-
|
|
35
|
+
attr_reader :localizations
|
|
33
36
|
|
|
34
37
|
def initialize(attributes = {})
|
|
35
38
|
@localizations = {}
|
|
36
|
-
|
|
39
|
+
@localized_concepts = {}
|
|
40
|
+
@localized_concept_class = Config.class_for(:localized_concept)
|
|
41
|
+
@uuid_namespace = Glossarist::Utilities::UUID::OID_NAMESPACE
|
|
37
42
|
|
|
38
43
|
attributes = symbolize_keys(attributes)
|
|
39
|
-
|
|
40
|
-
end
|
|
44
|
+
@uuid = attributes[:uuid]
|
|
41
45
|
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
data = attributes.delete(:data) || {}
|
|
47
|
+
data["groups"] = attributes[:groups]
|
|
44
48
|
|
|
45
|
-
|
|
46
|
-
add_l10n(l)
|
|
47
|
-
end
|
|
49
|
+
data = symbolize_keys(data.compact)
|
|
48
50
|
|
|
49
|
-
|
|
51
|
+
super(slice_keys(data, managed_concept_attributes))
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def uuid
|
|
55
|
+
@uuid ||= Glossarist::Utilities::UUID.uuid_v5(@uuid_namespace, to_h.to_yaml)
|
|
50
56
|
end
|
|
51
57
|
|
|
52
58
|
def related=(related)
|
|
@@ -63,10 +69,51 @@ module Glossarist
|
|
|
63
69
|
@groups = groups.is_a?(Array) ? groups : [groups]
|
|
64
70
|
end
|
|
65
71
|
|
|
72
|
+
def localized_concepts=(localized_concepts)
|
|
73
|
+
return unless localized_concepts
|
|
74
|
+
|
|
75
|
+
if localized_concepts.is_a?(Hash)
|
|
76
|
+
@localized_concepts = stringify_keys(localized_concepts)
|
|
77
|
+
else
|
|
78
|
+
localized_concepts.each do |localized_concept|
|
|
79
|
+
lang = localized_concept["language_code"].to_s
|
|
80
|
+
|
|
81
|
+
@localized_concepts[lang] = Glossarist::Utilities::UUID.uuid_v5(@uuid_namespace, localized_concept.to_h.to_yaml)
|
|
82
|
+
|
|
83
|
+
add_localization(
|
|
84
|
+
@localized_concept_class.new(localized_concept["data"] || localized_concept),
|
|
85
|
+
)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def localizations=(localizations)
|
|
91
|
+
return unless localizations
|
|
92
|
+
|
|
93
|
+
@localizations = {}
|
|
94
|
+
|
|
95
|
+
localizations.each do |localized_concept|
|
|
96
|
+
unless localized_concept.is_a?(@localized_concept_class)
|
|
97
|
+
localized_concept = @localized_concept_class.new(
|
|
98
|
+
localized_concept["data"] || localized_concept,
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
add_l10n(localized_concept)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def localizations_hash
|
|
107
|
+
@localizations.map do |key, localized_concept|
|
|
108
|
+
[key, localized_concept.to_h]
|
|
109
|
+
end.to_h
|
|
110
|
+
end
|
|
111
|
+
|
|
66
112
|
# Adds concept localization.
|
|
67
113
|
# @param localized_concept [LocalizedConcept]
|
|
68
114
|
def add_localization(localized_concept)
|
|
69
115
|
lang = localized_concept.language_code
|
|
116
|
+
@localized_concepts[lang] = @localized_concepts[lang] || localized_concept.uuid
|
|
70
117
|
localizations.store(lang, localized_concept)
|
|
71
118
|
end
|
|
72
119
|
|
|
@@ -83,17 +130,18 @@ module Glossarist
|
|
|
83
130
|
|
|
84
131
|
def to_h
|
|
85
132
|
{
|
|
86
|
-
"
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}.
|
|
133
|
+
"data" => {
|
|
134
|
+
"identifier" => id,
|
|
135
|
+
"localized_concepts" => localized_concepts.empty? ? nil : localized_concepts,
|
|
136
|
+
"groups" => groups,
|
|
137
|
+
}.compact,
|
|
138
|
+
}.compact
|
|
92
139
|
end
|
|
93
140
|
|
|
94
141
|
def default_designation
|
|
95
142
|
localized = localization("eng") || localizations.values.first
|
|
96
|
-
localized&.terms&.first
|
|
143
|
+
terms = localized&.preferred_terms&.first || localized&.terms&.first
|
|
144
|
+
terms&.designation
|
|
97
145
|
end
|
|
98
146
|
|
|
99
147
|
def default_definition
|
|
@@ -105,14 +153,34 @@ module Glossarist
|
|
|
105
153
|
localization("eng") || localizations.values.first
|
|
106
154
|
end
|
|
107
155
|
|
|
156
|
+
def date_accepted=(date)
|
|
157
|
+
date_hash = {
|
|
158
|
+
"type" => "accepted",
|
|
159
|
+
"date" => date,
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
@dates ||= []
|
|
163
|
+
@dates << ConceptDate.new(date_hash)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def date_accepted
|
|
167
|
+
@dates.find { |date| date.accepted? }
|
|
168
|
+
end
|
|
169
|
+
|
|
108
170
|
def managed_concept_attributes
|
|
109
171
|
%i[
|
|
172
|
+
data
|
|
110
173
|
id
|
|
111
|
-
|
|
174
|
+
identifier
|
|
175
|
+
uuid
|
|
112
176
|
related
|
|
113
177
|
status
|
|
114
178
|
dates
|
|
179
|
+
date_accepted
|
|
180
|
+
dateAccepted
|
|
115
181
|
localized_concepts
|
|
182
|
+
localizedConcepts
|
|
183
|
+
localizations
|
|
116
184
|
groups
|
|
117
185
|
].compact
|
|
118
186
|
end
|
|
@@ -6,6 +6,7 @@ module Glossarist
|
|
|
6
6
|
|
|
7
7
|
def initialize
|
|
8
8
|
@managed_concepts = {}
|
|
9
|
+
@managed_concepts_ids = {}
|
|
9
10
|
@concept_manager = ConceptManager.new
|
|
10
11
|
end
|
|
11
12
|
|
|
@@ -39,7 +40,7 @@ module Glossarist
|
|
|
39
40
|
# ManagedConcept ID
|
|
40
41
|
# @return [ManagedConcept, nil]
|
|
41
42
|
def fetch(id)
|
|
42
|
-
@managed_concepts[id]
|
|
43
|
+
@managed_concepts[id] || @managed_concepts[@managed_concepts_ids[id]]
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
alias :[] :fetch
|
|
@@ -52,16 +53,17 @@ module Glossarist
|
|
|
52
53
|
# ManagedConcept ID
|
|
53
54
|
# @return [ManagedConcept]
|
|
54
55
|
def fetch_or_initialize(id)
|
|
55
|
-
fetch(id) or store(ManagedConcept.new(id: id))
|
|
56
|
+
fetch(id) or store(ManagedConcept.new(data: { id: id }))
|
|
56
57
|
end
|
|
57
58
|
|
|
58
|
-
# Adds concept to the collection.
|
|
59
|
+
# Adds concept to the collection. If collection contains a concept with
|
|
59
60
|
# the same ID already, that concept is replaced.
|
|
60
61
|
#
|
|
61
62
|
# @param managed_concept [ManagedConcept]
|
|
62
63
|
# ManagedConcept about to be added
|
|
63
64
|
def store(managed_concept)
|
|
64
|
-
@managed_concepts[managed_concept.
|
|
65
|
+
@managed_concepts[managed_concept.uuid] = managed_concept
|
|
66
|
+
@managed_concepts_ids[managed_concept.id] = managed_concept.uuid if managed_concept.id
|
|
65
67
|
end
|
|
66
68
|
|
|
67
69
|
alias :<< :store
|
data/lib/glossarist/model.rb
CHANGED
|
@@ -18,7 +18,11 @@ module Glossarist
|
|
|
18
18
|
def set_attribute(name, value)
|
|
19
19
|
public_send("#{name}=", value)
|
|
20
20
|
rescue NoMethodError
|
|
21
|
-
|
|
21
|
+
# adding support for camel case
|
|
22
|
+
if name.match?(/[A-Z]/)
|
|
23
|
+
name = snake_case(name.to_s).to_sym
|
|
24
|
+
retry
|
|
25
|
+
elsif Config.extension_attributes.include?(name)
|
|
22
26
|
extension_attributes[name] = value
|
|
23
27
|
else
|
|
24
28
|
raise ArgumentError, "#{self.class.name} does not have " +
|
|
@@ -29,5 +33,9 @@ module Glossarist
|
|
|
29
33
|
def self.from_h(hash)
|
|
30
34
|
new(hash)
|
|
31
35
|
end
|
|
36
|
+
|
|
37
|
+
def snake_case(str)
|
|
38
|
+
str.gsub(/([A-Z])/) { "_#{$1.downcase}" }
|
|
39
|
+
end
|
|
32
40
|
end
|
|
33
41
|
end
|
|
@@ -9,7 +9,26 @@ module Glossarist
|
|
|
9
9
|
def symbolize_keys(hash)
|
|
10
10
|
result = {}
|
|
11
11
|
hash.each_pair do |key, value|
|
|
12
|
-
result[key.to_sym] = value
|
|
12
|
+
result[key.to_sym] = if value.is_a?(Hash)
|
|
13
|
+
symbolize_keys(value)
|
|
14
|
+
else
|
|
15
|
+
value
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
result
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Hash#transform_keys is not available in Ruby 2.4
|
|
22
|
+
# so we have to do this ourselves :(
|
|
23
|
+
# symbolize hash keys
|
|
24
|
+
def stringify_keys(hash)
|
|
25
|
+
result = {}
|
|
26
|
+
hash.each_pair do |key, value|
|
|
27
|
+
result[key.to_s] = if value.is_a?(Hash)
|
|
28
|
+
stringify_keys(value)
|
|
29
|
+
else
|
|
30
|
+
value
|
|
31
|
+
end
|
|
13
32
|
end
|
|
14
33
|
result
|
|
15
34
|
end
|
|
@@ -24,6 +43,22 @@ module Glossarist
|
|
|
24
43
|
end
|
|
25
44
|
result
|
|
26
45
|
end
|
|
46
|
+
|
|
47
|
+
def convert_keys_to_snake_case(hash)
|
|
48
|
+
result = {}
|
|
49
|
+
hash.each_pair do |key, value|
|
|
50
|
+
result[snake_case(key)] = if value.is_a?(Hash)
|
|
51
|
+
convert_keys_to_snake_case(value)
|
|
52
|
+
else
|
|
53
|
+
value
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
result
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def snake_case(str)
|
|
60
|
+
str.gsub(/([A-Z])/) { "_#{$1.downcase}" }
|
|
61
|
+
end
|
|
27
62
|
end
|
|
28
63
|
end
|
|
29
64
|
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# extracted from https://github.com/rails/rails/blob/main/activesupport/lib/active_support/core_ext/digest/uuid.rb
|
|
4
|
+
# to generate uuid_v5 for concept files
|
|
5
|
+
|
|
6
|
+
require "securerandom"
|
|
7
|
+
require "openssl"
|
|
8
|
+
|
|
9
|
+
module Glossarist
|
|
10
|
+
module Utilities
|
|
11
|
+
module UUID
|
|
12
|
+
DNS_NAMESPACE = "k\xA7\xB8\x10\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
|
|
13
|
+
URL_NAMESPACE = "k\xA7\xB8\x11\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
|
|
14
|
+
OID_NAMESPACE = "k\xA7\xB8\x12\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
|
|
15
|
+
X500_NAMESPACE = "k\xA7\xB8\x14\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
|
|
16
|
+
|
|
17
|
+
# Generates a v5 non-random UUID (Universally Unique IDentifier).
|
|
18
|
+
#
|
|
19
|
+
# Using OpenSSL::Digest::MD5 generates version 3 UUIDs; OpenSSL::Digest::SHA1 generates version 5 UUIDs.
|
|
20
|
+
# uuid_from_hash always generates the same UUID for a given name and namespace combination.
|
|
21
|
+
#
|
|
22
|
+
# See RFC 4122 for details of UUID at: https://www.ietf.org/rfc/rfc4122.txt
|
|
23
|
+
def self.uuid_from_hash(hash_class, namespace, name)
|
|
24
|
+
if hash_class == Digest::MD5 || hash_class == OpenSSL::Digest::MD5
|
|
25
|
+
version = 3
|
|
26
|
+
elsif hash_class == Digest::SHA1 || hash_class == OpenSSL::Digest::SHA1
|
|
27
|
+
version = 5
|
|
28
|
+
else
|
|
29
|
+
raise ArgumentError, "Expected OpenSSL::Digest::SHA1 or OpenSSL::Digest::MD5, got #{hash_class.name}."
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
uuid_namespace = pack_uuid_namespace(namespace)
|
|
33
|
+
|
|
34
|
+
hash = hash_class.new
|
|
35
|
+
hash.update(uuid_namespace)
|
|
36
|
+
hash.update(name)
|
|
37
|
+
|
|
38
|
+
ary = hash.digest.unpack("NnnnnN")
|
|
39
|
+
ary[2] = (ary[2] & 0x0FFF) | (version << 12)
|
|
40
|
+
ary[3] = (ary[3] & 0x3FFF) | 0x8000
|
|
41
|
+
|
|
42
|
+
"%08x-%04x-%04x-%04x-%04x%08x" % ary
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Convenience method for uuid_from_hash using OpenSSL::Digest::MD5.
|
|
46
|
+
def self.uuid_v3(uuid_namespace, name)
|
|
47
|
+
uuid_from_hash(OpenSSL::Digest::MD5, uuid_namespace, name)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Convenience method for uuid_from_hash using OpenSSL::Digest::SHA1.
|
|
51
|
+
def self.uuid_v5(uuid_namespace, name)
|
|
52
|
+
uuid_from_hash(OpenSSL::Digest::SHA1, uuid_namespace, name)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Convenience method for SecureRandom.uuid.
|
|
56
|
+
def self.uuid_v4
|
|
57
|
+
SecureRandom.uuid
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.pack_uuid_namespace(namespace)
|
|
61
|
+
if [DNS_NAMESPACE, OID_NAMESPACE, URL_NAMESPACE, X500_NAMESPACE].include?(namespace)
|
|
62
|
+
namespace
|
|
63
|
+
else
|
|
64
|
+
match_data = namespace.match(/\A(\h{8})-(\h{4})-(\h{4})-(\h{4})-(\h{4})(\h{8})\z/)
|
|
65
|
+
|
|
66
|
+
raise ArgumentError, "Only UUIDs are valid namespace identifiers" unless match_data.present?
|
|
67
|
+
|
|
68
|
+
match_data.captures.map { |s| s.to_i(16) }.pack("NnnnnN")
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private_class_method :pack_uuid_namespace
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
data/lib/glossarist/utilities.rb
CHANGED
data/lib/glossarist/version.rb
CHANGED
data/lib/glossarist.rb
CHANGED
|
@@ -30,28 +30,9 @@ require_relative "glossarist/non_verb_rep"
|
|
|
30
30
|
require_relative "glossarist/collections"
|
|
31
31
|
|
|
32
32
|
require_relative "glossarist/config"
|
|
33
|
+
require_relative "glossarist/error"
|
|
33
34
|
|
|
34
35
|
module Glossarist
|
|
35
|
-
class Error < StandardError; end
|
|
36
|
-
|
|
37
|
-
class InvalidTypeError < StandardError; end
|
|
38
|
-
|
|
39
|
-
class ParseError < StandardError
|
|
40
|
-
attr_accessor :line, :filename
|
|
41
|
-
|
|
42
|
-
def initialize(filename:, line: nil)
|
|
43
|
-
@filename = filename
|
|
44
|
-
@line = line
|
|
45
|
-
|
|
46
|
-
super()
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def to_s
|
|
50
|
-
"Unable to parse file: #{filename}, error on line: #{line}"
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
# Your code goes here...
|
|
54
|
-
|
|
55
36
|
def self.configure
|
|
56
37
|
config = Glossarist::Config.instance
|
|
57
38
|
|
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:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-
|
|
11
|
+
date: 2023-11-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: relaton
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 1.
|
|
19
|
+
version: 1.16.0
|
|
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: 1.
|
|
26
|
+
version: 1.16.0
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: thor
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -109,6 +109,8 @@ files:
|
|
|
109
109
|
- lib/glossarist/collections.rb
|
|
110
110
|
- lib/glossarist/collections/asset_collection.rb
|
|
111
111
|
- lib/glossarist/collections/bibliography_collection.rb
|
|
112
|
+
- lib/glossarist/collections/collection.rb
|
|
113
|
+
- lib/glossarist/collections/designation_collection.rb
|
|
112
114
|
- lib/glossarist/concept.rb
|
|
113
115
|
- lib/glossarist/concept_date.rb
|
|
114
116
|
- lib/glossarist/concept_manager.rb
|
|
@@ -124,6 +126,10 @@ files:
|
|
|
124
126
|
- lib/glossarist/designation/letter_symbol.rb
|
|
125
127
|
- lib/glossarist/designation/symbol.rb
|
|
126
128
|
- lib/glossarist/detailed_definition.rb
|
|
129
|
+
- lib/glossarist/error.rb
|
|
130
|
+
- lib/glossarist/error/invalid_language_code_error.rb
|
|
131
|
+
- lib/glossarist/error/invalid_type_error.rb
|
|
132
|
+
- lib/glossarist/error/parse_error.rb
|
|
127
133
|
- lib/glossarist/glossary_definition.rb
|
|
128
134
|
- lib/glossarist/localized_concept.rb
|
|
129
135
|
- lib/glossarist/managed_concept.rb
|
|
@@ -138,6 +144,7 @@ files:
|
|
|
138
144
|
- lib/glossarist/utilities/enum/class_methods.rb
|
|
139
145
|
- lib/glossarist/utilities/enum/enum_collection.rb
|
|
140
146
|
- lib/glossarist/utilities/enum/instance_methods.rb
|
|
147
|
+
- lib/glossarist/utilities/uuid.rb
|
|
141
148
|
- lib/glossarist/version.rb
|
|
142
149
|
homepage: https://github.com/glossarist/glossarist-ruby
|
|
143
150
|
licenses:
|